1239 lines
35 KiB
C++
1239 lines
35 KiB
C++
#include "precomp.h"
|
||
/*
|
||
* memmgr.cpp
|
||
*
|
||
* Copyright (c) 1993 - 1995 by DataBeam Corporation, Lexington, KY
|
||
*
|
||
* Abstract:
|
||
* This is the implementation file for the MemoryManager class. This
|
||
* file contains the code necessary to allocate and distribute memory
|
||
* in the form of Memory objects.
|
||
*
|
||
* Protected Instance Variables:
|
||
* Memory_Buffer
|
||
* This is the base address for the large memory buffer that the
|
||
* Memory Manager object allocates during instantiation. This is
|
||
* remembered so that the buffer can be freed when the Memory Manager
|
||
* object is destroyed.
|
||
* Memory_Information
|
||
* This is a pointer to the structure in memory that contains general
|
||
* information about the memory being managed by this object.
|
||
*
|
||
* Protected Member Functions:
|
||
* ReleaseMemory
|
||
* This is a private function releases memory used by a Memory object
|
||
* by putting it back into the proper free stack list.
|
||
* CalculateMemoryBufferSize
|
||
* AllocateMemoryBuffer
|
||
* InitializeMemoryBuffer
|
||
*
|
||
* Caveats:
|
||
* None.
|
||
*
|
||
* Author:
|
||
* James P. Galvin, Jr.
|
||
*/
|
||
|
||
DWORD MemoryManager::dwSystemPageSize = 0;
|
||
|
||
/*
|
||
* MemoryManager ()
|
||
*
|
||
* Public
|
||
*
|
||
* Functional Description:
|
||
* This is the default constructor for this class. It does nothing and
|
||
* only exists to allow classes to derive from this one without having to
|
||
* invoke the defined constructor.
|
||
*/
|
||
MemoryManager::MemoryManager () :
|
||
pExternal_Block_Information (NULL), fIsSharedMemory (TRUE),
|
||
bAllocs_Restricted (TRUE), Max_External_Blocks (0)
|
||
{
|
||
}
|
||
|
||
/*
|
||
* MemoryManager ()
|
||
*
|
||
* Public
|
||
*
|
||
* Functional Description:
|
||
* This is the constructor for the MemoryManager class. It calculates
|
||
* how much total memory will be required to hold all the blocks
|
||
* asked for in the memory template array that is passed in. It then
|
||
* allocates all of that memory in one operating system call. It then
|
||
* builds a set of free stacks, each of which contains all the blocks
|
||
* of a particular size.
|
||
*/
|
||
MemoryManager::MemoryManager (
|
||
PMemoryTemplate memory_template,
|
||
ULong memory_count,
|
||
PMemoryManagerError memory_manager_error,
|
||
ULong ulMaxExternalBlocks,
|
||
BOOL bAllocsRestricted) :
|
||
bAllocs_Restricted (bAllocsRestricted),
|
||
fIsSharedMemory (FALSE), Max_External_Blocks (0)
|
||
{
|
||
ULong memory_buffer_size;
|
||
|
||
*memory_manager_error = MEMORY_MANAGER_NO_ERROR;
|
||
|
||
/*
|
||
* Calculate the amount of memory required for this memory manager
|
||
* (including all management structures).
|
||
*/
|
||
memory_buffer_size = CalculateMemoryBufferSize (memory_template,
|
||
memory_count, NULL);
|
||
|
||
/*
|
||
* Allocate the memory buffer.
|
||
*/
|
||
AllocateMemoryBuffer (memory_buffer_size);
|
||
|
||
/*
|
||
* If the allocation succeeded, then initialize the memory buffer so that
|
||
* it can be used.
|
||
*/
|
||
if (Memory_Buffer != NULL)
|
||
{
|
||
/*
|
||
* Initialize the External block information dictionary.
|
||
* This is only for allocations that do not come from preallocated
|
||
* buffers.
|
||
*/
|
||
if (ulMaxExternalBlocks > 0) {
|
||
pExternal_Block_Information = new BlockInformationList (ulMaxExternalBlocks / 3);
|
||
|
||
if (NULL != pExternal_Block_Information) {
|
||
Max_External_Blocks = ulMaxExternalBlocks;
|
||
}
|
||
else
|
||
{
|
||
/*
|
||
* We were unable to allocate memory for the pre-allocated
|
||
* memory pool.
|
||
*/
|
||
ERROR_OUT(("MemoryManager::MemoryManager: "
|
||
"failed to allocate the external block information dictionary"));
|
||
*memory_manager_error = MEMORY_MANAGER_ALLOCATION_FAILURE;
|
||
}
|
||
}
|
||
|
||
if (*memory_manager_error != MEMORY_MANAGER_ALLOCATION_FAILURE) {
|
||
/*
|
||
* Initialize the memory buffer. Note that no error can occur doing
|
||
* this, since the allocation has already succeeded.
|
||
*/
|
||
InitializeMemoryBuffer (memory_template, memory_count);
|
||
|
||
/*
|
||
* Indicate that no error occured.
|
||
*/
|
||
TRACE_OUT(("MemoryManager::MemoryManager: allocation successful"));
|
||
TRACE_OUT(("MemoryManager::MemoryManager: Allocated %d memory blocks", GetBufferCount()));
|
||
*memory_manager_error = MEMORY_MANAGER_NO_ERROR;
|
||
}
|
||
}
|
||
else
|
||
{
|
||
/*
|
||
* We were unable to allocate memory for the pre-allocated
|
||
* memory pool.
|
||
*/
|
||
ERROR_OUT(("MemoryManager::MemoryManager: allocation failed"));
|
||
*memory_manager_error = MEMORY_MANAGER_ALLOCATION_FAILURE;
|
||
}
|
||
}
|
||
|
||
/*
|
||
* ~MemoryManager ()
|
||
*
|
||
* Public
|
||
*
|
||
* Functional Description:
|
||
* This is the destructor for the Memory Manager class. It frees up the
|
||
* memory allocated for the memory pool (if any).
|
||
*/
|
||
MemoryManager::~MemoryManager ()
|
||
{
|
||
PBlockInformation lpBlockInfo;
|
||
/*
|
||
* Iterate through the external block information list, deleting all
|
||
* block information structures contained therein.
|
||
*/
|
||
if (NULL != pExternal_Block_Information)
|
||
{
|
||
pExternal_Block_Information->reset();
|
||
while (pExternal_Block_Information->iterate ((PDWORD_PTR) &lpBlockInfo))
|
||
{
|
||
delete lpBlockInfo;
|
||
}
|
||
|
||
delete pExternal_Block_Information;
|
||
}
|
||
|
||
/*
|
||
* Free up the memory buffer (if there is one).
|
||
*/
|
||
if (Memory_Buffer != NULL)
|
||
{
|
||
LocalFree ((HLOCAL) Memory_Buffer);
|
||
Memory_Buffer = NULL;
|
||
}
|
||
}
|
||
|
||
/*
|
||
* PMemory AllocateMemory ()
|
||
*
|
||
* Public
|
||
*
|
||
* Functional Description:
|
||
* This function is used to allocate a Memory object from the Memory
|
||
* Manager object.
|
||
*/
|
||
PMemory MemoryManager::AllocateMemory (
|
||
PUChar reference_ptr,
|
||
ULong length,
|
||
MemoryLockMode memory_lock_mode)
|
||
{
|
||
PFreeStack free_stack;
|
||
ULong count;
|
||
PBlockNumber block_stack;
|
||
BlockNumber block_number;
|
||
PBlockInformation block_information;
|
||
PUChar copy_ptr = NULL;
|
||
PMemory memory = NULL;
|
||
|
||
// TRACE_OUT(("MemoryManager::AllocateMemory: Remaining %d memory blocks", GetBufferCount()));
|
||
|
||
/*
|
||
* If the application requests a block of size zero (0), then simply
|
||
* return a NULL without allocating a block.
|
||
*/
|
||
if (length != 0)
|
||
{
|
||
/*
|
||
* Walk through the free stack list look for a free stack that meets
|
||
* the following two allocation criteria:
|
||
*
|
||
* 1. It must contain blocks that are big enough to hold the
|
||
* reference data. This is why it is important for the block
|
||
* sizes to be specified in ascending order in the constructor.
|
||
* This code checks for a block that is big enough starting at the
|
||
* beginning. By putting them in ascending order, you are insured
|
||
* that the smallest available block will be used.
|
||
* 2. It must have enough free blocks left to allow the allocation.
|
||
* This is where priority is used. Right now it is very simple:
|
||
* the allocation will succeed if the number of available blocks
|
||
* is greater than the passed in priority (which is why a lower
|
||
* number actually reflects a higher priority).
|
||
*/
|
||
free_stack = Free_Stack;
|
||
for (count = 0; count < Free_Stack_Count; count++)
|
||
{
|
||
/*
|
||
* Check and see if the blocks in this free stack are big enough
|
||
* to hold the reference data. If so, are there enough to satisfy
|
||
* this allocation (taking memory priority into consideration).
|
||
*/
|
||
if ((length <= free_stack->block_size) &&
|
||
(free_stack->current_block_count > 0))
|
||
{
|
||
/*
|
||
* Calculate the address of the next available block number
|
||
* within the block stack. Then read the block number and
|
||
* advance the block stack offset to point to the next block.
|
||
*/
|
||
block_stack = (PBlockNumber) (Memory_Buffer +
|
||
free_stack->block_stack_offset);
|
||
block_number = *block_stack;
|
||
free_stack->block_stack_offset += sizeof (BlockNumber);
|
||
|
||
/*
|
||
* Calculate the address of the appropriate block information
|
||
* structure. Make sure that the lock count for the newly
|
||
* allocated block is zero, and the block is not marked as
|
||
* freed.
|
||
*/
|
||
block_information = (PBlockInformation) (Block_Information +
|
||
(sizeof (BlockInformation) * block_number));
|
||
ASSERT (block_information->flags & FREE_FLAG);
|
||
block_information->length = length;
|
||
block_information->lock_count = 0;
|
||
block_information->flags &= (~FREE_FLAG);
|
||
|
||
/*
|
||
* Decrement the number of blocks remaining, within this free stack.
|
||
*/
|
||
free_stack->current_block_count--;
|
||
|
||
/*
|
||
* Calculate the address of the newly allocated block. Then
|
||
* break out of the allocation loop to go use the block.
|
||
*/
|
||
copy_ptr = (PUChar) (Memory_Buffer +
|
||
block_information->block_offset);
|
||
|
||
ASSERT(copy_ptr != Memory_Buffer);
|
||
|
||
|
||
/*
|
||
* If this is a shared memory manager, and the block is not
|
||
* committed, we need to commit the block.
|
||
*/
|
||
if ((TRUE == fIsSharedMemory) && (0 == (block_information->flags & COMMIT_FLAG))) {
|
||
|
||
ASSERT ((free_stack->block_size % dwSystemPageSize) == 0);
|
||
ASSERT ((((DWORD_PTR) copy_ptr) % dwSystemPageSize) == 0);
|
||
|
||
PUChar temp = (PUChar) VirtualAlloc ((LPVOID) copy_ptr, free_stack->block_size,
|
||
MEM_COMMIT, PAGE_READWRITE);
|
||
block_information->flags |= COMMIT_FLAG;
|
||
|
||
ASSERT (temp == copy_ptr);
|
||
ASSERT (temp != NULL);
|
||
|
||
if (copy_ptr != temp) {
|
||
TRACE_OUT((">>>>>#### Copy_ptr: %p, Temp: %p, Committed?: %d",
|
||
copy_ptr, temp, block_information->flags & COMMIT_FLAG));
|
||
TRACE_OUT((">>>>>#### Size: %d, Req. length: %d",
|
||
free_stack->block_size, length));
|
||
copy_ptr = NULL;
|
||
}
|
||
}
|
||
break;
|
||
}
|
||
|
||
/*
|
||
* Point to the next entry in the free stack list.
|
||
*/
|
||
free_stack++;
|
||
}
|
||
|
||
/*
|
||
* If the memory allocation failed and it's for local memory,
|
||
* attempt to allocate external memory to hold the block.
|
||
*/
|
||
if ((copy_ptr == NULL) &&
|
||
((FALSE == bAllocs_Restricted) ||
|
||
((NULL != pExternal_Block_Information) &&
|
||
(Max_External_Blocks > pExternal_Block_Information->entries()))))
|
||
{
|
||
|
||
ASSERT (FALSE == fIsSharedMemory);
|
||
/*
|
||
* Try allocating from system memory. Set the free stack to NULL
|
||
* to indicate that this block did NOT come from one of our free
|
||
* stacks.
|
||
*/
|
||
copy_ptr = (PUChar) LocalAlloc (LMEM_FIXED, length);
|
||
|
||
if (copy_ptr != NULL)
|
||
{
|
||
/*
|
||
* Allocate a block information structure to hold relevant
|
||
* information about this externally allocated block.
|
||
*/
|
||
block_information = new BlockInformation;
|
||
|
||
if (block_information != NULL)
|
||
{
|
||
/*
|
||
* Fill in the block information structure. Block offset
|
||
* is irrelevant for an externally allocated block. A
|
||
* newly allocated block has a lock count of zero, and
|
||
* is not freed.
|
||
*/
|
||
block_information->length = length;
|
||
block_information->lock_count = 0;
|
||
block_information->flags = COMMIT_FLAG;
|
||
|
||
/*
|
||
* Put the block information structure into a dictionary
|
||
* for future use. This is only necessary for externally
|
||
* allocated blocks, since the block information structures
|
||
* for internal blocks are in the memory buffer.
|
||
*/
|
||
pExternal_Block_Information->insert ((DWORD_PTR) copy_ptr, (DWORD_PTR) block_information);
|
||
|
||
/*
|
||
* Set block number to be an
|
||
* invalid value to indicate that this block is NOT in
|
||
* the internally managed memory buffer.
|
||
*/
|
||
block_number = INVALID_BLOCK_NUMBER;
|
||
}
|
||
else
|
||
{
|
||
/*
|
||
* We were unable to allocate the space for the block
|
||
* information structure, so we must free the externally
|
||
* memory we just allocated.
|
||
*/
|
||
LocalFree ((HLOCAL) copy_ptr);
|
||
copy_ptr = NULL;
|
||
}
|
||
}
|
||
}
|
||
|
||
/*
|
||
* If there was a block available for the allocation, it is still
|
||
* necessary to create the Memory object that will hold the block.
|
||
*/
|
||
if (copy_ptr != NULL)
|
||
{
|
||
ASSERT (block_information->flags == COMMIT_FLAG);
|
||
/*
|
||
* Create the Memory object. If it fails, then cleanly release
|
||
* the memory that was to be used for this block.
|
||
*/
|
||
memory = new Memory (reference_ptr, length, copy_ptr,
|
||
block_number, memory_lock_mode);
|
||
|
||
if (memory == NULL)
|
||
{
|
||
/*
|
||
* If the free stack for the memory is not NULL, then it is
|
||
* an internally managed block. Otherwise, this was an
|
||
* externally allocated block that resulted from a critical
|
||
* allocation above.
|
||
*/
|
||
if (INVALID_BLOCK_NUMBER != block_number)
|
||
{
|
||
/*
|
||
* Adjust the block stack offset to point to the previous
|
||
* entry in the list. Note that it is not necessary to
|
||
* put the block number into the list since it still there
|
||
* from when we pulled it out above.
|
||
*/
|
||
free_stack->block_stack_offset -= sizeof (BlockNumber);
|
||
|
||
/*
|
||
* Indicate that the block is currently freed. Note that
|
||
* it is not necessary to calculate the address of the
|
||
* block information structure since we did this above.
|
||
*/
|
||
block_information->flags |= FREE_FLAG;
|
||
|
||
/*
|
||
* Decrement the block counter to indicate that there
|
||
* is another block in this free stack.
|
||
*/
|
||
free_stack->current_block_count++;
|
||
}
|
||
else
|
||
{
|
||
/*
|
||
* This block was externally allocated, so it must be
|
||
* externally freed. Also eliminate the block information
|
||
* structure associated with this memory block.
|
||
*/
|
||
pExternal_Block_Information->remove ((DWORD_PTR) copy_ptr);
|
||
delete block_information;
|
||
LocalFree ((HLOCAL) copy_ptr);
|
||
}
|
||
}
|
||
}
|
||
}
|
||
else
|
||
{
|
||
/*
|
||
* The application has attempted to allocate a block of size zero.
|
||
* It is necessary to fail the request.
|
||
*/
|
||
ERROR_OUT(("MemoryManager::AllocateMemory: attempt to allocate zero-length block"));
|
||
}
|
||
|
||
/*
|
||
* Decrement the number of blocks remaining
|
||
* in this memory manager as a whole.
|
||
*/
|
||
if ((TRUE == bAllocs_Restricted) && (memory != NULL))
|
||
Memory_Information->current_block_count--;
|
||
|
||
return (memory);
|
||
}
|
||
|
||
/*
|
||
* Void FreeMemory ()
|
||
*
|
||
* Public
|
||
*
|
||
* Functional Description:
|
||
* This function is used to release a previously allocated Memory object.
|
||
*/
|
||
Void MemoryManager::FreeMemory (
|
||
PMemory memory)
|
||
{
|
||
BlockNumber block_number;
|
||
PBlockInformation block_information;
|
||
PUChar copy_ptr;
|
||
|
||
/*
|
||
* Ask the specified memory object what block number it represents.
|
||
*/
|
||
block_number = memory->GetBlockNumber ();
|
||
|
||
/*
|
||
* Use the block number to determine if this is an internally
|
||
* allocated memory block, or an externally allocated one.
|
||
*/
|
||
if (block_number != INVALID_BLOCK_NUMBER)
|
||
{
|
||
/*
|
||
* From that, calculate the address of the block information structure.
|
||
*/
|
||
block_information = (PBlockInformation) (Block_Information +
|
||
(sizeof (BlockInformation) * block_number));
|
||
}
|
||
else
|
||
{
|
||
/*
|
||
* This is externally allocated memory, so it must be handled
|
||
* differently. Ask the memory block what the copy pointer is, and
|
||
* use that to look up the address of the block information structure.
|
||
*/
|
||
copy_ptr = memory->GetPointer ();
|
||
pExternal_Block_Information->find ((DWORD_PTR) copy_ptr, (PDWORD_PTR) &block_information);
|
||
}
|
||
|
||
/*
|
||
* Make sure that the indicated memory block has not already been
|
||
* freed.
|
||
*/
|
||
if ((block_information->flags & FREE_FLAG) == 0)
|
||
{
|
||
/*
|
||
* Mark the memory block as being freed.
|
||
*/
|
||
block_information->flags |= FREE_FLAG;
|
||
|
||
/*
|
||
* If the lock count for this block has reached zero, we can free
|
||
* the block for re-use. We can also delete the memory object, as it
|
||
* is no longer needed.
|
||
*/
|
||
if (block_information->lock_count == 0)
|
||
{
|
||
ReleaseMemory (memory);
|
||
delete memory;
|
||
}
|
||
else
|
||
{
|
||
/*
|
||
* If the lock count has not yet reached zero, check to see if the
|
||
* memory object is to be deleted anyway. If the memory lock mode
|
||
* is set to "IGNORED", then delete the memory object immediately.
|
||
*/
|
||
if (memory->GetMemoryLockMode () == MEMORY_LOCK_IGNORED)
|
||
delete memory;
|
||
}
|
||
}
|
||
else
|
||
{
|
||
/*
|
||
* The memory block has already been freed, so this call will be
|
||
* ignored.
|
||
*/
|
||
ERROR_OUT(("MemoryManager::FreeMemory: memory block already freed"));
|
||
}
|
||
}
|
||
|
||
/*
|
||
* PMemory CreateMemory ()
|
||
*
|
||
* Public
|
||
*
|
||
* Functional Description:
|
||
*/
|
||
PMemory MemoryManager::CreateMemory (
|
||
BlockNumber block_number,
|
||
MemoryLockMode memory_lock_mode)
|
||
{
|
||
ULong total_block_count = 0;
|
||
PFreeStack free_stack;
|
||
ULong count;
|
||
PBlockInformation block_information;
|
||
PUChar copy_ptr;
|
||
PMemory memory = NULL;
|
||
|
||
/*
|
||
* Make sure that this block number lies within the range handled by
|
||
* this memory manager.
|
||
*/
|
||
if (block_number < Memory_Information->total_block_count)
|
||
{
|
||
/*
|
||
* We must first walk through the free stack list to determine which
|
||
* free stack the specified block is in. Start by pointing to the
|
||
* first free stack.
|
||
*/
|
||
free_stack = Free_Stack;
|
||
for (count = 0; count < Free_Stack_Count; count++)
|
||
{
|
||
/*
|
||
* Update the counter which keeps track of how many blocks are
|
||
* represented by this free stack and the ones already processed.
|
||
* This is used to determine if the specified block number is in
|
||
* this free stack.
|
||
*/
|
||
total_block_count += free_stack->total_block_count;
|
||
|
||
/*
|
||
* Is the block in this free stack?
|
||
*/
|
||
if (block_number < total_block_count)
|
||
{
|
||
/*
|
||
* Yes it is. Claculate the address of the block information
|
||
* structure for this block. Then calculate the address of
|
||
* the actual block based on the address of the local memory
|
||
* buffer.
|
||
*/
|
||
block_information = (PBlockInformation) (Block_Information +
|
||
(sizeof (BlockInformation) * block_number));
|
||
copy_ptr = (PUChar) (Memory_Buffer +
|
||
block_information->block_offset);
|
||
ASSERT (block_information->flags & COMMIT_FLAG);
|
||
|
||
/*
|
||
* Create a memory object to represent this block.
|
||
*/
|
||
memory = new Memory (NULL, block_information->length, copy_ptr,
|
||
block_number, memory_lock_mode);
|
||
|
||
if (memory == NULL)
|
||
{
|
||
/*
|
||
* Allocation of the memory object failed, so we cannot
|
||
* create a memory block at this time.
|
||
*/
|
||
ERROR_OUT(("MemoryManager::CreateMemory: memory object allocation failed"));
|
||
}
|
||
break;
|
||
}
|
||
|
||
/*
|
||
* The block was not in the last free stack, so point to the
|
||
* next one.
|
||
*/
|
||
free_stack++;
|
||
}
|
||
}
|
||
else
|
||
{
|
||
/*
|
||
* The specified block number is out of range for this memory manager.
|
||
* The request must therefore fail.
|
||
*/
|
||
ERROR_OUT(("MemoryManager::CreateMemory: block number out of range"));
|
||
}
|
||
|
||
return (memory);
|
||
}
|
||
|
||
|
||
/*
|
||
* Void LockMemory ()
|
||
*
|
||
* Public
|
||
*
|
||
* Functional Description:
|
||
* This function is used to lock a Memory object.
|
||
*/
|
||
Void MemoryManager::LockMemory (
|
||
PMemory memory)
|
||
{
|
||
BlockNumber block_number;
|
||
PBlockInformation block_information;
|
||
PUChar copy_ptr;
|
||
|
||
/*
|
||
* Ask the specified memory object what block number it represents.
|
||
*/
|
||
block_number = memory->GetBlockNumber ();
|
||
|
||
/*
|
||
* Use the block number to determine if this is an internally
|
||
* allocated memory block, or an externally allocated one.
|
||
*/
|
||
if (block_number != INVALID_BLOCK_NUMBER)
|
||
{
|
||
/*
|
||
* From that, calculate the address of the block information structure.
|
||
*/
|
||
block_information = (PBlockInformation) (Block_Information +
|
||
(sizeof (BlockInformation) * block_number));
|
||
}
|
||
else
|
||
{
|
||
/*
|
||
* This is externally allocated memory, so it must be handled
|
||
* differently. Ask the memory block what the copy pointer is, and
|
||
* use that to look up the address of the block information structure.
|
||
*/
|
||
copy_ptr = memory->GetPointer ();
|
||
pExternal_Block_Information->find ((DWORD_PTR) copy_ptr, (PDWORD_PTR) &block_information);
|
||
}
|
||
|
||
ASSERT (block_information->flags & COMMIT_FLAG);
|
||
/*
|
||
* Increment the lock count for the specified memory block.
|
||
*/
|
||
block_information->lock_count++;
|
||
|
||
}
|
||
|
||
/*
|
||
* Void UnlockMemory ()
|
||
*
|
||
* Public
|
||
*
|
||
* Functional Description:
|
||
* This function is used to unlock a previously locked Memory object.
|
||
*/
|
||
Void MemoryManager::UnlockMemory (
|
||
PMemory memory)
|
||
{
|
||
BlockNumber block_number;
|
||
PBlockInformation block_information;
|
||
PUChar copy_ptr;
|
||
|
||
/*
|
||
* Ask the specified memory object what block number it represents.
|
||
*/
|
||
block_number = memory->GetBlockNumber ();
|
||
|
||
/*
|
||
* Use the block number to determine if this is an internally
|
||
* allocated memory block, or an externally allocated one.
|
||
*/
|
||
if (block_number != INVALID_BLOCK_NUMBER)
|
||
{
|
||
/*
|
||
* From that, calculate the address of the block information structure.
|
||
*/
|
||
block_information = (PBlockInformation) (Block_Information +
|
||
(sizeof (BlockInformation) * block_number));
|
||
}
|
||
else
|
||
{
|
||
/*
|
||
* This is externally allocated memory, so it must be handled
|
||
* differently. Ask the memory block what the copy pointer is, and
|
||
* use that to look up the address of the block information structure.
|
||
*/
|
||
copy_ptr = memory->GetPointer ();
|
||
pExternal_Block_Information->find ((DWORD_PTR) copy_ptr, (PDWORD_PTR) &block_information);
|
||
}
|
||
|
||
ASSERT (block_information->flags & COMMIT_FLAG);
|
||
/*
|
||
* Make sure that the lock isn't already zero before proceeding.
|
||
*/
|
||
if (block_information->lock_count > 0)
|
||
{
|
||
/*
|
||
* Decrement the lock count for the specified memory block.
|
||
*/
|
||
block_information->lock_count--;
|
||
|
||
/*
|
||
* If the lock count has reached zero and the memory block is
|
||
* marked as being freed, then we can free the block for re-use.
|
||
*/
|
||
if ((block_information->lock_count == 0) &&
|
||
(block_information->flags & FREE_FLAG))
|
||
{
|
||
ReleaseMemory (memory);
|
||
|
||
/*
|
||
* We have now released the memory buffer, so we must check to
|
||
* see if we are supposed to destroy the memory object itself.
|
||
*/
|
||
if (memory->GetMemoryLockMode () == MEMORY_LOCK_NORMAL)
|
||
delete memory;
|
||
}
|
||
}
|
||
else
|
||
{
|
||
/*
|
||
* The specified block has a lock count of zero already, so ignore
|
||
* this call.
|
||
*/
|
||
ERROR_OUT(("MemoryManager::UnlockMemory: memory block already unlocked"));
|
||
}
|
||
}
|
||
|
||
/*
|
||
* ULong GetBufferCount ()
|
||
*
|
||
* Public
|
||
*
|
||
* Functional Description:
|
||
*/
|
||
ULong MemoryManager::GetBufferCount (
|
||
ULong length)
|
||
{
|
||
PFreeStack free_stack;
|
||
ULong count;
|
||
ULong buffer_count;
|
||
|
||
if (FALSE == bAllocs_Restricted)
|
||
return (LARGE_BUFFER_COUNT);
|
||
|
||
buffer_count = Memory_Information->current_block_count;
|
||
free_stack = Free_Stack;
|
||
for (count = 0; count < Free_Stack_Count; count++)
|
||
{
|
||
/*
|
||
* Check and see if the blocks in this free stack are smaller than
|
||
* the specified length. If yes, we need to deduct these buffers.
|
||
* Otherwise, we can stop deducting.
|
||
*/
|
||
if (length > free_stack->block_size) {
|
||
buffer_count -= free_stack->current_block_count;
|
||
|
||
/*
|
||
* Point to the next entry in the free stack list.
|
||
*/
|
||
free_stack++;
|
||
}
|
||
else
|
||
break;
|
||
}
|
||
|
||
return (buffer_count);
|
||
}
|
||
|
||
/*
|
||
* Void ReleaseMemory (
|
||
* PMemory memory)
|
||
*
|
||
* Private
|
||
*
|
||
* Functional Description:
|
||
* This function is used to release a Memory object, and free the memory
|
||
* it represents back to the available pool.
|
||
*
|
||
* Formal Parameters:
|
||
* memory
|
||
* This is a pointer to the Memory object being released.
|
||
*
|
||
* Return Value:
|
||
* None.
|
||
*
|
||
* Side Effects:
|
||
* None.
|
||
*
|
||
* Caveats:
|
||
* None.
|
||
*/
|
||
Void MemoryManager::ReleaseMemory (
|
||
PMemory memory)
|
||
{
|
||
PFreeStack free_stack;
|
||
BlockNumber block_number;
|
||
PBlockNumber block_stack;
|
||
PBlockInformation block_information;
|
||
PUChar copy_ptr;
|
||
|
||
/*
|
||
* Ask the specified memory object what block number it represents.
|
||
*/
|
||
block_number = memory->GetBlockNumber ();
|
||
|
||
/*
|
||
* Use the block number to determine if this is an internally
|
||
* allocated memory block, or an externally allocated one.
|
||
*/
|
||
if (block_number != INVALID_BLOCK_NUMBER)
|
||
{
|
||
/*
|
||
* From that, calculate the address of the block information structure.
|
||
*/
|
||
block_information = (PBlockInformation) (Block_Information +
|
||
(sizeof (BlockInformation) * block_number));
|
||
|
||
/*
|
||
* Get the address of the free stack from which this block came.
|
||
*/
|
||
free_stack = (PFreeStack) (Memory_Buffer + block_information->free_stack_offset);
|
||
|
||
/*
|
||
* Adjust the block stack offset to point to the previous element,
|
||
* and then use it to calculate an address and put the block number
|
||
* there. This effectively "pushes" the block number onto the stack.
|
||
*/
|
||
free_stack->block_stack_offset -= sizeof (BlockNumber);
|
||
block_stack = (PBlockNumber) (Memory_Buffer +
|
||
free_stack->block_stack_offset);
|
||
*block_stack = block_number;
|
||
|
||
/*
|
||
* Indicate that this block is freed.
|
||
*/
|
||
block_information->flags = FREE_FLAG | COMMIT_FLAG;
|
||
|
||
/*
|
||
* Increment the counter indicating the number of available blocks
|
||
* in this free stack.
|
||
*/
|
||
free_stack->current_block_count++;
|
||
}
|
||
else
|
||
{
|
||
/*
|
||
* Since the block was allocated from system memory, thats where it
|
||
* needs to go back to.
|
||
*/
|
||
copy_ptr = memory->GetPointer ();
|
||
pExternal_Block_Information->find ((DWORD_PTR) copy_ptr, (PDWORD_PTR) &block_information);
|
||
pExternal_Block_Information->remove ((DWORD_PTR) copy_ptr);
|
||
delete block_information;
|
||
LocalFree ((HLOCAL) copy_ptr);
|
||
}
|
||
|
||
/*
|
||
* Increment the number of blocks available in this memory manager as a whole.
|
||
*/
|
||
if (TRUE == bAllocs_Restricted)
|
||
Memory_Information->current_block_count++;
|
||
}
|
||
|
||
/*
|
||
* ULong CalculateMemoryBufferSize (
|
||
* PMemoryTemplate memory_template,
|
||
* ULong memory_count,
|
||
* ULong * pulCommittedBytes)
|
||
*
|
||
* Protected
|
||
*
|
||
* Functional Description:
|
||
* This member function is used to calculate how much memory will be
|
||
* required in order to manage the number of memory blocks specified in
|
||
* the passed in memory template. Note that this total includes the size
|
||
* of the memory blocks as well as the amount of memory used for management
|
||
* functions.
|
||
*
|
||
* Formal Parameters:
|
||
* memory_template
|
||
* This is an array of structures that identify the blocks to be
|
||
* managed by this object.
|
||
* memory_count
|
||
* This is the number of entries in the above array.
|
||
* pulCommittedBytes
|
||
* If fIsSharedMemory == FALSE, this can be NULL. Otherwise, it is
|
||
* used to return the size of the total memory we need to commit
|
||
* when the manager is getting initialized.
|
||
*
|
||
* Return Value:
|
||
* The required size of the memory buffer for this object.
|
||
*
|
||
* Side Effects:
|
||
* None.
|
||
*
|
||
* Caveats:
|
||
* None.
|
||
*/
|
||
|
||
ULong MemoryManager::CalculateMemoryBufferSize (
|
||
PMemoryTemplate memory_template,
|
||
ULong memory_count,
|
||
ULong * pulCommittedBytes)
|
||
{
|
||
ULong memory_buffer_size;
|
||
PMemoryTemplate pMemTemplate;
|
||
ULong memory_per_block;
|
||
|
||
/*
|
||
* Claculate the amount of memory that will be required to hold the
|
||
* memory information structure and the free stacks.
|
||
*/
|
||
memory_buffer_size = (sizeof (MemoryInformation) +
|
||
(sizeof (FreeStack) * memory_count));
|
||
|
||
if (FALSE == fIsSharedMemory) {
|
||
/*
|
||
* Add in the amount of memory the block stacks, the block information
|
||
* structures, and the memory blocks themselves will take up.
|
||
*/
|
||
for (pMemTemplate = memory_template; pMemTemplate - memory_template < (int) memory_count; pMemTemplate++)
|
||
{
|
||
/*
|
||
* The amount of memory required for each managed block of memory can
|
||
* be calculated as a sum of the following:
|
||
*
|
||
* 1. sizeof (BlockNumber) - This is the amount of space taken by the
|
||
* block number in the block stack.
|
||
* 2. sizeof (BlockInformation) - Every managed block of memory has
|
||
* a BlockInformation structure associated with it.
|
||
* 3. block_size - The actual size of the block. This is provided
|
||
* in the memory template.
|
||
*/
|
||
memory_per_block = sizeof (BlockNumber) + sizeof (BlockInformation) +
|
||
pMemTemplate->block_size;
|
||
memory_buffer_size += (memory_per_block * pMemTemplate->block_count);
|
||
}
|
||
}
|
||
|
||
/*
|
||
* For shared memory, we need to do a few more extra things:
|
||
*
|
||
* Blocks of size greater or equal to the system's page, need to
|
||
* start on a page boundary. In addition, they can be expanded to
|
||
* end at a page boundary, too.
|
||
*/
|
||
else {
|
||
|
||
ULong reserved_buffer_size = 0;
|
||
ULong temp;
|
||
|
||
for (pMemTemplate = memory_template; pMemTemplate - memory_template < (int) memory_count; pMemTemplate++) {
|
||
if (dwSystemPageSize <= pMemTemplate->block_size) {
|
||
pMemTemplate->block_size = EXPAND_TO_PAGE_BOUNDARY(pMemTemplate->block_size);
|
||
reserved_buffer_size += pMemTemplate->block_count * pMemTemplate->block_size;
|
||
}
|
||
memory_per_block = sizeof (BlockNumber) + sizeof (BlockInformation) +
|
||
pMemTemplate->block_size;
|
||
memory_buffer_size += memory_per_block * pMemTemplate->block_count;
|
||
}
|
||
*pulCommittedBytes = memory_buffer_size - reserved_buffer_size;
|
||
temp = EXPAND_TO_PAGE_BOUNDARY(*pulCommittedBytes);
|
||
temp -= (*pulCommittedBytes);
|
||
*pulCommittedBytes += temp;
|
||
memory_buffer_size += temp;
|
||
ASSERT (*pulCommittedBytes <= memory_buffer_size);
|
||
ASSERT ((memory_buffer_size % dwSystemPageSize) == 0);
|
||
ASSERT ((*pulCommittedBytes % dwSystemPageSize) == 0);
|
||
ASSERT ((reserved_buffer_size % dwSystemPageSize) == 0);
|
||
}
|
||
|
||
return (memory_buffer_size);
|
||
}
|
||
|
||
|
||
/*
|
||
* Void AllocateMemoryBuffer (
|
||
* ULong memory_buffer_size)
|
||
*
|
||
* Protected
|
||
*
|
||
* Functional Description:
|
||
* This member function allocates the memory that is managed by an instance
|
||
* of MemoryManager. It does this using the standard Malloc macro.
|
||
*
|
||
* Formal Parameters:
|
||
* memory_buffer_size
|
||
* The size of the buffer to be allocated.
|
||
*
|
||
* Return Value:
|
||
* None.
|
||
*
|
||
* Side Effects:
|
||
* The instance variable Memory_Buffer is set to the address of the
|
||
* allocated block of memory. If it is NULL after the return from this
|
||
* call, that indicates that the memory could not be allocated.
|
||
*
|
||
* Caveats:
|
||
* None.
|
||
*/
|
||
Void MemoryManager::AllocateMemoryBuffer (
|
||
ULong memory_buffer_size)
|
||
{
|
||
TRACE_OUT(("MemoryManager::AllocateMemoryBuffer: allocating %ld bytes", memory_buffer_size));
|
||
if (memory_buffer_size != 0)
|
||
Memory_Buffer = (HPUChar) LocalAlloc (LMEM_FIXED, memory_buffer_size);
|
||
else
|
||
Memory_Buffer = NULL;
|
||
}
|
||
|
||
|
||
/*
|
||
* Void InitializeMemoryBuffer (
|
||
* PMemoryTemplate memory_template,
|
||
* ULong memory_count)
|
||
*
|
||
* Protected
|
||
*
|
||
* Functional Description:
|
||
* This member function is used to initialize the memory buffer for use.
|
||
* This primarily includes filling in the management structures that lie
|
||
* at the beginning of the allocated memory block, so that allocations
|
||
* can take place.
|
||
*
|
||
* Formal Parameters:
|
||
* memory_template
|
||
* This is an array of structures that identify the blocks to be
|
||
* managed by this object.
|
||
* memory_count
|
||
* This is the number of entries in the above array.
|
||
*
|
||
* Return Value:
|
||
* None.
|
||
*
|
||
* Side Effects:
|
||
* None.
|
||
*
|
||
* Caveats:
|
||
* None.
|
||
*/
|
||
|
||
Void MemoryManager::InitializeMemoryBuffer (
|
||
PMemoryTemplate memory_template,
|
||
ULong memory_count)
|
||
{
|
||
ULong block_count = 0;
|
||
ULong index;
|
||
ULong memory_information_size;
|
||
ULong free_stack_size;
|
||
ULong free_stack_offset;
|
||
ULong block_stack_size;
|
||
ULong block_information_size;
|
||
PFreeStack free_stack;
|
||
PBlockNumber block_stack;
|
||
PBlockInformation block_information;
|
||
ULong block_stack_offset;
|
||
BlockNumber block_number;
|
||
ULong block_offset;
|
||
ULong block_size;
|
||
ULong count;
|
||
BOOL fIsFirstTime;
|
||
|
||
/*
|
||
* Walk through the memory template calculating how many memory blocks
|
||
* exist (regardless of size).
|
||
*/
|
||
for (index = 0; index < memory_count; index++)
|
||
block_count += memory_template[index].block_count;
|
||
|
||
/*
|
||
* Calculate the amount of memory required to hold all the various sections
|
||
* of data in the memory buffer.
|
||
*/
|
||
memory_information_size = sizeof (MemoryInformation);
|
||
free_stack_size = sizeof (FreeStack) * memory_count;
|
||
block_stack_size = sizeof (BlockNumber) * block_count;
|
||
block_information_size = sizeof (BlockInformation) * block_count;
|
||
|
||
/*
|
||
* Initialize all elements of the memory information structure.
|
||
* Note that all offsets in this structure are from the beginning of the
|
||
* memory buffer.
|
||
*/
|
||
Memory_Information = (PMemoryInformation) Memory_Buffer;
|
||
Memory_Information->free_stack_offset = memory_information_size;
|
||
Memory_Information->free_stack_count = memory_count;
|
||
Memory_Information->block_information_offset =
|
||
memory_information_size + free_stack_size + block_stack_size;
|
||
Memory_Information->total_block_count = block_count;
|
||
if (TRUE == bAllocs_Restricted) {
|
||
// The current_block_count is only needed when allocations are restricted.
|
||
Memory_Information->current_block_count = block_count + Max_External_Blocks;
|
||
}
|
||
|
||
/*
|
||
* Now initialize the instance variables that point to each list within
|
||
* the memory buffer. These instance variables are later used to resolve
|
||
* all other offsets.
|
||
*/
|
||
Free_Stack = (PFreeStack) (Memory_Buffer + memory_information_size);
|
||
Free_Stack_Count = memory_count;
|
||
Block_Information = (Memory_Buffer +
|
||
Memory_Information->block_information_offset);
|
||
|
||
/*
|
||
* This loop walks through the memory template array again, this time
|
||
* filling in the contents of the free stacks, the blocks stacks, and
|
||
* the block information structures.
|
||
*/
|
||
fIsFirstTime = TRUE;
|
||
free_stack = Free_Stack;
|
||
free_stack_offset = memory_information_size;
|
||
block_stack_offset = memory_information_size + free_stack_size;
|
||
block_stack = (PBlockNumber) (Memory_Buffer + block_stack_offset);
|
||
block_information = (PBlockInformation) Block_Information;
|
||
block_number = 0;
|
||
block_offset = block_stack_offset + block_stack_size + block_information_size;
|
||
|
||
for (index = 0; index < memory_count; index++)
|
||
{
|
||
/*
|
||
* Get the block size and count from the template entry.
|
||
*/
|
||
block_size = memory_template[index].block_size;
|
||
block_count = memory_template[index].block_count;
|
||
|
||
/*
|
||
* Initialize the free stack for this block size, and then point to
|
||
* the next free stack in the list.
|
||
*/
|
||
free_stack->block_size = block_size;
|
||
free_stack->total_block_count = block_count;
|
||
free_stack->current_block_count = block_count;
|
||
(free_stack++)->block_stack_offset = block_stack_offset;
|
||
|
||
/*
|
||
* Adjust the block stack offset to point to the first block number
|
||
* of the next free stack (skip past all of the block numbers for
|
||
* this free stack).
|
||
*/
|
||
block_stack_offset += (sizeof (BlockNumber) * block_count);
|
||
|
||
/*
|
||
* The following happens only once in this loop:
|
||
* When the memory manager manages shared memory and
|
||
* The block size becomes FOR THE 1ST TIME, bigger than
|
||
* the page size, then, we need to jump to the next page
|
||
* boundary.
|
||
*/
|
||
if ((TRUE == fIsSharedMemory) && (TRUE == fIsFirstTime)
|
||
&& (block_size >= dwSystemPageSize)) {
|
||
fIsFirstTime = FALSE;
|
||
block_offset = EXPAND_TO_PAGE_BOUNDARY(block_offset);
|
||
}
|
||
|
||
/*
|
||
* Initialize the block list for this block size. Also, increment
|
||
* the total number of buffers for each block that is segmented
|
||
* off.
|
||
*/
|
||
for (count = 0; count < block_count; count++)
|
||
{
|
||
/*
|
||
* Put the block number for this block into the current block
|
||
* stack. Increment both the block stack pointer and the block
|
||
* number.
|
||
*/
|
||
*(block_stack++) = block_number++;
|
||
|
||
/*
|
||
* Fill in the block information structure for this block. Then
|
||
* increment the block information pointer to point to the next
|
||
* entry in the list.
|
||
*/
|
||
#ifdef _DEBUG
|
||
if ((TRUE == fIsSharedMemory) && (block_size >= dwSystemPageSize)) {
|
||
ASSERT ((block_size % dwSystemPageSize) == 0);
|
||
ASSERT ((block_offset % dwSystemPageSize) == 0);
|
||
}
|
||
#endif
|
||
block_information->block_offset = block_offset;
|
||
block_information->free_stack_offset = free_stack_offset;
|
||
if ((TRUE == fIsSharedMemory) && (block_size >= dwSystemPageSize))
|
||
block_information->flags = FREE_FLAG;
|
||
else
|
||
block_information->flags = FREE_FLAG | COMMIT_FLAG;
|
||
block_information++;
|
||
|
||
/*
|
||
* Adjust the block offset to point to the next block.
|
||
*/
|
||
block_offset += block_size;
|
||
}
|
||
|
||
free_stack_offset += sizeof (FreeStack);
|
||
}
|
||
}
|
||
|
||
|