// Ruler // 1 2 3 4 5 6 7 8 //345678901234567890123456789012345678901234567890123456789012345678901234567890 /********************************************************************/ /* */ /* The standard layout. */ /* */ /* The standard layout for 'cpp' files in this code is as */ /* follows: */ /* */ /* 1. Include files. */ /* 2. Constants local to the class. */ /* 3. Data structures local to the class. */ /* 4. Data initializations. */ /* 5. Static functions. */ /* 6. Class functions. */ /* */ /* The constructor is typically the first function, class */ /* member functions appear in alphabetical order with the */ /* destructor appearing at the end of the file. Any section */ /* or function this is not required is simply omitted. */ /* */ /********************************************************************/ #include "InterfacePCH.hpp" #include "Assembly.hpp" #include "Prefetch.hpp" #include "Spinlock.hpp" #include "ZoneHeap.hpp" /********************************************************************/ /* */ /* Constants local to the class. */ /* */ /* The constants supplied here try to make the layout of the */ /* the caches easier to understand and update. */ /* */ /********************************************************************/ CONST SBIT32 AlignmentMask = (sizeof(double)-1); CONST SBIT32 FindCacheSize = 2048; CONST SBIT32 FindCacheThreshold = 0; CONST SBIT32 FindSize = 1024; CONST SBIT32 Stride1 = 1024; CONST SBIT32 Stride2 = 1024; CONST SBIT32 ZonePageSize = 4096; /********************************************************************/ /* */ /* The description of the heap. */ /* */ /* A heap is a collection of fixed sized allocation caches. */ /* An allocation cache consists of an allocation size, the */ /* number of pre-built allocations to cache, a chunk size and */ /* a parent page size which is sub-divided to create elements */ /* for this cache. A heap consists of two arrays of caches. */ /* Each of these arrays has a stride (i.e. 'Stride1' and */ /* 'Stride2') which is typically the smallest common factor of */ /* all the allocation sizes in the array. */ /* */ /********************************************************************/ STATIC ROCKALL::CACHE_DETAILS Caches1[] = { // // Bucket Size Of Bucket Parent // Size Cache Chunks Page Size // { 4096, 8, 65536, 65536 }, { 0,0,0,0 } }; STATIC ROCKALL::CACHE_DETAILS Caches2[] = { // // Bucket Size Of Bucket Parent // Size Cache Chunks Page Size // { 5120, 4, 65536, 65536 }, { 6144, 4, 65536, 65536 }, { 7168, 4, 65536, 65536 }, { 8192, 8, 65536, 65536 }, { 9216, 0, 65536, 65536 }, { 10240, 0, 65536, 65536 }, { 12288, 0, 65536, 65536 }, { 16384, 2, 65536, 65536 }, { 21504, 0, 65536, 65536 }, { 32768, 0, 65536, 65536 }, { 65536, 0, 65536, 65536 }, { 65536, 0, 65536, 65536 }, { 0,0,0,0 } }; /********************************************************************/ /* */ /* The description bit vectors. */ /* */ /* All heaps keep track of allocations using bit vectors. An */ /* allocation requires 2 bits to keep track of its state. The */ /* following array supplies the size of the available bit */ /* vectors measured in 32 bit words. */ /* */ /********************************************************************/ STATIC int NewPageSizes[] = { 2,0 }; /********************************************************************/ /* */ /* Static data structures. */ /* */ /* The static data structures are initialized and prepared for */ /* use here. */ /* */ /********************************************************************/ STATIC PREFETCH Prefetch; /********************************************************************/ /* */ /* Class constructor. */ /* */ /* The overall structure and layout of the heap is controlled */ /* by the various constants and calls made in this function. */ /* There is a significant amount of flexibility available to */ /* a heap which can lead to them having dramatically different */ /* properties. */ /* */ /********************************************************************/ ZONE_HEAP::ZONE_HEAP ( int MaxFreeSpace, bool Recycle, bool SingleImage, bool ThreadSafe ) : // // Call the constructors for the contained classes. // ROCKALL ( Caches1, Caches2, FindCacheSize, FindCacheThreshold, FindSize, MaxFreeSpace, NewPageSizes, Recycle, SingleImage, Stride1, Stride2, ThreadSafe ) { AUTO ZONE NewZone = { NULL,NULL }; // // Setup the heap structures. // MaxSize = ZonePageSize; ThreadLocks = ThreadSafe; Zone = NewZone; } /********************************************************************/ /* */ /* Delete all allocations. */ /* */ /* Delete all the heap allocations and return all the space */ /* back to the operating system. */ /* */ /********************************************************************/ void ZONE_HEAP::DeleteAll( bool Recycle ) { AUTO ZONE Update = { NULL,NULL }; // // Delete all outstanding allocations. // ROCKALL::DeleteAll( Recycle ); // // Delete the stale zone pointers. // WriteZone( & Update ); } /********************************************************************/ /* */ /* Multiple memory allocations. */ /* */ /* We allocate by advancing a pinter down an array. This */ /* is very fast but means that it can not be deleted except */ /* by destroying the entire heap. */ /* */ /********************************************************************/ bool ZONE_HEAP::MultipleNew ( int *Actual, void *Array[], int Requested, int Size, int *Space, bool Zero ) { // // We simply call 'New' to create each element // for a zone heap. // for ( (*Actual)=0;(*Actual) < Requested;(*Actual) ++ ) { REGISTER VOID **Current = & Array[ (*Actual) ]; // // Create an allocation. // (*Current) = (ZONE_HEAP::New( Size,Space,Zero )); // // Exit if there is no more space. // if ( (*Current) == NULL ) { return false; } } return true; } /********************************************************************/ /* */ /* Memory allocation. */ /* */ /* We allocate by advancing a pinter down an array. This */ /* is very fast but means that it can not be deleted except */ /* by destroying the entire heap. */ /* */ /********************************************************************/ void *ZONE_HEAP::New( int Size,int *Space,bool Zero ) { // // We would really hope that nobody would ask // for a negative amount of memory but just to // be sure we verify that this is not the case. // if ( Size >= 0 ) { AUTO SBIT32 NewSize = ((Size + AlignmentMask) & ~AlignmentMask); AUTO ZONE Original; AUTO ZONE Update; // // We would hope to create the allocation on the // first try but there is a possibility that it // may take serveral tries. do { // // Extract a copy of the current zone pointers // into local variables. // Original = Zone; Update = Original; // // We need to ensure that there is enough space // in the zone for the current allocation. // if ( (Update.Start == NULL) || ((Update.Start += NewSize) > Update.End) ) { // // We do not have enough space. If the size // seems reasonable then get a new block from // Rockall. If not just pass the request along // to Rockall. // if ( NewSize <= (MaxSize / 2) ) { STATIC SPINLOCK Spinlock; // // We need to create a new zone page // so claim a lock. // Spinlock.ClaimLock(); // // We may find that the zone has // already been updated. If so // just exit. // if ( Update.End == Zone.End ) { // // Try to allocate a new zone // block. // Update.Start = ((CHAR*) ROCKALL::New( MaxSize )); // // Verify we were able to create // a new zone page. // if ( Update.Start != NULL ) { Update.End = (Update.Start + MaxSize); } else { Update.End = NULL; } // // Update the zone. // WriteZone( & Update ); } // // Release the lock. // Spinlock.ReleaseLock(); // // If we were unable to get more // space then exit. // if ( Update.Start == NULL ) { return NULL; } } else { return (ROCKALL::New( Size,Space,Zero )); } } } while ( ! UpdateZone( & Original,& Update ) ); // // Prefetch the first cache line of // the allocation if we are running // a Pentium III or better. // Prefetch.L1( ((CHAR*) Original.Start),1 ); // // If the caller wants to know the real // size them we supply it. // if ( Space != NULL ) { (*Space) = NewSize; } // // If we need to zero the allocation // we do it here. // if ( Zero ) { ZeroMemory( Original.Start,NewSize ); } // // Exit and return the address. // return (Original.Start); } else { return NULL; } } /********************************************************************/ /* */ /* Update the zone. */ /* */ /* Ww update the zone when we do an allocation. We do this */ /* atomically if there are multiple threads. */ /* */ /********************************************************************/ bool ZONE_HEAP::UpdateZone( ZONE *Original,ZONE *Update ) { // // Do we need to allow for multiple threads. If // so we need to do the update atomically. If // not then a simple assignment is fine. // if ( ThreadLocks ) { REGISTER SBIT64 FinalValue = ( ASSEMBLY::AtomicCompareExchange64 ( ((SBIT64*) & Zone), (*((SBIT64*) Update)), (*((SBIT64*) Original)) ) ); return (FinalValue == (*((SBIT64*) Original))); } else { Zone = (*Update); return True; } } /********************************************************************/ /* */ /* Class destructor. */ /* */ /* Destory the heap. */ /* */ /********************************************************************/ ZONE_HEAP::~ZONE_HEAP( VOID ) { /* void */ }