The-Simpsons-Hit-and-Run/game/code/memory/memorypool.cpp

342 lines
9.4 KiB
C++

//==============================================================================
// Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved.
//
// File: memorypool.cpp
//
// Description: Implementation of the memory pool based on the one described
// in "Effective C++".
//
// History: + 2001/12/04 Created -- Darwin Chau
// + 2002/06/19 Adapted form Simpsons2 -- Darwin Chau
//
//==============================================================================
//========================================
// System Includes
//========================================
// Foundation
#include <raddebug.hpp>
//========================================
// Project Includes
//========================================
#include <memory/memorypool.h>
//******************************************************************************
//
// Global Data, Local Data, Local Classes
//
//******************************************************************************
//******************************************************************************
//
// Public Member Functions
//
//******************************************************************************
//==============================================================================
// FBMemoryPool::FBMemoryPool
//==============================================================================
//
// Description: Constructor
//
// Parameters: size
// - size (in bytes) of the object being memory pooled
//
// blockCapacity
// - controls the size of each memory block in the the pool by
// specifying the number of objects that will fit in each block
// (block size = size * blockCapacity)
//
// allocator
// - specifiy which heap to allocate memory from
//
// Return: N/A.
//
//==============================================================================
FBMemoryPool::FBMemoryPool
(
size_t size,
int blockCapacity,
GameMemoryAllocator allocator
)
:
mSize( size ),
mBlockCapacity( blockCapacity ),
mGMAllocator( allocator ),
mpHeadOfFreeList( NULL ),
mCurrentAllocs( 0 ),
mCurrentBlock( 0 )
{
int i;
for( i = 0; i < MAX_BLOCKS; ++i )
{
mpBlockArray[mCurrentBlock] = NULL;
}
#ifndef RAD_RELEASE
mTotalAllocs = 0;
mTotalFrees = 0;
mPeakAllocs = 0;
#endif // !RAD_RELEASE
}
//==============================================================================
// FBMemoryPool::~FBMemoryPool
//==============================================================================
//
// Description: Destructor
//
// Parameters: None.
//
// Return: N/A.
//
//==============================================================================
FBMemoryPool::~FBMemoryPool()
{
rAssertMsg( mCurrentAllocs == 0, "*** MEMORY LEAK ! ***\n" );
}
//==============================================================================
// FBMemoryPool::Allocate
//==============================================================================
//
// Description: Allocate memory from the pool. The pool allocates memory in
// large blocks which it then parcels out for subsequent allocation
// requests. All allocations from the pool must be the same size.
//
// Constraints: The size of the memory pool is aritificially capped by the
// max number of blocks that can be allocated. This was done
// to avoid managing the memory required by an STL container class.
//
// Parameters: size - amount of memory requested; this is only used to verify
// the size is the same as what the pool is configured for
//
// Return: pointer to allocated memory on success
// NULL on failure
//
//==============================================================================
void* FBMemoryPool::Allocate( size_t size )
{
rAssert( size <= mSize );
void* pMemory = NULL;
//
// Allocate memory from our pool.
//
if( mpHeadOfFreeList != NULL )
{
//
// Allocate from the free list.
//
pMemory = static_cast<void*>( mpHeadOfFreeList );
mpHeadOfFreeList = mpHeadOfFreeList->mpNext;
}
else
{
//
// Need to allocate a new block of memory.
//
if( mCurrentBlock == MAX_BLOCKS )
{
//
// Array to store blocks is full.
//
rTuneString( "FBMemoryPool - Max blocks exceeded, tune pool config.\n");
return( NULL );
}
void* pNewBlock = ::operator new( (mBlockCapacity * mSize), mGMAllocator );
rAssert( pNewBlock != 0 );
//
// Store the pointer so we can delete the block later.
//
mpBlockArray[mCurrentBlock] = pNewBlock;
++mCurrentBlock;
//
// Make a linked list out of the block.
//
for( int i = 0; i < mBlockCapacity - 1; ++i )
{
char* pCurrent = static_cast<char*>(pNewBlock) + (i * mSize);
MemoryPoolList* pList = reinterpret_cast<MemoryPoolList*>( pCurrent );
char* pNext = static_cast<char*>(pNewBlock) + ( (i + 1) * mSize);
pList->mpNext = reinterpret_cast<MemoryPoolList*>( pNext );
}
//
// Mark the end of the list with a NULL.
//
char* pLast = static_cast<char*>(pNewBlock) + ( (mBlockCapacity - 1) * mSize);
MemoryPoolList* pListEnd = reinterpret_cast<MemoryPoolList*>( pLast );
pListEnd->mpNext = NULL;
//
// Return this piece of memory.
//
pMemory = pNewBlock;
//
// This is the new head of the free list.
//
mpHeadOfFreeList = reinterpret_cast<MemoryPoolList*>( pNewBlock )->mpNext;
}
++mCurrentAllocs;
#ifndef RAD_RELEASE
++mTotalAllocs;
if( mTotalAllocs > mPeakAllocs )
{
mPeakAllocs = mTotalAllocs;
}
#endif // !RAD_RELEASE
return( pMemory );
}
//==============================================================================
// FBMemoryPool::Free
//==============================================================================
//
// Description: Return memory to the pool. When all of the memory has been
// returned to the pool, all of the memory blocks are released.
//
// Parameters: mem - pointer to memory being returned to the pool
// size - size of memory being returned.
//
// Return: None.
//
//==============================================================================
void FBMemoryPool::Free( void* mem, size_t size )
{
//
// Calling delete on a NULL pointer is "legal" so better handle it.
//
if( mem == NULL )
{
return;
}
rAssert( size <= mSize );
//
// Return the memory to the free list.
//
MemoryPoolList* pCarcass = static_cast<MemoryPoolList*>( mem );
pCarcass->mpNext = mpHeadOfFreeList;
mpHeadOfFreeList = pCarcass;
--mCurrentAllocs;
//
// If there are no outstanding alloctions, free all the memory blocks.
//
if( mCurrentAllocs == 0 )
{
int i;
for( i = 0; i < MAX_BLOCKS; ++i )
{
if( mpBlockArray[i] != NULL )
{
::operator delete( mpBlockArray[i], mGMAllocator );
mpBlockArray[i] = NULL;
}
}
mpHeadOfFreeList = NULL;
mCurrentBlock = 0;
}
#ifndef RAD_RELEASE
++mTotalFrees;
if( mCurrentAllocs == 0 )
{
this->DumpStats();
}
#endif // !RAD_RELEASE
}
//==============================================================================
// FBMemoryPool::ChangeAllocator
//==============================================================================
//
// Description: Can only switch heaps if there are no outstanding allocations.
//
// Parameters: allocator - switch to allocating from this heap
//
// Return: true if successful
// false otherwise
//
//==============================================================================
bool FBMemoryPool::ChangeAllocator( GameMemoryAllocator allocator )
{
if( mCurrentAllocs == 0 )
{
mGMAllocator = allocator;
return( true );
}
else
{
rAssertMsg( false, "Failed to change allocators" );
return( false );
}
}
bool FBMemoryPool::OneOfOurs(void* mem)
{
for( int i = 0; i < MAX_BLOCKS; ++i )
{
if( mpBlockArray[i] != NULL )
{
if((mem >= mpBlockArray[i]) && (mem < (((char*)mpBlockArray[i]) + (mBlockCapacity * mSize))))
{
return true;
}
}
}
return false;
}
//==============================================================================
// FBMemoryPool::DumpStats
//==============================================================================
//
// Description: Debug spew.
//
// Parameters: None.
//
// Return: None.
//
//==============================================================================
void FBMemoryPool::DumpStats()
{
rTunePrintf( "FBMemoryPool Stats: (Element Size: %d ; Block Capacity: %d)\n",
mSize, mBlockCapacity );
rTunePrintf( "\tCurrent Allocs: %d\n", mCurrentAllocs );
#ifndef RAD_RELEASE
rTunePrintf( "\tTotal Allocs: %d\n", mTotalAllocs );
rTunePrintf( "\tTotal Frees: %d\n", mTotalFrees );
rTunePrintf( "\tPeak Allocs: %d\n", mPeakAllocs );
#endif // !RAD_RELEASE
}