Eneboo - Documentación para desarrolladores
src/hoard/src/heaplayers/experimental/alignedchunk.h
Ir a la documentación de este archivo.
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
 Todo Clases Namespaces Archivos Funciones Variables 'typedefs' Enumeraciones Valores de enumeraciones Propiedades Amigas 'defines'