450 lines
9.1 KiB
C
450 lines
9.1 KiB
C
/*++
|
|
|
|
Copyright (c) 1991 Microsoft Corporation
|
|
|
|
Module Name:
|
|
|
|
bufpool.c
|
|
|
|
Abstract:
|
|
|
|
Generic Buffer Pool Manager.
|
|
|
|
Author:
|
|
|
|
Mike Massa (mikemas) April 5, 1996
|
|
|
|
Revision History:
|
|
|
|
Who When What
|
|
-------- -------- ----------------------------------------------
|
|
mikemas 04-05-96 created
|
|
|
|
Notes:
|
|
|
|
Buffer Pools provide a mechanism for managing caches of fixed size
|
|
structures which are frequently allocated/deallocated.
|
|
|
|
--*/
|
|
|
|
#include "clusrtlp.h"
|
|
|
|
|
|
//
|
|
// Pool of generic buffers
|
|
//
|
|
typedef struct _CLRTL_BUFFER_POOL {
|
|
DWORD PoolSignature;
|
|
DWORD BufferSize;
|
|
SINGLE_LIST_ENTRY FreeList;
|
|
DWORD MaximumCached;
|
|
DWORD CurrentCached;
|
|
DWORD MaximumAllocated;
|
|
DWORD ReferenceCount;
|
|
CLRTL_BUFFER_CONSTRUCTOR Constructor;
|
|
CLRTL_BUFFER_DESTRUCTOR Destructor;
|
|
CRITICAL_SECTION Lock;
|
|
} CLRTL_BUFFER_POOL;
|
|
|
|
|
|
//
|
|
// Header for each allocated buffer
|
|
//
|
|
typedef struct {
|
|
SINGLE_LIST_ENTRY Linkage;
|
|
PCLRTL_BUFFER_POOL Pool;
|
|
} BUFFER_HEADER, *PBUFFER_HEADER;
|
|
|
|
|
|
#define BP_SIG 'loop'
|
|
|
|
#define ASSERT_BP_SIG(pool) CL_ASSERT((pool)->PoolSignature == BP_SIG)
|
|
|
|
|
|
//
|
|
// Macros
|
|
//
|
|
//
|
|
#define BpAllocateMemory(size) LocalAlloc(LMEM_FIXED, (size))
|
|
#define BpFreeMemory(buf) LocalFree(buf)
|
|
#define BpAcquirePoolLock(Pool) EnterCriticalSection(&((Pool)->Lock))
|
|
#define BpReleasePoolLock(Pool) LeaveCriticalSection(&((Pool)->Lock))
|
|
|
|
|
|
//
|
|
// Public Functions
|
|
//
|
|
PCLRTL_BUFFER_POOL
|
|
ClRtlCreateBufferPool(
|
|
IN DWORD BufferSize,
|
|
IN DWORD MaximumCached,
|
|
IN DWORD MaximumAllocated,
|
|
IN CLRTL_BUFFER_CONSTRUCTOR Constructor, OPTIONAL
|
|
IN CLRTL_BUFFER_DESTRUCTOR Destructor OPTIONAL
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Creates a pool from which fixed-size buffers may be allocated.
|
|
|
|
Arguments:
|
|
|
|
BufferSize - Size of the buffers managed by the pool.
|
|
|
|
MaximumCached - The maximum number of buffers to cache in the pool.
|
|
Must be less than or equal to MaximumAllocated.
|
|
|
|
MaximumAllocated - The maximum number of buffers to allocate from
|
|
system memory. Must be less than or equal to
|
|
CLRTL_MAX_POOL_BUFFERS.
|
|
|
|
Constructor - An optional routine to be called when a new buffer
|
|
is allocated from system memory. May be NULL
|
|
|
|
Destructor - An optional routine to be called when a buffer
|
|
is returned to system memory. May be NULL.
|
|
|
|
Return Value:
|
|
|
|
A pointer to the created buffer pool or NULL on error.
|
|
Extended error information is available from GetLastError().
|
|
|
|
--*/
|
|
|
|
{
|
|
PCLRTL_BUFFER_POOL pool;
|
|
|
|
|
|
if ( (MaximumAllocated > CLRTL_MAX_POOL_BUFFERS) ||
|
|
(MaximumCached > MaximumAllocated)
|
|
)
|
|
{
|
|
SetLastError(ERROR_INVALID_PARAMETER);
|
|
return(NULL);
|
|
}
|
|
|
|
pool = BpAllocateMemory(sizeof(CLRTL_BUFFER_POOL));
|
|
|
|
if (pool != NULL) {
|
|
|
|
InitializeCriticalSection(&(pool->Lock));
|
|
|
|
pool->PoolSignature = BP_SIG;
|
|
pool->BufferSize = sizeof(BUFFER_HEADER) + BufferSize;
|
|
pool->FreeList.Next = NULL;
|
|
pool->MaximumCached = MaximumCached;
|
|
pool->CurrentCached = 0;
|
|
pool->MaximumAllocated = MaximumAllocated + 1;
|
|
pool->ReferenceCount = 1;
|
|
pool->Constructor = Constructor;
|
|
pool->Destructor = Destructor;
|
|
}
|
|
else {
|
|
SetLastError(ERROR_NOT_ENOUGH_MEMORY);
|
|
}
|
|
|
|
return(pool);
|
|
}
|
|
|
|
|
|
VOID
|
|
ClRtlDestroyBufferPool(
|
|
IN PCLRTL_BUFFER_POOL Pool
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Destroys a previously created buffer pool.
|
|
|
|
Arguments:
|
|
|
|
Pool - A pointer to the pool to destroy.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
Notes:
|
|
|
|
The pool will not actually be destroyed until all outstanding
|
|
buffers have been returned. Each outstanding buffer is effectively
|
|
a reference on the pool.
|
|
|
|
--*/
|
|
|
|
{
|
|
SINGLE_LIST_ENTRY deleteList;
|
|
CLRTL_BUFFER_DESTRUCTOR destructor;
|
|
PSINGLE_LIST_ENTRY item;
|
|
PBUFFER_HEADER header;
|
|
BOOLEAN freePool;
|
|
|
|
|
|
deleteList.Next = NULL;
|
|
|
|
ASSERT_BP_SIG(Pool);
|
|
|
|
BpAcquirePoolLock(Pool);
|
|
|
|
CL_ASSERT(Pool->ReferenceCount != 0);
|
|
Pool->ReferenceCount--; // Remove initial reference.
|
|
destructor = Pool->Destructor;
|
|
|
|
//
|
|
// Free all cached buffers
|
|
//
|
|
item = PopEntryList(&(Pool->FreeList));
|
|
|
|
while (item != NULL) {
|
|
CL_ASSERT(Pool->ReferenceCount != 0);
|
|
PushEntryList(&deleteList, item);
|
|
Pool->ReferenceCount--;
|
|
|
|
item = PopEntryList(&(Pool->FreeList));
|
|
}
|
|
|
|
if (Pool->ReferenceCount == 0) {
|
|
BpReleasePoolLock(Pool);
|
|
|
|
DeleteCriticalSection(&(Pool->Lock));
|
|
BpFreeMemory(Pool);
|
|
}
|
|
else {
|
|
//
|
|
// Pool destruction is deferred until all buffers have been freed.
|
|
//
|
|
Pool->CurrentCached = 0;
|
|
Pool->MaximumCached = 0;
|
|
|
|
BpReleasePoolLock(Pool);
|
|
}
|
|
|
|
item = PopEntryList(&deleteList);
|
|
|
|
while (item != NULL) {
|
|
header = CONTAINING_RECORD(
|
|
item,
|
|
BUFFER_HEADER,
|
|
Linkage
|
|
);
|
|
|
|
if (destructor != NULL) {
|
|
(*destructor)(header+1);
|
|
}
|
|
|
|
BpFreeMemory(header);
|
|
|
|
item = PopEntryList(&deleteList);
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
|
|
PVOID
|
|
ClRtlAllocateBuffer(
|
|
IN PCLRTL_BUFFER_POOL Pool
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Allocates a buffer from a previously created buffer pool.
|
|
|
|
Arguments:
|
|
|
|
Pool - A pointer to the pool from which to allocate the buffer.
|
|
|
|
Return Value:
|
|
|
|
A pointer to the allocated buffer if the routine was successfull.
|
|
NULL if the routine failed. Extended error information is available
|
|
by calling GetLastError().
|
|
|
|
--*/
|
|
|
|
{
|
|
|
|
//
|
|
// turn this fancy stuff off until it works.
|
|
//
|
|
#if 0
|
|
PSINGLE_LIST_ENTRY item;
|
|
PBUFFER_HEADER header;
|
|
PVOID buffer;
|
|
DWORD status;
|
|
|
|
|
|
ASSERT_BP_SIG(Pool);
|
|
|
|
BpAcquirePoolLock(Pool);
|
|
|
|
//
|
|
// First, check the cache.
|
|
//
|
|
item = PopEntryList(&(Pool->FreeList));
|
|
|
|
if (item != NULL) {
|
|
CL_ASSERT(Pool->CurrentCached != 0);
|
|
Pool->CurrentCached--;
|
|
|
|
BpReleasePoolLock(Pool);
|
|
|
|
header = CONTAINING_RECORD(
|
|
item,
|
|
BUFFER_HEADER,
|
|
Linkage
|
|
);
|
|
|
|
return(header+1);
|
|
}
|
|
|
|
//
|
|
// Need to allocate a fresh buffer from system memory.
|
|
//
|
|
if (Pool->ReferenceCount < Pool->MaximumAllocated) {
|
|
//
|
|
// This is equivalent to a reference on the Pool.
|
|
//
|
|
Pool->ReferenceCount++;
|
|
|
|
BpReleasePoolLock(Pool);
|
|
|
|
header = BpAllocateMemory(Pool->BufferSize);
|
|
|
|
if (header != NULL) {
|
|
header->Pool = Pool;
|
|
buffer = header+1;
|
|
|
|
if (Pool->Constructor == NULL) {
|
|
return(buffer);
|
|
}
|
|
|
|
status = (*(Pool->Constructor))(buffer);
|
|
|
|
if (status == ERROR_SUCCESS) {
|
|
return(buffer);
|
|
}
|
|
|
|
SetLastError(status);
|
|
|
|
//
|
|
// The constructor failed.
|
|
//
|
|
BpFreeMemory(header);
|
|
}
|
|
else {
|
|
SetLastError(ERROR_NOT_ENOUGH_MEMORY);
|
|
}
|
|
|
|
//
|
|
// Failed - undo the reference.
|
|
//
|
|
BpAcquirePoolLock(Pool);
|
|
|
|
Pool->ReferenceCount--;
|
|
}
|
|
else {
|
|
SetLastError(ERROR_NOT_ENOUGH_MEMORY);
|
|
}
|
|
|
|
BpReleasePoolLock(Pool);
|
|
|
|
return(NULL);
|
|
#else
|
|
return(LocalAlloc(LMEM_FIXED, Pool->BufferSize));
|
|
#endif
|
|
}
|
|
|
|
|
|
VOID
|
|
ClRtlFreeBuffer(
|
|
PVOID Buffer
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
|
|
Frees a buffer back to its owning pool.
|
|
|
|
Arguments:
|
|
|
|
Buffer - The buffer to free.
|
|
|
|
Return Value:
|
|
|
|
None.
|
|
|
|
--*/
|
|
{
|
|
//
|
|
// turn this fancy stuff off until it works.
|
|
//
|
|
#if 0
|
|
|
|
PBUFFER_HEADER header;
|
|
PCLRTL_BUFFER_POOL pool;
|
|
CLRTL_BUFFER_DESTRUCTOR destructor;
|
|
|
|
|
|
header = ((PBUFFER_HEADER) Buffer) - 1;
|
|
|
|
pool = header->Pool;
|
|
|
|
ASSERT_BP_SIG(pool);
|
|
|
|
BpAcquirePoolLock(pool);
|
|
|
|
if (pool->CurrentCached < pool->MaximumCached) {
|
|
//
|
|
// Return to free list
|
|
//
|
|
PushEntryList(
|
|
&(pool->FreeList),
|
|
(PSINGLE_LIST_ENTRY) &(header->Linkage)
|
|
);
|
|
|
|
pool->CurrentCached++;
|
|
|
|
BpReleasePoolLock(pool);
|
|
|
|
return;
|
|
}
|
|
|
|
destructor = pool->Destructor;
|
|
|
|
CL_ASSERT(pool->ReferenceCount != 0);
|
|
|
|
if (--(pool->ReferenceCount) != 0) {
|
|
BpReleasePoolLock(pool);
|
|
|
|
if (destructor) {
|
|
(*destructor)(Buffer);
|
|
}
|
|
|
|
BpFreeMemory(header);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
CL_ASSERT(pool->CurrentCached == 0);
|
|
BpReleasePoolLock(pool);
|
|
DeleteCriticalSection(&(pool->Lock));
|
|
BpFreeMemory(pool);
|
|
|
|
if (destructor) {
|
|
(*destructor)(Buffer);
|
|
}
|
|
|
|
BpFreeMemory(header);
|
|
|
|
return;
|
|
#else
|
|
LocalFree(Buffer);
|
|
#endif
|
|
}
|
|
|