Protium
Math and Design Features
 All Classes Namespaces Files Functions Variables Typedefs Enumerator Friends Macros Pages
FixedAllocator.cxx
Go to the documentation of this file.
2 
3 #include <assert.h>
4 
7 
8 // FixedAllocator::FixedAllocator ---------------------------------------------
9 
11  : blockSize_( 0 )
12  , numBlocks_( 0 )
13  , chunks_( 0 )
14  , allocChunk_( NULL )
15  , deallocChunk_( NULL )
16  , emptyChunk_( NULL )
17 {
18 }
19 
20 // FixedAllocator::~FixedAllocator --------------------------------------------
21 
23 {
24 #ifdef DO_EXTRA_LOKI_TESTS
25  TrimEmptyChunk();
26  assert( chunks_.empty() && "Memory leak detected!" );
27 #endif
28  for ( ChunkIter i( chunks_.begin() ); i != chunks_.end(); ++i )
29  i->Release();
30 }
31 
32 // FixedAllocator::Initialize -------------------------------------------------
33 
34 void Protium::Allocation::FixedAllocator::Initialize( std::size_t blockSize, std::size_t pageSize )
35 {
36  assert( blockSize > 0 );
37  assert( pageSize >= blockSize );
38  blockSize_ = blockSize;
39 
40  std::size_t numBlocks = pageSize / blockSize;
41  if ( numBlocks > MaxObjectsPerChunk_ ) numBlocks = MaxObjectsPerChunk_;
42  else if ( numBlocks < MinObjectsPerChunk_ ) numBlocks = MinObjectsPerChunk_;
43 
44  numBlocks_ = static_cast<unsigned char>(numBlocks);
45  assert(numBlocks_ == numBlocks);
46 }
47 
48 // FixedAllocator::CountEmptyChunks -------------------------------------------
49 
51 #ifdef DO_EXTRA_LOKI_TESTS
52  // This code is only used for specialized tests of the allocator.
53  // It is #ifdef-ed so that its O(C) complexity does not overwhelm the
54  // functions which call it.
55  std::size_t count = 0;
56  for ( ChunkCIter it( chunks_.begin() ); it != chunks_.end(); ++it )
57  {
58  const Chunk & chunk = *it;
59  if ( chunk.HasAvailable( numBlocks_ ) )
60  ++count;
61  }
62  return count;
63 #else
64  return ( NULL == emptyChunk_ ) ? 0 : 1;
65 #endif
66 }
67 
68 // FixedAllocator::IsCorrupt --------------------------------------------------
69 
71  const bool isEmpty = chunks_.empty();
72  ChunkCIter start( chunks_.begin() );
73  ChunkCIter last( chunks_.end() );
74  const size_t emptyChunkCount = CountEmptyChunks();
75 
76  if ( isEmpty )
77  {
78  if ( start != last )
79  {
80  assert( false );
81  return true;
82  }
83  if ( 0 < emptyChunkCount )
84  {
85  assert( false );
86  return true;
87  }
88  if ( NULL != deallocChunk_ )
89  {
90  assert( false );
91  return true;
92  }
93  if ( NULL != allocChunk_ )
94  {
95  assert( false );
96  return true;
97  }
98  if ( NULL != emptyChunk_ )
99  {
100  assert( false );
101  return true;
102  }
103  }
104 
105  else
106  {
107  const Chunk * front = &chunks_.front();
108  const Chunk * back = &chunks_.back();
109  if ( start >= last )
110  {
111  assert( false );
112  return true;
113  }
114  if ( back < deallocChunk_ )
115  {
116  assert( false );
117  return true;
118  }
119  if ( back < allocChunk_ )
120  {
121  assert( false );
122  return true;
123  }
124  if ( front > deallocChunk_ )
125  {
126  assert( false );
127  return true;
128  }
129  if ( front > allocChunk_ )
130  {
131  assert( false );
132  return true;
133  }
134 
135  switch ( emptyChunkCount )
136  {
137  case 0:
138  if ( emptyChunk_ != NULL )
139  {
140  assert( false );
141  return true;
142  }
143  break;
144  case 1:
145  if ( emptyChunk_ == NULL )
146  {
147  assert( false );
148  return true;
149  }
150  if ( back < emptyChunk_ )
151  {
152  assert( false );
153  return true;
154  }
155  if ( front > emptyChunk_ )
156  {
157  assert( false );
158  return true;
159  }
160  if ( !emptyChunk_->HasAvailable( numBlocks_ ) )
161  {
162  // This may imply somebody tried to delete a block twice.
163  assert( false );
164  return true;
165  }
166  break;
167  default:
168  assert( false );
169  return true;
170  }
171  for ( ChunkCIter it( start ); it != last; ++it )
172  {
173  const Chunk & chunk = *it;
174  if ( chunk.IsCorrupt( numBlocks_, blockSize_, true ) )
175  return true;
176  }
177  }
178 
179  return false;
180 }
181 
182 // FixedAllocator::HasBlock ---------------------------------------------------
183 
185  const std::size_t chunkLength = numBlocks_ * blockSize_;
186  for ( ChunkCIter it( chunks_.begin() ); it != chunks_.end(); ++it )
187  {
188  const Chunk & chunk = *it;
189  if ( chunk.HasBlock( p, chunkLength ) )
190  return &chunk;
191  }
192  return NULL;
193 }
194 
195 // FixedAllocator::TrimEmptyChunk ---------------------------------------------
196 
198 {
199  // prove either emptyChunk_ points nowhere, or points to a truly empty Chunk.
200  assert( ( NULL == emptyChunk_ ) || ( emptyChunk_->HasAvailable( numBlocks_ ) ) );
201  if ( NULL == emptyChunk_ ) return false;
202 
203  // If emptyChunk_ points to valid Chunk, then chunk list is not empty.
204  assert( !chunks_.empty() );
205  // And there should be exactly 1 empty Chunk.
206  assert( 1 == CountEmptyChunks() );
207 
208  Chunk * lastChunk = &chunks_.back();
209  if ( lastChunk != emptyChunk_ )
210  std::swap( *emptyChunk_, *lastChunk );
211  assert( lastChunk->HasAvailable( numBlocks_ ) );
212  lastChunk->Release();
213  chunks_.pop_back();
214 
215  if ( chunks_.empty() )
216  {
217  allocChunk_ = NULL;
218  deallocChunk_ = NULL;
219  }
220  else
221  {
222  if ( deallocChunk_ == emptyChunk_ )
223  {
224  deallocChunk_ = &chunks_.front();
225  assert( deallocChunk_->fNAvail < numBlocks_ );
226  }
227  if ( allocChunk_ == emptyChunk_ )
228  {
229  allocChunk_ = &chunks_.back();
230  assert( allocChunk_->fNAvail < numBlocks_ );
231  }
232  }
233 
234  emptyChunk_ = NULL;
235  assert( 0 == CountEmptyChunks() );
236 
237  return true;
238 }
239 
240 // FixedAllocator::TrimChunkList ----------------------------------------------
241 
243 {
244  if ( chunks_.empty() )
245  {
246  assert( NULL == allocChunk_ );
247  assert( NULL == deallocChunk_ );
248  }
249 
250  if ( chunks_.size() == chunks_.capacity() )
251  return false;
252  // Use the "make-a-temp-and-swap" trick to remove excess capacity.
253  Chunks( chunks_ ).swap( chunks_ );
254 
255  return true;
256 }
257 
258 // FixedAllocator::MakeNewChunk -----------------------------------------------
259 
261 {
262  bool allocated = false;
263  try
264  {
265  std::size_t size = chunks_.size();
266  // Calling chunks_.reserve *before* creating and initializing the new
267  // Chunk means that nothing is leaked by this function in case an
268  // exception is thrown from reserve.
269  if ( chunks_.capacity() == size )
270  {
271  if ( 0 == size ) size = 4;
272  chunks_.reserve( size * 2 );
273  }
274  Chunk newChunk;
275  allocated = newChunk.Init( blockSize_, numBlocks_ );
276  if ( allocated )
277  chunks_.push_back( newChunk );
278  }
279  catch ( ... )
280  {
281  allocated = false;
282  }
283  if ( !allocated ) return false;
284 
285  allocChunk_ = &chunks_.back();
286  deallocChunk_ = &chunks_.front();
287  return true;
288 }
289 
290 // FixedAllocator::Allocate ---------------------------------------------------
291 
293 {
294  // prove either emptyChunk_ points nowhere, or points to a truly empty Chunk.
295  assert( ( NULL == emptyChunk_ ) || ( emptyChunk_->HasAvailable( numBlocks_ ) ) );
296  assert( CountEmptyChunks() < 2 );
297 
298  if ( ( NULL == allocChunk_ ) || allocChunk_->IsFilled() )
299  {
300  if ( NULL != emptyChunk_ )
301  {
302  allocChunk_ = emptyChunk_;
303  emptyChunk_ = NULL;
304  }
305  else
306  {
307  for ( ChunkIter i( chunks_.begin() ); ; ++i )
308  {
309  if ( chunks_.end() == i )
310  {
311  if ( !MakeNewChunk() )
312  return NULL;
313  break;
314  }
315  if ( !i->IsFilled() )
316  {
317  allocChunk_ = &*i;
318  break;
319  }
320  }
321  }
322  }
323  else if ( allocChunk_ == emptyChunk_)
324  // detach emptyChunk_ from allocChunk_, because after
325  // calling allocChunk_->Allocate(blockSize_); the chunk
326  // is no longer empty.
327  emptyChunk_ = NULL;
328 
329  assert( allocChunk_ != NULL );
330  assert( !allocChunk_->IsFilled() );
331  void * place = allocChunk_->Allocate( blockSize_ );
332 
333  // prove either emptyChunk_ points nowhere, or points to a truly empty Chunk.
334  assert( ( NULL == emptyChunk_ ) || ( emptyChunk_->HasAvailable( numBlocks_ ) ) );
335  assert( CountEmptyChunks() < 2 );
336 #ifdef LOKI_CHECK_FOR_CORRUPTION
337  if ( allocChunk_->IsCorrupt( numBlocks_, blockSize_, true ) )
338  {
339  assert( false );
340  return NULL;
341  }
342 #endif
343 
344  return place;
345 }
346 
347 // FixedAllocator::Deallocate -------------------------------------------------
348 
350 {
351  assert(!chunks_.empty());
352  assert(&chunks_.front() <= deallocChunk_);
353  assert(&chunks_.back() >= deallocChunk_);
354  assert( &chunks_.front() <= allocChunk_ );
355  assert( &chunks_.back() >= allocChunk_ );
356  assert( CountEmptyChunks() < 2 );
357 
358  Chunk * foundChunk = ( NULL == hint ) ? VicinityFind( p ) : hint;
359  if ( NULL == foundChunk )
360  return false;
361 
362  assert( foundChunk->HasBlock( p, numBlocks_ * blockSize_ ) );
363 #ifdef LOKI_CHECK_FOR_CORRUPTION
364  if ( foundChunk->IsCorrupt( numBlocks_, blockSize_, true ) )
365  {
366  assert( false );
367  return false;
368  }
369  if ( foundChunk->IsBlockAvailable( p, numBlocks_, blockSize_ ) )
370  {
371  assert( false );
372  return false;
373  }
374 #endif
375  deallocChunk_ = foundChunk;
376  DoDeallocate(p);
377  assert( CountEmptyChunks() < 2 );
378 
379  return true;
380 }
381 
382 // FixedAllocator::VicinityFind -----------------------------------------------
383 
385  if ( chunks_.empty() ) return NULL;
386  assert(deallocChunk_);
387 
388  const std::size_t chunkLength = numBlocks_ * blockSize_;
389  Chunk * lo = deallocChunk_;
390  Chunk * hi = deallocChunk_ + 1;
391  const Chunk * loBound = &chunks_.front();
392  const Chunk * hiBound = &chunks_.back() + 1;
393 
394  // Special case: deallocChunk_ is the last in the array
395  if (hi == hiBound) hi = NULL;
396 
397  for (;;)
398  {
399  if (lo)
400  {
401  if ( lo->HasBlock( p, chunkLength ) ) return lo;
402  if ( lo == loBound )
403  {
404  lo = NULL;
405  if ( NULL == hi ) break;
406  }
407  else --lo;
408  }
409 
410  if (hi)
411  {
412  if ( hi->HasBlock( p, chunkLength ) ) return hi;
413  if ( ++hi == hiBound )
414  {
415  hi = NULL;
416  if ( NULL == lo ) break;
417  }
418  }
419  }
420 
421  return NULL;
422 }
423 
424 // FixedAllocator::DoDeallocate -----------------------------------------------
425 
427 {
428  // Show that deallocChunk_ really owns the block at address p.
429  assert( deallocChunk_->HasBlock( p, numBlocks_ * blockSize_ ) );
430  // Either of the next two assertions may fail if somebody tries to
431  // delete the same block twice.
432  assert( emptyChunk_ != deallocChunk_ );
433  assert( !deallocChunk_->HasAvailable( numBlocks_ ) );
434  // prove either emptyChunk_ points nowhere, or points to a truly empty Chunk.
435  assert( ( NULL == emptyChunk_ ) || ( emptyChunk_->HasAvailable( numBlocks_ ) ) );
436 
437  // call into the chunk, will adjust the inner list but won't release memory
438  deallocChunk_->Deallocate(p, blockSize_);
439 
440  if ( deallocChunk_->HasAvailable( numBlocks_ ) )
441  {
442  assert( emptyChunk_ != deallocChunk_ );
443  // deallocChunk_ is empty, but a Chunk is only released if there are 2
444  // empty chunks. Since emptyChunk_ may only point to a previously
445  // cleared Chunk, if it points to something else besides deallocChunk_,
446  // then FixedAllocator currently has 2 empty Chunks.
447  if ( NULL != emptyChunk_ )
448  {
449  // If last Chunk is empty, just change what deallocChunk_
450  // points to, and release the last. Otherwise, swap an empty
451  // Chunk with the last, and then release it.
452  Chunk * lastChunk = &chunks_.back();
453  if ( lastChunk == deallocChunk_ )
454  deallocChunk_ = emptyChunk_;
455  else if ( lastChunk != emptyChunk_ )
456  std::swap( *emptyChunk_, *lastChunk );
457  assert( lastChunk->HasAvailable( numBlocks_ ) );
458  lastChunk->Release();
459  chunks_.pop_back();
460  if ( ( allocChunk_ == lastChunk ) || allocChunk_->IsFilled() )
461  allocChunk_ = deallocChunk_;
462  }
463  emptyChunk_ = deallocChunk_;
464  }
465 
466  // prove either emptyChunk_ points nowhere, or points to a truly empty Chunk.
467  assert( ( NULL == emptyChunk_ ) || ( emptyChunk_->HasAvailable( numBlocks_ ) ) );
468 }
FixedAllocator()
Create a FixedAllocator which manages blocks of 'blockSize' size.
Chunks::iterator ChunkIter
Iterator through container of Chunks.
static unsigned char MaxObjectsPerChunk_
Most # of objects managed by a Chunk - never exceeds UCHAR_MAX.
bool IsCorrupt(unsigned char numBlocks, std::size_t blockSize, bool checkIndexes) const
Definition: Chunk.cxx:61
Chunk * VicinityFind(void *p) const
bool HasBlock(void *p, std::size_t chunkLength) const
Checks if this block belongs to this chunk.
Definition: Chunk.h:54
bool HasAvailable(unsigned char numBlocks) const
Checks to see if this number of blocks is available in this chunk.
Definition: Chunk.h:59
void Initialize(std::size_t blockSize, std::size_t pageSize)
Initializes a FixedAllocator by calculating # of blocks per Chunk.
bool Deallocate(void *p, Chunk *hint)
const Chunk * HasBlock(void *p) const
Represents a fixed number of blocks.
Definition: Chunk.h:16
bool Init(std::size_t size, unsigned char n)
Definition: Chunk.cxx:6
void Release()
Deallocates all blocks in chunck.
Definition: Chunk.cxx:24
static unsigned char MinObjectsPerChunk_
Fewest # of objects managed by a Chunk.
bool IsBlockAvailable(void *p, unsigned char numBlocks, std::size_t blockSize) const
Checks the block starting at p and for p+numBlocks*blockSize checks to see if they're used...
Definition: Chunk.cxx:160
std::size_t CountEmptyChunks(void) const
std::vector< Chunk > Chunks
Type of container used to hold Chunks.
~FixedAllocator()
Destroy the FixedAllocator and release all its Chunks.
Chunks::const_iterator ChunkCIter
Iterator through const container of Chunks.