Added functions to trim extra memory within allocator. Made a new_handler
function for allocator. Added deallocator function for nothrow delete operator to insure nothing is leaked when constructor throws. git-svn-id: svn://svn.code.sf.net/p/loki-lib/code/trunk@229 7ec92016-0320-0410-acc4-a06ded1c099a
This commit is contained in:
parent
d740b86c0a
commit
c78269f482
2 changed files with 198 additions and 9 deletions
|
@ -59,10 +59,14 @@ namespace Loki
|
||||||
|
|
||||||
void Deallocate( void * p, std::size_t size );
|
void Deallocate( void * p, std::size_t size );
|
||||||
|
|
||||||
|
void Deallocate( void * p );
|
||||||
|
|
||||||
inline std::size_t GetMaxObjectSize() const { return maxSmallObjectSize_; }
|
inline std::size_t GetMaxObjectSize() const { return maxSmallObjectSize_; }
|
||||||
|
|
||||||
inline std::size_t GetAlignment() const { return objectAlignSize_; }
|
inline std::size_t GetAlignment() const { return objectAlignSize_; }
|
||||||
|
|
||||||
|
bool TrimExcessMemory( void );
|
||||||
|
|
||||||
private:
|
private:
|
||||||
/// Copy-constructor is not implemented.
|
/// Copy-constructor is not implemented.
|
||||||
SmallObjAllocator( const SmallObjAllocator & );
|
SmallObjAllocator( const SmallObjAllocator & );
|
||||||
|
@ -85,19 +89,39 @@ namespace Loki
|
||||||
|
|
||||||
template
|
template
|
||||||
<
|
<
|
||||||
template <class> class ThreadingModel,
|
template <class> class ThreadingModel = DEFAULT_THREADING_NO_OBJ_LEVEL,
|
||||||
std::size_t chunkSize,
|
std::size_t chunkSize = DEFAULT_CHUNK_SIZE,
|
||||||
std::size_t maxSmallObjectSize,
|
std::size_t maxSmallObjectSize = MAX_SMALL_OBJECT_SIZE,
|
||||||
std::size_t objectAlignSize
|
std::size_t objectAlignSize = LOKI_DEFAULT_OBJECT_ALIGNMENT,
|
||||||
|
template <class> class LifetimePolicy = Loki::NoDestroy
|
||||||
>
|
>
|
||||||
class AllocatorSingleton : public SmallObjAllocator
|
class AllocatorSingleton : public SmallObjAllocator
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
|
||||||
|
/// Defines type of allocator.
|
||||||
|
typedef AllocatorSingleton< ThreadingModel, chunkSize,
|
||||||
|
maxSmallObjectSize, objectAlignSize, LifetimePolicy > MyAllocator;
|
||||||
|
|
||||||
|
/// Defines type for thread-safety locking mechanism.
|
||||||
|
typedef ThreadingModel< MyAllocator > MyThreadingModel;
|
||||||
|
|
||||||
|
/// Defines singleton made from allocator.
|
||||||
|
typedef Loki::SingletonHolder< MyAllocator, Loki::CreateStatic,
|
||||||
|
LifetimePolicy, ThreadingModel > MyAllocatorSingleton;
|
||||||
|
|
||||||
|
inline static AllocatorSingleton & Instance( void )
|
||||||
|
{
|
||||||
|
return MyAllocatorSingleton::Instance();
|
||||||
|
}
|
||||||
|
|
||||||
inline AllocatorSingleton() :
|
inline AllocatorSingleton() :
|
||||||
SmallObjAllocator( chunkSize, maxSmallObjectSize, objectAlignSize )
|
SmallObjAllocator( chunkSize, maxSmallObjectSize, objectAlignSize )
|
||||||
{}
|
{}
|
||||||
inline ~AllocatorSingleton( void ) {}
|
inline ~AllocatorSingleton( void ) {}
|
||||||
|
|
||||||
|
static void ClearExtraMemory( void );
|
||||||
|
|
||||||
private:
|
private:
|
||||||
/// Copy-constructor is not implemented.
|
/// Copy-constructor is not implemented.
|
||||||
AllocatorSingleton( const AllocatorSingleton & );
|
AllocatorSingleton( const AllocatorSingleton & );
|
||||||
|
@ -105,6 +129,20 @@ namespace Loki
|
||||||
AllocatorSingleton & operator = ( const AllocatorSingleton & );
|
AllocatorSingleton & operator = ( const AllocatorSingleton & );
|
||||||
};
|
};
|
||||||
|
|
||||||
|
template
|
||||||
|
<
|
||||||
|
template <class> class TM,
|
||||||
|
std::size_t CS,
|
||||||
|
std::size_t MSOS,
|
||||||
|
std::size_t OAS,
|
||||||
|
template <class> class LP
|
||||||
|
>
|
||||||
|
void AllocatorSingleton< TM, CS, MSOS, OAS, LP >::ClearExtraMemory( void )
|
||||||
|
{
|
||||||
|
typename MyThreadingModel::Lock lock;
|
||||||
|
(void)lock; // get rid of warning
|
||||||
|
Instance().TrimExcessMemory();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
@ -174,12 +212,11 @@ namespace Loki
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Non-throwing single-object delete.
|
/// Non-throwing single-object delete.
|
||||||
static void operator delete ( void * p, std::size_t size,
|
static void operator delete ( void * p, const std::nothrow_t & ) throw()
|
||||||
const std::nothrow_t & ) throw()
|
|
||||||
{
|
{
|
||||||
typename MyThreadingModel::Lock lock;
|
typename MyThreadingModel::Lock lock;
|
||||||
(void)lock; // get rid of warning
|
(void)lock; // get rid of warning
|
||||||
MyAllocatorSingleton::Instance().Deallocate( p, size );
|
MyAllocatorSingleton::Instance().Deallocate( p );
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Placement single-object delete.
|
/// Placement single-object delete.
|
||||||
|
@ -261,6 +298,11 @@ namespace Loki
|
||||||
// Nov. 26, 2004: re-implemented by Rich Sposato.
|
// Nov. 26, 2004: re-implemented by Rich Sposato.
|
||||||
//
|
//
|
||||||
// $Log$
|
// $Log$
|
||||||
|
// Revision 1.8 2005/09/09 00:24:59 rich_sposato
|
||||||
|
// Added functions to trim extra memory within allocator. Made a new_handler
|
||||||
|
// function for allocator. Added deallocator function for nothrow delete
|
||||||
|
// operator to insure nothing is leaked when constructor throws.
|
||||||
|
//
|
||||||
// Revision 1.7 2005/09/01 22:01:33 rich_sposato
|
// Revision 1.7 2005/09/01 22:01:33 rich_sposato
|
||||||
// Added #ifdef to deal with MSVC warning about exception specification lists.
|
// Added #ifdef to deal with MSVC warning about exception specification lists.
|
||||||
//
|
//
|
||||||
|
|
151
src/SmallObj.cpp
151
src/SmallObj.cpp
|
@ -100,6 +100,12 @@ namespace Loki
|
||||||
inline std::size_t BlockSize() const
|
inline std::size_t BlockSize() const
|
||||||
{ return blockSize_; }
|
{ return blockSize_; }
|
||||||
|
|
||||||
|
bool TrimEmptyChunk( void );
|
||||||
|
|
||||||
|
std::size_t CountEmptyChunks( void ) const;
|
||||||
|
|
||||||
|
bool HasBlock( void * p ) const;
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
@ -251,6 +257,85 @@ void FixedAllocator::Initialize( std::size_t blockSize, std::size_t pageSize )
|
||||||
assert(numBlocks_ == numBlocks);
|
assert(numBlocks_ == numBlocks);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// FixedAllocator::CountEmptyChunks -------------------------------------------
|
||||||
|
/// Returns count of number of empty Chunks inside the chunk list.
|
||||||
|
|
||||||
|
std::size_t FixedAllocator::CountEmptyChunks( void ) const
|
||||||
|
{
|
||||||
|
std::size_t count = 0;
|
||||||
|
for ( ChunkCIter it( chunks_.begin() ); it != chunks_.end(); ++it )
|
||||||
|
{
|
||||||
|
const Chunk & chunk = *it;
|
||||||
|
if ( chunk.HasAvailable( numBlocks_ ) )
|
||||||
|
++count;
|
||||||
|
}
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
|
||||||
|
// FixedAllocator::HasBlock ---------------------------------------------------
|
||||||
|
/// Determines if any Chunk inside FixedAllocator has a block at address p.
|
||||||
|
/// @return True if Chunk owned by this has the block, else false.
|
||||||
|
|
||||||
|
bool FixedAllocator::HasBlock( void * p ) const
|
||||||
|
{
|
||||||
|
const std::size_t chunkLength = numBlocks_ * blockSize_;
|
||||||
|
unsigned char * pc = static_cast< unsigned char * >( p );
|
||||||
|
for ( ChunkCIter it( chunks_.begin() ); it != chunks_.end(); ++it )
|
||||||
|
{
|
||||||
|
const Chunk & chunk = *it;
|
||||||
|
if ( chunk.HasBlock( pc, chunkLength ) )
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// FixedAllocator::TrimEmptyChunk
|
||||||
|
// Releases the memory used by the empty Chunk.
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
bool FixedAllocator::TrimEmptyChunk( void )
|
||||||
|
{
|
||||||
|
// prove either emptyChunk_ points nowhere, or points to a truly empty Chunk.
|
||||||
|
assert( ( NULL == emptyChunk_ ) || ( emptyChunk_->HasAvailable( numBlocks_ ) ) );
|
||||||
|
if ( NULL == emptyChunk_ ) return false;
|
||||||
|
|
||||||
|
// If emptyChunk_ points to valid Chunk, then chunk list is not empty.
|
||||||
|
assert( !chunks_.empty() );
|
||||||
|
// And there should be exactly 1 empty Chunk.
|
||||||
|
assert( 1 == CountEmptyChunks() );
|
||||||
|
|
||||||
|
Chunk * lastChunk = &chunks_.back();
|
||||||
|
if ( lastChunk != emptyChunk_ )
|
||||||
|
std::swap( *emptyChunk_, *lastChunk );
|
||||||
|
assert( lastChunk->HasAvailable( numBlocks_ ) );
|
||||||
|
lastChunk->Release();
|
||||||
|
chunks_.pop_back();
|
||||||
|
|
||||||
|
assert( 0 == CountEmptyChunks() );
|
||||||
|
if ( chunks_.empty() )
|
||||||
|
{
|
||||||
|
allocChunk_ = NULL;
|
||||||
|
deallocChunk_ = NULL;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if ( deallocChunk_ == emptyChunk_ )
|
||||||
|
{
|
||||||
|
deallocChunk_ = &chunks_.front();
|
||||||
|
assert( deallocChunk_->blocksAvailable_ < numBlocks_ );
|
||||||
|
}
|
||||||
|
if ( allocChunk_ == emptyChunk_ )
|
||||||
|
{
|
||||||
|
allocChunk_ = &chunks_.back();
|
||||||
|
assert( allocChunk_->blocksAvailable_ < numBlocks_ );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
emptyChunk_ = NULL;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
// FixedAllocator::MakeNewChunk
|
// FixedAllocator::MakeNewChunk
|
||||||
// Allocates a new Chunk for a FixedAllocator.
|
// Allocates a new Chunk for a FixedAllocator.
|
||||||
|
@ -295,6 +380,7 @@ void * FixedAllocator::Allocate( void )
|
||||||
{
|
{
|
||||||
// prove either emptyChunk_ points nowhere, or points to a truly empty Chunk.
|
// prove either emptyChunk_ points nowhere, or points to a truly empty Chunk.
|
||||||
assert( ( NULL == emptyChunk_ ) || ( emptyChunk_->HasAvailable( numBlocks_ ) ) );
|
assert( ( NULL == emptyChunk_ ) || ( emptyChunk_->HasAvailable( numBlocks_ ) ) );
|
||||||
|
assert( CountEmptyChunks() < 2 );
|
||||||
|
|
||||||
if ( ( NULL == allocChunk_ ) || allocChunk_->IsFilled() )
|
if ( ( NULL == allocChunk_ ) || allocChunk_->IsFilled() )
|
||||||
{
|
{
|
||||||
|
@ -331,8 +417,9 @@ void * FixedAllocator::Allocate( void )
|
||||||
assert( !allocChunk_->IsFilled() );
|
assert( !allocChunk_->IsFilled() );
|
||||||
void *place = allocChunk_->Allocate(blockSize_);
|
void *place = allocChunk_->Allocate(blockSize_);
|
||||||
|
|
||||||
// prove either emptyChunk_ points nowhere, or points to a truly empty Chunk.
|
// prove emptyChunk_ points nowhere.
|
||||||
assert( ( NULL == emptyChunk_ ) || ( emptyChunk_->HasAvailable( numBlocks_ ) ) );
|
assert( NULL == emptyChunk_ );
|
||||||
|
assert( 0 == CountEmptyChunks() );
|
||||||
|
|
||||||
return place;
|
return place;
|
||||||
}
|
}
|
||||||
|
@ -352,6 +439,7 @@ bool FixedAllocator::Deallocate( void * p, bool doChecks )
|
||||||
assert(&chunks_.back() >= deallocChunk_);
|
assert(&chunks_.back() >= deallocChunk_);
|
||||||
assert( &chunks_.front() <= allocChunk_ );
|
assert( &chunks_.front() <= allocChunk_ );
|
||||||
assert( &chunks_.back() >= allocChunk_ );
|
assert( &chunks_.back() >= allocChunk_ );
|
||||||
|
assert( CountEmptyChunks() < 2 );
|
||||||
}
|
}
|
||||||
|
|
||||||
Chunk * foundChunk = VicinityFind( p );
|
Chunk * foundChunk = VicinityFind( p );
|
||||||
|
@ -364,6 +452,8 @@ bool FixedAllocator::Deallocate( void * p, bool doChecks )
|
||||||
|
|
||||||
deallocChunk_ = foundChunk;
|
deallocChunk_ = foundChunk;
|
||||||
DoDeallocate(p);
|
DoDeallocate(p);
|
||||||
|
assert( CountEmptyChunks() < 2 );
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -532,6 +622,23 @@ SmallObjAllocator::~SmallObjAllocator( void )
|
||||||
delete [] pool_;
|
delete [] pool_;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// SmallObjAllocator::TrimExcessMemory
|
||||||
|
// Trims excess memory within all FixedAllocators.
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
bool SmallObjAllocator::TrimExcessMemory( void )
|
||||||
|
{
|
||||||
|
bool found = false;
|
||||||
|
const std::size_t allocCount = GetOffset( GetMaxObjectSize(), GetAlignment() );
|
||||||
|
for ( std::size_t i = 0; i < allocCount; ++i )
|
||||||
|
{
|
||||||
|
if ( pool_[ i ].TrimEmptyChunk() )
|
||||||
|
found = true;
|
||||||
|
}
|
||||||
|
return found;
|
||||||
|
}
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
// SmallObjAllocator::Allocate
|
// SmallObjAllocator::Allocate
|
||||||
// Handles request to allocate numBytes for 1 object.
|
// Handles request to allocate numBytes for 1 object.
|
||||||
|
@ -556,6 +663,10 @@ void * SmallObjAllocator::Allocate( std::size_t numBytes, bool doThrow )
|
||||||
assert( allocator.BlockSize() >= numBytes );
|
assert( allocator.BlockSize() >= numBytes );
|
||||||
assert( allocator.BlockSize() < numBytes + GetAlignment() );
|
assert( allocator.BlockSize() < numBytes + GetAlignment() );
|
||||||
void * place = allocator.Allocate();
|
void * place = allocator.Allocate();
|
||||||
|
|
||||||
|
if ( ( NULL == place ) && TrimExcessMemory() )
|
||||||
|
place = allocator.Allocate();
|
||||||
|
|
||||||
if ( ( NULL == place ) && doThrow )
|
if ( ( NULL == place ) && doThrow )
|
||||||
{
|
{
|
||||||
#if _MSC_VER
|
#if _MSC_VER
|
||||||
|
@ -596,6 +707,37 @@ void SmallObjAllocator::Deallocate( void * p, std::size_t numBytes )
|
||||||
assert( found );
|
assert( found );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// SmallObjAllocator::Deallocate
|
||||||
|
// Handles request to deallocate 1 object that was allocated by non-throwing
|
||||||
|
// new operator. This will act in linear-time. It will never throw.
|
||||||
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
|
void SmallObjAllocator::Deallocate( void * p )
|
||||||
|
{
|
||||||
|
if ( NULL == p ) return;
|
||||||
|
assert( NULL != pool_ );
|
||||||
|
FixedAllocator * pAllocator = NULL;
|
||||||
|
const std::size_t allocCount = GetOffset( GetMaxObjectSize(), GetAlignment() );
|
||||||
|
|
||||||
|
for ( std::size_t ii = 0; ii < allocCount; ++ii )
|
||||||
|
{
|
||||||
|
if ( pool_[ ii ].HasBlock( p ) )
|
||||||
|
{
|
||||||
|
pAllocator = &pool_[ ii ];
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if ( NULL == pAllocator )
|
||||||
|
{
|
||||||
|
DefaultDeallocator( p );
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const bool found = pAllocator->Deallocate( p, true );
|
||||||
|
assert( found );
|
||||||
|
}
|
||||||
|
|
||||||
} // end namespace Loki
|
} // end namespace Loki
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
@ -609,6 +751,11 @@ void SmallObjAllocator::Deallocate( void * p, std::size_t numBytes )
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
// $Log$
|
// $Log$
|
||||||
|
// Revision 1.4 2005/09/09 00:25:00 rich_sposato
|
||||||
|
// Added functions to trim extra memory within allocator. Made a new_handler
|
||||||
|
// function for allocator. Added deallocator function for nothrow delete
|
||||||
|
// operator to insure nothing is leaked when constructor throws.
|
||||||
|
//
|
||||||
// Revision 1.3 2005/09/01 22:15:47 rich_sposato
|
// Revision 1.3 2005/09/01 22:15:47 rich_sposato
|
||||||
// Changed Chunk list to double in size when adding new chunks instead of
|
// Changed Chunk list to double in size when adding new chunks instead of
|
||||||
// just incrementing by 1. Changes linear operation into amortized constant
|
// just incrementing by 1. Changes linear operation into amortized constant
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue