Added functions to check for corrupted Chunks and FixedAllocators. Made
several minor coding changes. git-svn-id: svn://svn.code.sf.net/p/loki-lib/code/trunk@377 7ec92016-0320-0410-acc4-a06ded1c099a
This commit is contained in:
parent
fb0f712fde
commit
985f330d3d
1 changed files with 322 additions and 28 deletions
350
src/SmallObj.cpp
350
src/SmallObj.cpp
|
@ -20,12 +20,13 @@
|
|||
|
||||
#include <cassert>
|
||||
#include <vector>
|
||||
#include <bitset>
|
||||
|
||||
//#define DO_EXTRA_LOKI_TESTS
|
||||
//#define USE_NEW_TO_ALLOCATE
|
||||
|
||||
#ifdef DO_EXTRA_LOKI_TESTS
|
||||
#include <iostream>
|
||||
#include <bitset>
|
||||
#endif
|
||||
|
||||
|
||||
|
@ -108,11 +109,33 @@ namespace Loki
|
|||
/// Releases the allocated block of memory.
|
||||
void Release();
|
||||
|
||||
bool IsCorrupt( unsigned char numBlocks, std::size_t blockSize ) const;
|
||||
/** Determines if the Chunk has been corrupted.
|
||||
@param numBlocks Total # of blocks in the Chunk.
|
||||
@param blockSize # of bytes in each block.
|
||||
@param checkIndexes True if caller wants to check indexes of available
|
||||
blocks for corruption. If false, then caller wants to skip some
|
||||
tests tests just to run faster. (Debug version does more checks, but
|
||||
release version runs faster.)
|
||||
@return True if Chunk is corrupt.
|
||||
*/
|
||||
bool IsCorrupt( unsigned char numBlocks, std::size_t blockSize,
|
||||
bool checkIndexes ) const;
|
||||
|
||||
/** Determines if block is available.
|
||||
@param p Address of block managed by Chunk.
|
||||
@param numBlocks Total # of blocks in the Chunk.
|
||||
@param blockSize # of bytes in each block.
|
||||
@return True if block is available, else false if allocated.
|
||||
*/
|
||||
bool IsBlockAvailable( void * p, unsigned char numBlocks,
|
||||
std::size_t blockSize ) const;
|
||||
|
||||
/// Returns true if block at address P is inside this Chunk.
|
||||
inline bool HasBlock( unsigned char * p, std::size_t chunkLength ) const
|
||||
{ return ( pData_ <= p ) && ( p < pData_ + chunkLength ); }
|
||||
inline bool HasBlock( void * p, std::size_t chunkLength ) const
|
||||
{
|
||||
unsigned char * pc = static_cast< unsigned char * >( p );
|
||||
return ( pData_ <= pc ) && ( pc < pData_ + chunkLength );
|
||||
}
|
||||
|
||||
inline bool HasAvailable( unsigned char numBlocks ) const
|
||||
{ return ( blocksAvailable_ == numBlocks ); }
|
||||
|
@ -238,6 +261,14 @@ namespace Loki
|
|||
*/
|
||||
std::size_t CountEmptyChunks( void ) const;
|
||||
|
||||
/** Determines if FixedAllocator is corrupt. Checks data members to
|
||||
see if any have erroneous values, or violate class invariants. It
|
||||
also checks if any Chunk is corrupt. Complexity is O(C) where C is
|
||||
the number of Chunks. If any data is corrupt, this will return true
|
||||
in release mode, or assert in debug mode.
|
||||
*/
|
||||
bool IsCorrupt( void ) const;
|
||||
|
||||
/** Returns true if the block at address p is within a Chunk owned by
|
||||
this FixedAllocator. Complexity is O(C) where C is the total number
|
||||
of Chunks - empty or used.
|
||||
|
@ -251,7 +282,6 @@ namespace Loki
|
|||
|
||||
};
|
||||
|
||||
|
||||
// Chunk::Init ----------------------------------------------------------------
|
||||
|
||||
bool Chunk::Init( std::size_t blockSize, unsigned char blocks )
|
||||
|
@ -338,7 +368,7 @@ void Chunk::Deallocate(void* p, std::size_t blockSize)
|
|||
#if defined(DEBUG) || defined(_DEBUG)
|
||||
// Check if block was already deleted. Attempting to delete the same
|
||||
// block more than once causes Chunk's linked-list of stealth indexes to
|
||||
// become corrupt. And causes count of blocksAvailable_ ) to be wrong.
|
||||
// become corrupt. And causes count of blocksAvailable_ to be wrong.
|
||||
if ( 0 < blocksAvailable_ )
|
||||
assert( firstAvailableBlock_ != index );
|
||||
#endif
|
||||
|
@ -353,38 +383,144 @@ void Chunk::Deallocate(void* p, std::size_t blockSize)
|
|||
|
||||
// Chunk::IsCorrupt -----------------------------------------------------------
|
||||
|
||||
bool Chunk::IsCorrupt( unsigned char numBlocks, std::size_t blockSize ) const
|
||||
bool Chunk::IsCorrupt( unsigned char numBlocks, std::size_t blockSize,
|
||||
bool checkIndexes ) const
|
||||
{
|
||||
|
||||
if ( numBlocks < blocksAvailable_ ) return true;
|
||||
if ( 0 == blocksAvailable_ ) return false;
|
||||
if ( numBlocks < blocksAvailable_ )
|
||||
{
|
||||
// Contents at this Chunk corrupted. This might mean something has
|
||||
// overwritten memory owned by the Chunks container.
|
||||
assert( false );
|
||||
return true;
|
||||
}
|
||||
if ( IsFilled() )
|
||||
// Useless to do further corruption checks if all blocks allocated.
|
||||
return false;
|
||||
unsigned char index = firstAvailableBlock_;
|
||||
if ( numBlocks <= index ) return true;
|
||||
if ( numBlocks <= index )
|
||||
{
|
||||
// Contents at this Chunk corrupted. This might mean something has
|
||||
// overwritten memory owned by the Chunks container.
|
||||
assert( false );
|
||||
return true;
|
||||
}
|
||||
if ( !checkIndexes )
|
||||
// Caller chose to skip more complex corruption tests.
|
||||
return false;
|
||||
|
||||
#ifdef DO_EXTRA_LOKI_TESTS
|
||||
std::bitset< 256 > foundBlocks;
|
||||
/* If the bit at index was set in foundBlocks, then the stealth index was
|
||||
found on the linked-list.
|
||||
*/
|
||||
std::bitset< UCHAR_MAX > foundBlocks;
|
||||
unsigned char * nextBlock = NULL;
|
||||
|
||||
unsigned char cc = 0;
|
||||
for ( ;; )
|
||||
/* The loop goes along singly linked-list of stealth indexes and makes sure
|
||||
that each index is within bounds (0 <= index < numBlocks) and that the
|
||||
index was not already found while traversing the linked-list. The linked-
|
||||
list should have exactly blocksAvailable_ nodes, so the for loop will not
|
||||
check more than blocksAvailable_. This loop can't check inside allocated
|
||||
blocks for corruption since such blocks are not within the linked-list.
|
||||
Contents of allocated blocks are not changed by Chunk.
|
||||
|
||||
Here are the types of corrupted link-lists which can be verified. The
|
||||
corrupt index is shown with asterisks in each example.
|
||||
|
||||
Type 1: Index is too big.
|
||||
numBlocks == 64
|
||||
blocksAvailable_ == 7
|
||||
firstAvailableBlock_ -> 17 -> 29 -> *101*
|
||||
There should be no indexes which are equal to or larger than the total
|
||||
number of blocks. Such an index would refer to a block beyond the
|
||||
Chunk's allocated domain.
|
||||
|
||||
Type 2: Index is repeated.
|
||||
numBlocks == 64
|
||||
blocksAvailable_ == 5
|
||||
firstAvailableBlock_ -> 17 -> 29 -> 53 -> *17* -> 29 -> 53 ...
|
||||
No index should be repeated within the linked-list since that would
|
||||
indicate the presence of a loop in the linked-list.
|
||||
*/
|
||||
for ( unsigned char cc = 0; ; )
|
||||
{
|
||||
nextBlock = pData_ + ( index * blockSize );
|
||||
foundBlocks.set( index, true );
|
||||
++cc;
|
||||
if ( cc >= blocksAvailable_ )
|
||||
// Successfully counted off number of nodes in linked-list.
|
||||
break;
|
||||
index = *nextBlock;
|
||||
if ( numBlocks <= index )
|
||||
{
|
||||
/* This catches Type 1 corruptions as shown in above comments.
|
||||
This implies that a block was corrupted due to a stray pointer
|
||||
or an operation on a nearby block overran the size of the block.
|
||||
*/
|
||||
assert( false );
|
||||
return true;
|
||||
}
|
||||
if ( foundBlocks.test( index ) )
|
||||
{
|
||||
/* This catches Type 2 corruptions as shown in above comments.
|
||||
This implies that a block was corrupted due to a stray pointer
|
||||
or an operation on a nearby block overran the size of the block.
|
||||
Or perhaps the program tried to delete a block more than once.
|
||||
*/
|
||||
assert( false );
|
||||
return true;
|
||||
}
|
||||
}
|
||||
if ( foundBlocks.count() != blocksAvailable_ )
|
||||
{
|
||||
/* This implies that the singly-linked-list of stealth indexes was
|
||||
corrupted. Ideally, this should have been detected within the loop.
|
||||
*/
|
||||
assert( false );
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// Chunk::IsBlockAvailable ----------------------------------------------------
|
||||
|
||||
bool Chunk::IsBlockAvailable( void * p, unsigned char numBlocks,
|
||||
std::size_t blockSize ) const
|
||||
{
|
||||
|
||||
if ( IsFilled() )
|
||||
return false;
|
||||
|
||||
#else
|
||||
(void)blockSize; // cast as void to make warning go away.
|
||||
#endif
|
||||
unsigned char * place = static_cast< unsigned char * >( p );
|
||||
// Alignment check
|
||||
assert( ( place - pData_ ) % blockSize == 0 );
|
||||
unsigned char blockIndex = static_cast< unsigned char >(
|
||||
( place - pData_ ) / blockSize );
|
||||
|
||||
unsigned char index = firstAvailableBlock_;
|
||||
assert( numBlocks > index );
|
||||
if ( index == blockIndex )
|
||||
return true;
|
||||
|
||||
/* If the bit at index was set in foundBlocks, then the stealth index was
|
||||
found on the linked-list.
|
||||
*/
|
||||
std::bitset< UCHAR_MAX > foundBlocks;
|
||||
unsigned char * nextBlock = NULL;
|
||||
for ( unsigned char cc = 0; ; )
|
||||
{
|
||||
nextBlock = pData_ + ( index * blockSize );
|
||||
foundBlocks.set( index, true );
|
||||
++cc;
|
||||
if ( cc >= blocksAvailable_ )
|
||||
// Successfully counted off number of nodes in linked-list.
|
||||
break;
|
||||
index = *nextBlock;
|
||||
if ( index == blockIndex )
|
||||
return true;
|
||||
assert( numBlocks > index );
|
||||
assert( !foundBlocks.test( index ) );
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
@ -444,16 +580,130 @@ std::size_t FixedAllocator::CountEmptyChunks( void ) const
|
|||
#endif
|
||||
}
|
||||
|
||||
// FixedAllocator::IsCorrupt --------------------------------------------------
|
||||
|
||||
bool FixedAllocator::IsCorrupt( void ) const
|
||||
{
|
||||
const bool isEmpty = chunks_.empty();
|
||||
ChunkCIter start( chunks_.begin() );
|
||||
ChunkCIter last( chunks_.end() );
|
||||
const size_t emptyChunkCount = CountEmptyChunks();
|
||||
|
||||
if ( isEmpty )
|
||||
{
|
||||
if ( start != last )
|
||||
{
|
||||
assert( false );
|
||||
return true;
|
||||
}
|
||||
if ( 0 < emptyChunkCount )
|
||||
{
|
||||
assert( false );
|
||||
return true;
|
||||
}
|
||||
if ( NULL != deallocChunk_ )
|
||||
{
|
||||
assert( false );
|
||||
return true;
|
||||
}
|
||||
if ( NULL != allocChunk_ )
|
||||
{
|
||||
assert( false );
|
||||
return true;
|
||||
}
|
||||
if ( NULL != emptyChunk_ )
|
||||
{
|
||||
assert( false );
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
else
|
||||
{
|
||||
const Chunk * front = &chunks_.front();
|
||||
const Chunk * back = &chunks_.back();
|
||||
if ( start >= last )
|
||||
{
|
||||
assert( false );
|
||||
return true;
|
||||
}
|
||||
if ( back < deallocChunk_ )
|
||||
{
|
||||
assert( false );
|
||||
return true;
|
||||
}
|
||||
if ( back < allocChunk_ )
|
||||
{
|
||||
assert( false );
|
||||
return true;
|
||||
}
|
||||
if ( front > deallocChunk_ )
|
||||
{
|
||||
assert( false );
|
||||
return true;
|
||||
}
|
||||
if ( front > allocChunk_ )
|
||||
{
|
||||
assert( false );
|
||||
return true;
|
||||
}
|
||||
|
||||
switch ( emptyChunkCount )
|
||||
{
|
||||
case 0:
|
||||
if ( emptyChunk_ != NULL )
|
||||
{
|
||||
assert( false );
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
case 1:
|
||||
if ( emptyChunk_ == NULL )
|
||||
{
|
||||
assert( false );
|
||||
return true;
|
||||
}
|
||||
if ( back < emptyChunk_ )
|
||||
{
|
||||
assert( false );
|
||||
return true;
|
||||
}
|
||||
if ( front > emptyChunk_ )
|
||||
{
|
||||
assert( false );
|
||||
return true;
|
||||
}
|
||||
if ( !emptyChunk_->HasAvailable( numBlocks_ ) )
|
||||
{
|
||||
// This may imply somebody tried to delete a block twice.
|
||||
assert( false );
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
assert( false );
|
||||
return true;
|
||||
}
|
||||
for ( ChunkCIter it( start ); it != last; ++it )
|
||||
{
|
||||
const Chunk & chunk = *it;
|
||||
if ( chunk.IsCorrupt( numBlocks_, blockSize_, true ) )
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// FixedAllocator::HasBlock ---------------------------------------------------
|
||||
|
||||
const Chunk * 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 ) )
|
||||
if ( chunk.HasBlock( p, chunkLength ) )
|
||||
return &chunk;
|
||||
}
|
||||
return NULL;
|
||||
|
@ -601,9 +851,19 @@ bool FixedAllocator::Deallocate( void * p, Chunk * hint )
|
|||
if ( NULL == foundChunk )
|
||||
return false;
|
||||
|
||||
assert( foundChunk->HasBlock( static_cast< unsigned char * >( p ),
|
||||
numBlocks_ * blockSize_ ) );
|
||||
assert( !foundChunk->IsCorrupt( numBlocks_, blockSize_ ) );
|
||||
assert( foundChunk->HasBlock( p, numBlocks_ * blockSize_ ) );
|
||||
#ifdef LOKI_CHECK_FOR_CORRUPTION
|
||||
if ( foundChunk->IsCorrupt( numBlocks_, blockSize_, true ) )
|
||||
{
|
||||
assert( false );
|
||||
return false;
|
||||
}
|
||||
if ( foundChunk->IsBlockAvailable( p, numBlocks_, blockSize_ ) )
|
||||
{
|
||||
assert( false );
|
||||
return false;
|
||||
}
|
||||
#endif
|
||||
deallocChunk_ = foundChunk;
|
||||
DoDeallocate(p);
|
||||
assert( CountEmptyChunks() < 2 );
|
||||
|
@ -618,9 +878,7 @@ Chunk * FixedAllocator::VicinityFind( void * p ) const
|
|||
if ( chunks_.empty() ) return NULL;
|
||||
assert(deallocChunk_);
|
||||
|
||||
unsigned char * pc = static_cast< unsigned char * >( p );
|
||||
const std::size_t chunkLength = numBlocks_ * blockSize_;
|
||||
|
||||
Chunk * lo = deallocChunk_;
|
||||
Chunk * hi = deallocChunk_ + 1;
|
||||
const Chunk * loBound = &chunks_.front();
|
||||
|
@ -633,7 +891,7 @@ Chunk * FixedAllocator::VicinityFind( void * p ) const
|
|||
{
|
||||
if (lo)
|
||||
{
|
||||
if ( lo->HasBlock( pc, chunkLength ) ) return lo;
|
||||
if ( lo->HasBlock( p, chunkLength ) ) return lo;
|
||||
if ( lo == loBound )
|
||||
{
|
||||
lo = NULL;
|
||||
|
@ -644,7 +902,7 @@ Chunk * FixedAllocator::VicinityFind( void * p ) const
|
|||
|
||||
if (hi)
|
||||
{
|
||||
if ( hi->HasBlock( pc, chunkLength ) ) return hi;
|
||||
if ( hi->HasBlock( p, chunkLength ) ) return hi;
|
||||
if ( ++hi == hiBound )
|
||||
{
|
||||
hi = NULL;
|
||||
|
@ -660,8 +918,12 @@ Chunk * FixedAllocator::VicinityFind( void * p ) const
|
|||
|
||||
void FixedAllocator::DoDeallocate(void* p)
|
||||
{
|
||||
assert( deallocChunk_->HasBlock( static_cast< unsigned char * >( p ),
|
||||
numBlocks_ * blockSize_ ) );
|
||||
// Show that deallocChunk_ really owns the block at address p.
|
||||
assert( deallocChunk_->HasBlock( p, numBlocks_ * blockSize_ ) );
|
||||
// Either of the next two assertions may fail if somebody tries to
|
||||
// delete the same block twice.
|
||||
assert( emptyChunk_ != deallocChunk_ );
|
||||
assert( !deallocChunk_->HasAvailable( numBlocks_ ) );
|
||||
// prove either emptyChunk_ points nowhere, or points to a truly empty Chunk.
|
||||
assert( ( NULL == emptyChunk_ ) || ( emptyChunk_->HasAvailable( numBlocks_ ) ) );
|
||||
|
||||
|
@ -874,6 +1136,34 @@ void SmallObjAllocator::Deallocate( void * p )
|
|||
assert( found );
|
||||
}
|
||||
|
||||
// SmallObjAllocator::IsCorrupt -----------------------------------------------
|
||||
|
||||
bool SmallObjAllocator::IsCorrupt( void ) const
|
||||
{
|
||||
if ( NULL == pool_ )
|
||||
{
|
||||
assert( false );
|
||||
return true;
|
||||
}
|
||||
if ( 0 == GetAlignment() )
|
||||
{
|
||||
assert( false );
|
||||
return true;
|
||||
}
|
||||
if ( 0 == GetMaxObjectSize() )
|
||||
{
|
||||
assert( false );
|
||||
return true;
|
||||
}
|
||||
const std::size_t allocCount = GetOffset( GetMaxObjectSize(), GetAlignment() );
|
||||
for ( std::size_t ii = 0; ii < allocCount; ++ii )
|
||||
{
|
||||
if ( pool_[ ii ].IsCorrupt() )
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
} // end namespace Loki
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
@ -887,6 +1177,10 @@ void SmallObjAllocator::Deallocate( void * p )
|
|||
////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
// $Log$
|
||||
// Revision 1.18 2005/12/08 22:08:20 rich_sposato
|
||||
// Added functions to check for corrupted Chunks and FixedAllocators. Made
|
||||
// several minor coding changes.
|
||||
//
|
||||
// Revision 1.17 2005/11/03 12:43:55 syntheticpp
|
||||
// more doxygen documentation, modules added
|
||||
//
|
||||
|
|
Loading…
Reference in a new issue