533 lines
13 KiB
C
533 lines
13 KiB
C
|
// Copyright (C) 2007 Id Software, Inc.
|
||
|
//
|
||
|
|
||
|
|
||
|
#ifndef __LIB_POOL_ALLOCATOR_H__
|
||
|
#define __LIB_POOL_ALLOCATOR_H__
|
||
|
|
||
|
namespace sdDetails {
|
||
|
/*
|
||
|
===============================================================================
|
||
|
|
||
|
Block based allocator for fixed size objects. (based off of idBlockAlloc)
|
||
|
All objects of the 'type' are NOT constructed.
|
||
|
|
||
|
===============================================================================
|
||
|
*/
|
||
|
|
||
|
template<class type, int blockSize>
|
||
|
class sdPoolAlloc {
|
||
|
public:
|
||
|
sdPoolAlloc( void );
|
||
|
~sdPoolAlloc( void );
|
||
|
|
||
|
void Shutdown( void );
|
||
|
|
||
|
type * Alloc( void );
|
||
|
void Free( type *element );
|
||
|
int Compact( void );
|
||
|
|
||
|
int GetTotalCount( void ) const { return total; }
|
||
|
int GetAllocCount( void ) const { return active; }
|
||
|
int GetFreeCount( void ) const { return total - active; }
|
||
|
|
||
|
private:
|
||
|
struct element_t {
|
||
|
element_t * next;
|
||
|
char t[ sizeof( type ) ];
|
||
|
};
|
||
|
struct block_t {
|
||
|
element_t elements[blockSize];
|
||
|
block_t * next;
|
||
|
};
|
||
|
|
||
|
block_t * blocks;
|
||
|
element_t * free;
|
||
|
int total;
|
||
|
int active;
|
||
|
int numFree;
|
||
|
|
||
|
private:
|
||
|
bool IsFree( const element_t* element );
|
||
|
};
|
||
|
|
||
|
/*
|
||
|
============
|
||
|
sdPoolAlloc<type,blockSize>::sdPoolAlloc
|
||
|
============
|
||
|
*/
|
||
|
template<class type, int blockSize>
|
||
|
sdPoolAlloc<type,blockSize>::sdPoolAlloc( void ) {
|
||
|
blocks = NULL;
|
||
|
free = NULL;
|
||
|
total = active = numFree = 0;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
============
|
||
|
sdPoolAlloc<type,blockSize>::~sdPoolAlloc
|
||
|
============
|
||
|
*/
|
||
|
template<class type, int blockSize>
|
||
|
sdPoolAlloc<type,blockSize>::~sdPoolAlloc( void ) {
|
||
|
Shutdown();
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
============
|
||
|
sdPoolAlloc<type,blockSize>::IsFree
|
||
|
============
|
||
|
*/
|
||
|
template<class type, int blockSize>
|
||
|
bool sdPoolAlloc<type,blockSize>::IsFree( const element_t* element ) {
|
||
|
element_t* iter = free;
|
||
|
while( iter != NULL ) {
|
||
|
if( iter == element ) {
|
||
|
return true;
|
||
|
}
|
||
|
iter = iter->next;
|
||
|
}
|
||
|
return false;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
============
|
||
|
sdPoolAlloc<type,blockSize>::Alloc
|
||
|
============
|
||
|
*/
|
||
|
template<class type, int blockSize>
|
||
|
type *sdPoolAlloc<type,blockSize>::Alloc( void ) {
|
||
|
if ( !free ) {
|
||
|
block_t *block = new block_t;
|
||
|
block->next = blocks;
|
||
|
blocks = block;
|
||
|
for ( int i = 0; i < blockSize; i++ ) {
|
||
|
block->elements[i].next = free;
|
||
|
free = &block->elements[i];
|
||
|
}
|
||
|
total += blockSize;
|
||
|
}
|
||
|
active++;
|
||
|
element_t *element = free;
|
||
|
free = free->next;
|
||
|
element->next = NULL;
|
||
|
return reinterpret_cast< type* >( &element->t );
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
============
|
||
|
sdPoolAlloc<type,blockSize>::Free
|
||
|
============
|
||
|
*/
|
||
|
template<class type, int blockSize>
|
||
|
void sdPoolAlloc<type,blockSize>::Free( type *t ) {
|
||
|
if( t == NULL ) {
|
||
|
return;
|
||
|
}
|
||
|
element_t *element = (element_t *)( ( (unsigned char *) t ) - ( (UINT_PTR) &((element_t *)0)->t ) );
|
||
|
//element_t *element;
|
||
|
//element = ( element_t * )( ( (unsigned char *)t ) - offsetof( element_t, t ) );
|
||
|
element->next = free;
|
||
|
free = element;
|
||
|
active--;
|
||
|
numFree++;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
============
|
||
|
sdPoolAlloc<type,blockSize>::Compact
|
||
|
find blocks that are completely empty and free them
|
||
|
|
||
|
this is relatively slow and should only be called after an event that is known to
|
||
|
free a large chunk of elements
|
||
|
|
||
|
returns the number of bytes freed
|
||
|
============
|
||
|
*/
|
||
|
template<class type, int blockSize>
|
||
|
int sdPoolAlloc<type,blockSize>::Compact( void ) {
|
||
|
bool usedHeap = false;
|
||
|
block_t** blocksToUnlink = NULL;
|
||
|
int blocksToUnlinkNum = 0;
|
||
|
|
||
|
if( numFree >= ( 512 * 1024 ) ) {
|
||
|
usedHeap = true;
|
||
|
blocksToUnlink = static_cast< block_t** >( Mem_AllocAligned( numFree, ALIGN_16 ) );
|
||
|
} else {
|
||
|
blocksToUnlink = static_cast< block_t** >( _alloca16( numFree ) );
|
||
|
}
|
||
|
|
||
|
block_t* block = blocks;
|
||
|
block_t* prev = NULL;
|
||
|
|
||
|
while( block != NULL ) {
|
||
|
bool allFreed = true;
|
||
|
|
||
|
for( int i = 0; i < blockSize; i++ ) {
|
||
|
if( !IsFree( &block->elements[ i ] ) ) {
|
||
|
allFreed = false;
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// all elements are in the free list, unlink and deallocate this block
|
||
|
if( allFreed ) {
|
||
|
// unlink all elements from the free list
|
||
|
|
||
|
for( int i = 0; i < blockSize; i++ ) {
|
||
|
element_t* current = &block->elements[ i ];
|
||
|
element_t* prev = NULL;
|
||
|
element_t* iter = free;
|
||
|
while( iter != NULL ) {
|
||
|
element_t* next = iter->next;
|
||
|
if( iter == current ) {
|
||
|
if( prev != NULL ) {
|
||
|
prev->next = next;
|
||
|
} else {
|
||
|
free = next;
|
||
|
}
|
||
|
break;
|
||
|
}
|
||
|
prev = iter;
|
||
|
iter = next;
|
||
|
}
|
||
|
}
|
||
|
blocksToUnlink[ blocksToUnlinkNum++ ] = block;
|
||
|
} else {
|
||
|
prev = block;
|
||
|
}
|
||
|
block = block->next;
|
||
|
}
|
||
|
|
||
|
assert( blocksToUnlinkNum <= numFree );
|
||
|
|
||
|
for( int i = 0; i < blocksToUnlinkNum; i++ ) {
|
||
|
block_t* current = blocksToUnlink[ i ];
|
||
|
block_t* prev = NULL;
|
||
|
block_t* iter = blocks;
|
||
|
while( iter != NULL ) {
|
||
|
block_t* next = iter->next;
|
||
|
if( iter == current ) {
|
||
|
if( prev != NULL ) {
|
||
|
prev->next = next;
|
||
|
} else {
|
||
|
blocks = next;
|
||
|
}
|
||
|
break;
|
||
|
}
|
||
|
prev = iter;
|
||
|
iter = next;
|
||
|
}
|
||
|
delete current;
|
||
|
}
|
||
|
|
||
|
if( usedHeap ) {
|
||
|
Mem_FreeAligned( blocksToUnlink );
|
||
|
blocksToUnlink = NULL;
|
||
|
}
|
||
|
|
||
|
numFree -= blocksToUnlinkNum;
|
||
|
|
||
|
total -= blockSize * blocksToUnlinkNum;
|
||
|
return blocksToUnlinkNum * blockSize * sizeof( type );
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
============
|
||
|
sdPoolAlloc<type,blockSize>::Shutdown
|
||
|
============
|
||
|
*/
|
||
|
template<class type, int blockSize>
|
||
|
void sdPoolAlloc<type,blockSize>::Shutdown( void ) {
|
||
|
while( blocks != NULL ) {
|
||
|
block_t *block = blocks;
|
||
|
blocks = blocks->next;
|
||
|
delete block;
|
||
|
}
|
||
|
blocks = NULL;
|
||
|
free = NULL;
|
||
|
total = active = 0;
|
||
|
}
|
||
|
|
||
|
}
|
||
|
|
||
|
// derive class T from sdPoolAllocator< T > to allocate all objects of type T from a memory pool
|
||
|
// use new and delete like normal on the objects
|
||
|
#define SD_DISAMBIGUATE_POOL_ALLOCATOR( Allocator ) \
|
||
|
void* operator new[]( size_t size ) { return Allocator::operator new[]( size ); } \
|
||
|
void operator delete[]( void* ptr, size_t size ) { Allocator::operator delete[]( ptr, size ); } \
|
||
|
void* operator new( size_t size ) { return Allocator::operator new( size ); } \
|
||
|
void operator delete( void* ptr, size_t size ) { Allocator::operator delete( ptr, size ); } \
|
||
|
void* operator new[]( size_t size, size_t t1, int t2, char *fileName, int lineNumber ) { return Allocator::operator new[]( size, t1, t2, fileName, lineNumber ); } \
|
||
|
void operator delete[]( void *ptr, size_t size, int t2, char *fileName, int lineNumber ) { Allocator::operator delete[]( ptr, size, t2, fileName, lineNumber ); } \
|
||
|
void* operator new( size_t size, size_t t1, int t2, char *fileName, int lineNumber ) { return Allocator::operator new( size, t1, t2, fileName, lineNumber ); } \
|
||
|
void operator delete( void *ptr, size_t size, int t2, char *fileName, int lineNumber ) { Allocator::operator delete( ptr, size, t2, fileName, lineNumber ); }
|
||
|
|
||
|
extern const char sdPoolAllocator_DefaultIdentifier[];
|
||
|
class sdDynamicBlockManagerBase {
|
||
|
public:
|
||
|
virtual ~sdDynamicBlockManagerBase() {}
|
||
|
|
||
|
virtual void Init() = 0;
|
||
|
virtual void Shutdown() = 0;
|
||
|
|
||
|
virtual void PrintInfo() = 0;
|
||
|
virtual void Accumulate( int& baseBytes, int& freeBytes ) = 0;
|
||
|
virtual const char* GetName() const = 0;
|
||
|
virtual int Compact() = 0;
|
||
|
virtual void Purge() = 0;
|
||
|
virtual bool IsValid() const = 0;
|
||
|
|
||
|
static void MemoryReport( const idCmdArgs& args );
|
||
|
|
||
|
static void InitPools();
|
||
|
static void ShutdownPools();
|
||
|
static void CompactPools();
|
||
|
|
||
|
protected:
|
||
|
typedef idList< sdDynamicBlockManagerBase* > blockManagerList_t;
|
||
|
static blockManagerList_t& GetList() {
|
||
|
static blockManagerList_t list;
|
||
|
return list;
|
||
|
}
|
||
|
};
|
||
|
|
||
|
/*
|
||
|
============
|
||
|
sdDynamicBlockManager
|
||
|
============
|
||
|
*/
|
||
|
template< class T, const char* blockName, size_t blockSize >
|
||
|
class sdDynamicBlockManager :
|
||
|
public sdDynamicBlockManagerBase {
|
||
|
public:
|
||
|
typedef sdDetails::sdPoolAlloc< T, blockSize > allocatorType_t;
|
||
|
|
||
|
sdDynamicBlockManager() {
|
||
|
Init();
|
||
|
GetList().Append( this );
|
||
|
}
|
||
|
virtual ~sdDynamicBlockManager() {
|
||
|
}
|
||
|
|
||
|
virtual void Init() {
|
||
|
assert( allocator == NULL );
|
||
|
allocator = new allocatorType_t;
|
||
|
}
|
||
|
virtual void Shutdown() {
|
||
|
if( allocator != NULL ) {
|
||
|
allocator->Shutdown();
|
||
|
delete allocator;
|
||
|
allocator = NULL;
|
||
|
}
|
||
|
GetList().Remove( this );
|
||
|
}
|
||
|
|
||
|
const char* GetName() const { return blockName; }
|
||
|
|
||
|
T* Alloc() {
|
||
|
assert( allocator != NULL );
|
||
|
if( allocator == NULL ) {
|
||
|
return NULL;
|
||
|
}
|
||
|
return allocator->Alloc();
|
||
|
}
|
||
|
|
||
|
void Free( T* ptr ) {
|
||
|
assert( allocator != NULL );
|
||
|
if( allocator == NULL ) {
|
||
|
return;
|
||
|
}
|
||
|
allocator->Free( ptr );
|
||
|
}
|
||
|
|
||
|
virtual int Compact() {
|
||
|
if( allocator == NULL ) {
|
||
|
return 0;
|
||
|
}
|
||
|
return allocator->Compact();
|
||
|
}
|
||
|
|
||
|
virtual void Purge() {
|
||
|
if( allocator != NULL ) {
|
||
|
allocator->Shutdown();
|
||
|
}
|
||
|
}
|
||
|
|
||
|
virtual void PrintInfo() {
|
||
|
idLib::Printf( "\n%s\n", blockName );
|
||
|
idLib::Printf( "===================\n", blockName );
|
||
|
idLib::Printf( "Base Block Memory %i bytes free, %i bytes total\n", allocator->GetFreeCount() * sizeof( T ), allocator->GetTotalCount() * sizeof( T ) );
|
||
|
}
|
||
|
|
||
|
virtual void Accumulate( int& baseBytes, int& freeBytes ) {
|
||
|
assert( allocator != NULL );
|
||
|
baseBytes += allocator->GetTotalCount();
|
||
|
freeBytes += allocator->GetFreeCount();
|
||
|
}
|
||
|
|
||
|
virtual bool IsValid() const { return allocator != NULL; }
|
||
|
|
||
|
private:
|
||
|
allocatorType_t* allocator;
|
||
|
};
|
||
|
|
||
|
class sdLockingPolicy_None {
|
||
|
public:
|
||
|
void Acquire() {}
|
||
|
void Release() {}
|
||
|
};
|
||
|
|
||
|
class sdLockingPolicy_Lock {
|
||
|
public:
|
||
|
void Acquire() { lock.Acquire(); }
|
||
|
void Release() { lock.Release(); }
|
||
|
|
||
|
private:
|
||
|
sdLock lock;
|
||
|
};
|
||
|
|
||
|
/*
|
||
|
============
|
||
|
sdPoolAllocator
|
||
|
============
|
||
|
*/
|
||
|
template< class T, const char* name = sdPoolAllocator_DefaultIdentifier, size_t baseBlockSize = 512, class lockingPolicy = sdLockingPolicy_None >
|
||
|
class sdPoolAllocator {
|
||
|
|
||
|
public:
|
||
|
static const size_t ELEMENTS_PER_PAGE = baseBlockSize;
|
||
|
|
||
|
// free the entire pool
|
||
|
static void PurgeAllocator() {
|
||
|
GetMemoryManager().Purge();
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
============
|
||
|
operator new
|
||
|
============
|
||
|
*/
|
||
|
#ifdef ID_REDIRECT_NEWDELETE
|
||
|
#undef new
|
||
|
#endif
|
||
|
void* operator new( size_t size ) {
|
||
|
#ifdef ID_REDIRECT_NEWDELETE
|
||
|
#define new ID_DEBUG_NEW
|
||
|
#endif
|
||
|
if( size != sizeof( T )) {
|
||
|
assert( 0 );
|
||
|
}
|
||
|
|
||
|
lock.Acquire();
|
||
|
void* retVal = static_cast< void* >( GetMemoryManager().Alloc() );
|
||
|
lock.Release();
|
||
|
return retVal;
|
||
|
}
|
||
|
|
||
|
#ifdef ID_REDIRECT_NEWDELETE
|
||
|
#undef new
|
||
|
#endif
|
||
|
void* operator new( size_t size, size_t t1, int t2, char *fileName, int lineNumber ) {
|
||
|
#ifdef ID_REDIRECT_NEWDELETE
|
||
|
#define new ID_DEBUG_NEW
|
||
|
#endif
|
||
|
if( size != sizeof( T )) {
|
||
|
assert( 0 );
|
||
|
}
|
||
|
|
||
|
lock.Acquire();
|
||
|
void* retVal = static_cast< void* >( GetMemoryManager().Alloc() );
|
||
|
lock.Release();
|
||
|
return retVal;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
============
|
||
|
operator delete
|
||
|
============
|
||
|
*/
|
||
|
void operator delete( void* ptr, size_t size ) {
|
||
|
lock.Acquire();
|
||
|
GetMemoryManager().Free( static_cast< T* >( ptr ) );
|
||
|
lock.Release();
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
============
|
||
|
operator delete
|
||
|
============
|
||
|
*/
|
||
|
void operator delete( void *ptr, size_t size, int t2, char *fileName, int lineNumber ) {
|
||
|
lock.Acquire();
|
||
|
GetMemoryManager().Free( static_cast< T* >( ptr ) );
|
||
|
lock.Release();
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
============
|
||
|
operator new[]
|
||
|
============
|
||
|
*/
|
||
|
#ifdef ID_REDIRECT_NEWDELETE
|
||
|
#undef new
|
||
|
#endif
|
||
|
void* operator new[]( size_t size ) {
|
||
|
#ifdef ID_REDIRECT_NEWDELETE
|
||
|
#define new ID_DEBUG_NEW
|
||
|
#endif
|
||
|
return ::new char[ size ];
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
============
|
||
|
operator new[]
|
||
|
============
|
||
|
*/
|
||
|
#ifdef ID_REDIRECT_NEWDELETE
|
||
|
#undef new
|
||
|
#endif
|
||
|
void* operator new[]( size_t size, size_t t1, int t2, char *fileName, int lineNumber ) {
|
||
|
#ifdef ID_REDIRECT_NEWDELETE
|
||
|
#define new ID_DEBUG_NEW
|
||
|
#endif
|
||
|
return ::new char[ size ];
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
============
|
||
|
operator delete[]
|
||
|
============
|
||
|
*/
|
||
|
void operator delete[]( void* ptr, size_t size ) {
|
||
|
::delete[]( static_cast< char* >( ptr ) );
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
============
|
||
|
operator delete[]
|
||
|
============
|
||
|
*/
|
||
|
void operator delete[]( void *ptr, size_t size, int t2, char *fileName, int lineNumber ) {
|
||
|
::delete[]( static_cast< char* >( ptr ) );
|
||
|
}
|
||
|
|
||
|
private:
|
||
|
typedef lockingPolicy LockingPolicy;
|
||
|
static LockingPolicy lock;
|
||
|
|
||
|
typedef sdDynamicBlockManager< T, name, baseBlockSize > memoryManager_t;
|
||
|
static memoryManager_t& GetMemoryManager() {
|
||
|
static memoryManager_t manager;
|
||
|
if( !manager.IsValid() ) {
|
||
|
manager.Init();
|
||
|
}
|
||
|
return manager;
|
||
|
}
|
||
|
};
|
||
|
|
||
|
template< class T, const char* name, size_t baseBlockSize, class lockingPolicy >
|
||
|
typename sdPoolAllocator< T, name, baseBlockSize, lockingPolicy >::LockingPolicy sdPoolAllocator< T, name, baseBlockSize, lockingPolicy >::lock;
|
||
|
|
||
|
#endif // !__LIB_POOL_ALLOCATOR_H__
|