Eneboo - Documentación para desarrolladores
|
00001 /* -*- C++ -*- */ 00002 00003 #ifndef _ALIGNEDCHUNK_H_ 00004 #define _ALIGNEDCHUNK_H_ 00005 00006 /* 00007 00008 Heap Layers: An Extensible Memory Allocation Infrastructure 00009 00010 Copyright (C) 2000-2003 by Emery Berger 00011 http://www.cs.umass.edu/~emery 00012 emery@cs.umass.edu 00013 00014 This program is free software; you can redistribute it and/or modify 00015 it under the terms of the GNU General Public License as published by 00016 the Free Software Foundation; either version 2 of the License, or 00017 (at your option) any later version. 00018 00019 This program is distributed in the hope that it will be useful, 00020 but WITHOUT ANY WARRANTY; without even the implied warranty of 00021 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 00022 GNU General Public License for more details. 00023 00024 You should have received a copy of the GNU General Public License 00025 along with this program; if not, write to the Free Software 00026 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA 00027 00028 */ 00029 00030 #include <stdlib.h> 00031 #include <malloc.h> 00032 #include <assert.h> 00033 00034 #include "bitindex.h" 00035 00036 /* 00037 An aligned chunk is a chunk of memory 00038 containing a number of fixed-size "slots". 00039 As long as the chunk is naturally aligned, 00040 each slot will also be naturally aligned. 00041 00042 This alignment allows us to use address masking to find 00043 which chunk a given pointer belongs to. 00044 00045 All information about the chunk is stored at the end 00046 of the chunk. 00047 00048 */ 00049 00050 // AlignHeap aligns every memory request to chunkSize. 00051 // 00052 // If you know that memory allocations are always aligned to chunkSize 00053 // from your allocator of choice, don't use AlignHeap because it 00054 // will waste memory. 00055 00056 namespace HL { 00057 00058 template <int chunkSize, class Super> 00059 class AlignHeap { 00060 public: 00061 inline void * malloc (size_t sz) { 00062 // Get a piece of memory large enough to be able to guarantee alignment. 00063 void * buf = ::malloc (sz + chunkSize); 00064 // Align the buffer. 00065 void * alignedBuf = (void *) (((unsigned long) buf + sizeof(unsigned long) + chunkSize - 1) & -chunkSize); 00066 // Record the original buffer's address right behind the aligned part. 00067 assert ((unsigned long) alignedBuf - (unsigned long) buf > sizeof(unsigned long)); 00068 *((unsigned long *) alignedBuf - 1) = (unsigned long) buf; 00069 return alignedBuf; 00070 } 00071 00072 void free (void * ptr) { 00073 // Get the original buffer's address and free that. 00074 ::free ((void *) *((unsigned long *) ptr - 1)); 00075 } 00076 }; 00077 00078 00079 // An aligned chunk is of size chunkSize and is divided up into 32 pieces 00080 // of size slotSize. The 32nd one will not be available for allocation 00081 // because it is used for 'header' information (albeit stored in the footer). 00082 00083 // Some restrictions: chunkSize MUST BE A POWER OF TWO. 00084 // slotSize MUST BE AT LEAST THE SIZE OF A DOUBLE. 00085 00086 // The amount of memory that this approach wastes is small in practice: 00087 // For one aligned chunk, utilization is 31/32 = 97%. 00088 // For two nested chunks, utilization is (31/32)^2 = 94%. 00089 // For three nested chunks, utilization is (31/32)^3 = 91%. 00090 // Note that the smallest possible size of a three-deep aligned chunk 00091 // is 32 * 32 * 32 * 32 = one megabyte. 00092 00093 template <int chunkSize, int slotSize> 00094 class AlignedChunk { 00095 public: 00096 00097 AlignedChunk (void) 00098 : prev (NULL), 00099 next (NULL), 00100 status (ACQUIRED), 00101 inUse (0) 00102 { 00103 // Make sure there's enough slop to let us store the chunk information! 00104 assert (getNumSlots() * slotSize + sizeof(AlignedChunk) <= chunkSize); 00105 // The slot size must be at least large enough to hold a double. 00106 assert (slotSize == align(slotSize)); 00107 // Initialize the bitmap. 00108 freeBitmap = 0; 00109 // Block the last slot. 00110 BitIndex::set (freeBitmap, 0); 00111 emptyBitmap = freeBitmap; 00112 } 00113 00114 ~AlignedChunk (void) 00115 {} 00116 00117 00118 // Get and put slots. 00119 // These are ATOMIC and lock-free. 00120 00121 // Get returns NULL iff there are no slots available. 00122 void * getSlot (void) { 00123 RETRY_UNSET: 00124 unsigned long currBitmap = freeBitmap; 00125 // If currBitmap is all ones, everything is in use. 00126 // Just return NULL. 00127 if (currBitmap == (unsigned long) -1) { 00128 assert (inUse == getNumSlots()); 00129 return NULL; 00130 } 00131 // Find an unset bit. 00132 // We flip the bits in currBitmap and find the index of a one bit 00133 // (which corresponds to the index of a zero in currBitmap). 00134 int bitIndex; 00135 unsigned long oneBit = (~currBitmap) & (-((signed long) ~currBitmap)); 00136 assert (oneBit != 0); 00137 bitIndex = BitIndex::msb (oneBit); 00138 if (bitIndex > getNumSlots()) { 00139 assert (inUse == getNumSlots()); 00140 return NULL; 00141 } 00142 assert (inUse < getNumSlots()); 00143 assert (bitIndex < getNumSlots() + 1); 00144 // Set it. 00145 unsigned long oldBitmap = currBitmap; 00146 BitIndex::set (currBitmap, bitIndex); 00147 unsigned long updatedBitmap = InterlockedExchange ((long *) &freeBitmap, currBitmap); 00148 if (updatedBitmap == oldBitmap) { 00149 // Success. 00150 // Return a pointer to the appropriate slot. 00151 char * start = (char *) ((unsigned long) this & -chunkSize); 00152 inUse++; 00153 return start + slotSize * (getNumSlots() - bitIndex); 00154 } 00155 // Someone changed the bitmap before we were able to write it. 00156 // Try again. 00157 goto RETRY_UNSET; 00158 } 00159 00160 00161 // Put returns 1 iff the chunk is now completely empty. 00162 inline int putSlot (void * ptr) { 00163 assert (inUse >= 1); 00164 AlignedChunk * ch = getChunk (ptr); 00165 assert (ch == this); 00166 // Find the index of this pointer. 00167 // Since slotSize is known at compile time and is usually a power of two, 00168 // the divide should be turned into shifts and will be fast. 00169 char * start = (char *) ((unsigned long) ptr & -chunkSize); 00170 int bitIndex = getNumSlots() - (((unsigned long) ((char *) ptr - start)) / slotSize); 00171 RETRY_RESET: 00172 unsigned long currBitmap = freeBitmap; 00173 unsigned long oldBitmap = currBitmap; 00174 BitIndex::reset (currBitmap, bitIndex); 00175 unsigned long updatedBitmap = InterlockedExchange ((long *) &freeBitmap, currBitmap); 00176 if (updatedBitmap == oldBitmap) { 00177 // Success. 00178 inUse--; 00179 assert ((inUse != 0) || (currBitmap == emptyBitmap)); 00180 assert ((inUse == 0) || (currBitmap != emptyBitmap)); 00181 // Return 1 iff the chunk is now empty. 00182 if (currBitmap == emptyBitmap) { 00183 assert (inUse == 0); 00184 return 1; 00185 } else { 00186 assert (inUse > 0); 00187 return 0; 00188 } 00189 } 00190 // Try again. 00191 goto RETRY_RESET; 00192 } 00193 00194 00195 // How many slots are there in total? 00196 inline static int getNumSlots (void) { 00197 return 31; 00198 } 00199 00200 inline int isReleased (void) { 00201 return (status == RELEASED); 00202 } 00203 00204 inline void acquire (void) { 00205 assert (status == RELEASED); 00206 status = ACQUIRED; 00207 } 00208 00209 inline void release (void) { 00210 assert (status == ACQUIRED); 00211 status = RELEASED; 00212 } 00213 00214 // Find a chunk for a given slot. 00215 inline static AlignedChunk * getChunk (void * slot) { 00216 // Here's where the alignment is CRITICAL!! 00217 // Verify that chunkSize is a power of two. 00218 assert ((chunkSize & (chunkSize - 1)) == 0); 00219 // Mask off the slot to find the start of the chunkSize block. 00220 char * start = (char *) ((unsigned long) slot & -chunkSize); 00221 // Find the end of the block. 00222 char * eob = (start + chunkSize); 00223 // Now locate the 'header' (in this case, it's actually a footer). 00224 char * headerPos = eob - sizeof(AlignedChunk); 00225 return (AlignedChunk *) headerPos; 00226 } 00227 00228 00229 // Add doubly linked-list operations. 00230 AlignedChunk * getNext (void) { return next; } 00231 AlignedChunk * getPrev (void) { return prev; } 00232 void setNext (AlignedChunk * p) { next = p; } 00233 void setPrev (AlignedChunk * p) { prev = p; } 00234 00235 private: 00236 00237 enum { RELEASED = 0, ACQUIRED = 1 }; 00238 00239 static inline size_t align (size_t sz) { 00240 return (sz + (sizeof(double) - 1)) & ~(sizeof(double) - 1); 00241 } 00242 00243 int inUse; // For debugging only. 00244 unsigned long freeBitmap; 00245 unsigned long emptyBitmap; 00246 int status; 00247 AlignedChunk * prev; 00248 AlignedChunk * next; 00249 }; 00250 00251 00252 // AlignedChunkHeap manages a number of chunks. 00253 00254 template <int maxFree, int chunkSize, int slotSize, class Super> 00255 class AlignedChunkHeap : public Super { 00256 public: 00257 00258 AlignedChunkHeap (void) 00259 : chunkList (NULL), 00260 length (0) 00261 {} 00262 00263 virtual ~AlignedChunkHeap (void) 00264 { 00265 chunkType * ch, * tmp; 00266 ch = chunkList; 00267 while (ch != NULL) { 00268 tmp = ch; 00269 ch = ch->getNext(); 00270 assert (tmp->isReleased()); 00271 Super::free ((char *) ((unsigned long) tmp & -chunkSize)); 00272 } 00273 } 00274 00275 // Malloc a CHUNK. Returns a pointer to the start of the allocated block. 00276 inline void * malloc (size_t sz) 00277 { 00278 assert (sz == chunkSize); 00279 chunkType * ch; 00280 // Get a chunk from our chunk list 00281 // or make one. 00282 if (chunkList != NULL) { 00283 ch = chunkList; 00284 chunkList = chunkList->getNext(); 00285 length--; 00286 ch->acquire(); 00287 } else { 00288 // Make a buffer large enough to hold the chunk. 00289 void * buf = Super::malloc (chunkSize); 00290 // The buffer MUST BE ALIGNED. 00291 assert ((unsigned long) buf == ((unsigned long) buf & -chunkSize)); 00292 // Instantiate the chunk "header" (actually the footer) 00293 // at the end of the chunk. 00294 ch = new (chunkType::getChunk (buf)) chunkType; 00295 } 00296 // Now return the start of the chunk's buffer. 00297 assert (!ch->isReleased()); 00298 return (void *) ((unsigned long) ch & -chunkSize); 00299 } 00300 00301 // Free a CHUNK. 00302 // The pointer is to the chunk header. 00303 inline void free (void * ptr) 00304 { 00305 chunkType * ch = (chunkType *) AlignedChunk<chunkSize, slotSize>::getChunk(ptr); 00306 assert (ch->isReleased()); 00307 if (length > maxFree) { 00308 Super::free ((void *) ((unsigned long) ch & -chunkSize)); 00309 } else { 00310 ch->setNext (chunkList); 00311 chunkList = ch; 00312 length++; 00313 } 00314 } 00315 00316 size_t size (void * ptr) { 00317 return slotSize; 00318 } 00319 00320 private: 00321 00322 typedef AlignedChunk<chunkSize, slotSize> chunkType; 00323 00324 chunkType * chunkList; 00325 int length; 00326 }; 00327 00328 00329 // An AlignedSlotHeap holds at most one chunk. 00330 // When all of the slots are allocated from the chunk, 00331 // the chunk is "released" so that it may be freed back 00332 // to the super heap when all of its slots are freed. 00333 00334 template <int chunkSize, int slotSize, class Super> 00335 class AlignedSlotHeap : public Super { 00336 public: 00337 00338 AlignedSlotHeap (void) 00339 : myChunk (NULL) 00340 {} 00341 00342 virtual ~AlignedSlotHeap (void) { 00343 // Note that this is NOT enough to completely clean up after ourselves!! 00344 if (myChunk != NULL) { 00345 myChunk->release(); 00346 Super::free ((void *) ((unsigned long) myChunk & -chunkSize)); 00347 } 00348 } 00349 00350 // Malloc a SLOT. 00351 // Use up a chunk, if we've got one. 00352 // Once it's used up, 'release it' and get another one. 00353 inline void * malloc (size_t sz) { 00354 assert (sz <= slotSize); 00355 void * ptr = NULL; 00356 while (ptr == NULL) { 00357 if (myChunk == NULL) { 00358 myChunk = AlignedChunk<chunkSize, slotSize>::getChunk(Super::malloc (chunkSize)); 00359 } 00360 ptr = myChunk->getSlot(); 00361 if (ptr == NULL) { 00362 // This chunk is completely in use. 00363 // "Release" it. 00364 myChunk->release(); 00365 myChunk = NULL; 00366 } 00367 }; 00368 return ptr; 00369 } 00370 00371 // Free a SLOT. 00372 // If the chunk is now completely empty and has been 'released', 00373 // free the whole chunk. 00374 inline void free (void * ptr) 00375 { 00376 // Find out which chunk this slot belongs to. 00377 AlignedChunk<chunkSize, slotSize> * ch = AlignedChunk<chunkSize, slotSize>::getChunk (ptr); 00378 // Return it to its chunk. 00379 int empty = ch->putSlot (ptr); 00380 if (empty && ch->isReleased()) { 00381 Super::free ((void *) ((unsigned long) ch & -chunkSize)); 00382 } 00383 } 00384 00385 private: 00386 00387 // A one chunk buffer. Emptied when the chunk is completely allocated. 00388 AlignedChunk<chunkSize, slotSize> * myChunk; 00389 00390 }; 00391 00392 00393 template <int maxFree, int chunkSize, int slotSize, class Super> 00394 class AlignedChunkHeapFoo : public AlignedSlotHeap<chunkSize, slotSize, AlignedChunkHeap<maxFree, chunkSize, slotSize, Super> > {}; 00395 00396 }; 00397 00398 #endif