// 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 "Dll.hpp" #include "List.hpp" #include "New.hpp" #include "Sharelock.hpp" #include "SmpHeap.hpp" #include "Tls.hpp" /********************************************************************/ /* */ /* Structures local to the class. */ /* */ /* The structures supplied here describe the layout of the */ /* private per thread heap structures. */ /* */ /********************************************************************/ typedef struct PRIVATE_HEAP : public LIST { SMP_HEAP_TYPE Heap; } PRIVATE_HEAP; /********************************************************************/ /* */ /* Static data structures. */ /* */ /* The static data structures are initialized and prepared for */ /* use here. */ /* */ /********************************************************************/ STATIC SHARELOCK Sharelock; /********************************************************************/ /* */ /* 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. */ /* */ /********************************************************************/ SMP_HEAP::SMP_HEAP ( int MaxFreeSpace, bool Recycle, bool SingleImage, bool ThreadSafe, bool DeleteHeapOnExit ) : // // Call the constructors for the contained classes. // MaxFreeSpace(MaxFreeSpace), Recycle(Recycle), SingleImage(True), ThreadSafe(ThreadSafe), SMP_HEAP_TYPE( 0,false,true,true ) { // // Setup various control variables. // Active = false; DeleteOnExit = DeleteHeapOnExit; // // Create the linked list headers and a thread // local store variable to point at each threads // private heap. // ActiveHeaps = ((LIST*) SMP_HEAP_TYPE::New( sizeof(LIST) )); DllEvents = ((DLL*) SMP_HEAP_TYPE::New( sizeof(DLL) )); FreeHeaps = ((LIST*) SMP_HEAP_TYPE::New( sizeof(LIST) )); HeapWalk = NULL; Tls = ((TLS*) SMP_HEAP_TYPE::New( sizeof(TLS) )); // // We can only activate the the heap if we manage // to allocate space we requested. // if ( (ActiveHeaps != NULL) && (DllEvents != NULL) && (FreeHeaps != NULL) && (Tls != NULL) ) { // // Execute the constructors for each linked list // and for the thread local store. // PLACEMENT_NEW( ActiveHeaps,LIST ); #ifdef COMPILING_ROCKALL_DLL PLACEMENT_NEW( DllEvents,DLL )( ThreadDetach,this ); #endif PLACEMENT_NEW( FreeHeaps,LIST ); PLACEMENT_NEW( Tls,TLS ); // // Activate the heap. // ActiveLocks = 0; Active = true; } } /********************************************************************/ /* */ /* Memory deallocation. */ /* */ /* When we delete an allocation we try to delete it in the */ /* private per thread heap if it exists. */ /* */ /********************************************************************/ bool SMP_HEAP::Delete( void *Address,int Size ) { // // Although it is very rare there is a chance // that we failed to build the basic heap structures. // if ( Active ) { REGISTER PRIVATE_HEAP *PrivateHeap = ((PRIVATE_HEAP*) Tls -> GetPointer()); // // We need to examine the TLS pointer to make // sure we have a heap for the current thread. // If not we just use the internal heap. // if ( PrivateHeap != NULL ) { return (PrivateHeap -> Heap.Delete( Address,Size )); } else { return (SMP_HEAP_TYPE::Delete( Address,Size )); } } else { return false; } } /********************************************************************/ /* */ /* Delete all allocations. */ /* */ /* We walk the list of all the heaps and instruct each heap */ /* to delete everything. */ /* */ /********************************************************************/ void SMP_HEAP::DeleteAll( bool Recycle ) { // // Although it is very rare there is a chance // that we failed to build the basic heap structures. // if ( Active ) { REGISTER PRIVATE_HEAP *Current; // // Claim a process wide shared lock // to ensure the list of heaps does // not change until we have finished. // Sharelock.ClaimShareLock(); // // You just have to hope the user knows // what they are doing as everything gets // blown away. // for ( Current = ((PRIVATE_HEAP*) ActiveHeaps -> First()); (Current != NULL); Current = ((PRIVATE_HEAP*) Current -> Next()) ) { Current -> Heap.DeleteAll( Recycle ); } // // Release the lock. // Sharelock.ReleaseShareLock(); // // Claim a process wide exclusive lock // to ensure the list of heaps does // not change until we have finished. // Sharelock.ClaimExclusiveLock(); // // We walk the free list of heaps and // delete everything. // for ( Current = ((PRIVATE_HEAP*) FreeHeaps -> First()); Current != NULL; Current = ((PRIVATE_HEAP*) FreeHeaps -> First()) ) { // // Delete each heap from the free list, // call the destructor and delete any // associated space. // Current -> Delete( FreeHeaps ); PLACEMENT_DELETE( Current,PRIVATE_HEAP ); SMP_HEAP_TYPE::Delete( Current ); } // // Release the lock. // Sharelock.ReleaseExclusiveLock(); } } /********************************************************************/ /* */ /* Memory allocation details. */ /* */ /* When we are asked for details we try to the private per */ /* thread heap if it exists. */ /* */ /********************************************************************/ bool SMP_HEAP::Details( void *Address,int *Space ) { // // Although it is very rare there is a chance // that we failed to build the basic heap structures. // if ( Active ) { REGISTER PRIVATE_HEAP *PrivateHeap = ((PRIVATE_HEAP*) Tls -> GetPointer()); // // We need to examine the TLS pointer to make // sure we have a heap for the current thread. // If not we just use the internal heap. // if ( PrivateHeap != NULL ) { return (PrivateHeap -> Heap.Details( Address,Space )); } else { return (SMP_HEAP_TYPE::Details( Address,Space )); } } else { return false; } } /********************************************************************/ /* */ /* Extract the private heap. */ /* */ /* We need to provide all threads with a private heap. When */ /* we discover we need another heap we either recycle an */ /* existing heap or create a new one. */ /* */ /********************************************************************/ PRIVATE_HEAP *SMP_HEAP::GetPrivateHeap( void ) { REGISTER PRIVATE_HEAP *PrivateHeap = ((PRIVATE_HEAP*) Tls -> GetPointer()); // // We need to examine the TLS pointer to make // sure we have a heap for the current thread. // If not we just create a new heap. // if ( PrivateHeap == NULL ) { // // Claim a process wide exclusive lock // to ensure the list of heaps does // not change until we have finished. // Sharelock.ClaimExclusiveLock(); // // When there is an available free heap // then extract it from the free list. // if ( (PrivateHeap = ((PRIVATE_HEAP*) FreeHeaps -> First())) != NULL ) { // // Delete the heap from the list of // of free heaps. // PrivateHeap -> Delete( FreeHeaps ); } // // When there is no available free heap then // we try to make a new heap. // if ( PrivateHeap == NULL ) { // // Release the lock. // Sharelock.ReleaseExclusiveLock(); // // Allocate space for the new private per // thread heap. // PrivateHeap = ((PRIVATE_HEAP*) SMP_HEAP_TYPE::New( sizeof(PRIVATE_HEAP) )); // // We need to ensure that the allocation // worked before we try to add it into // the list of active heaps. // if ( PrivateHeap != NULL ) { // // Activate the new heap. // PLACEMENT_NEW( PrivateHeap,LIST ); PLACEMENT_NEW( & PrivateHeap -> Heap,SMP_HEAP_TYPE ) ( MaxFreeSpace, Recycle, SingleImage, ThreadSafe ); // // If the heap constructor failed, then // do not put this heap in the list of // active heaps, and return NULL back to // the caller. A side-effect of this is // that the allocation for the PrivateHeap // will be leaked. // if (! PrivateHeap->Heap.Available()) { PrivateHeap = NULL; } } // // Claim a process wide exclusive lock // to ensure the list of heaps does // not change until we have finished. // Sharelock.ClaimExclusiveLock(); } // // We would expect to have a new heap by this // point. If not then we just exit. // if ( PrivateHeap != NULL ) { // // Insert the new heap in the list // of active heaps. // PrivateHeap -> Insert( ActiveHeaps ); // // Nasty: we may have an outstanding lock // on the rest of the heaps. If so claim // it for this heap as well. // if ( ActiveLocks > 0 ) { PrivateHeap -> Heap.LockAll(); } // // Update the TLS pointer. // Tls -> SetPointer( ((VOID*) PrivateHeap) ); } // // Release the lock. // Sharelock.ReleaseExclusiveLock(); } return PrivateHeap; } /********************************************************************/ /* */ /* A known area. */ /* */ /* When we are asked about an address we try to the private per */ /* thread heap if it exists. */ /* */ /********************************************************************/ bool SMP_HEAP::KnownArea( void *Address ) { // // Although it is very rare there is a chance // that we failed to build the basic heap structures. // if ( Active ) { REGISTER PRIVATE_HEAP *PrivateHeap = ((PRIVATE_HEAP*) Tls -> GetPointer()); // // We need to examine the TLS pointer to make // sure we have a heap for the current thread. // If not we just use the internal heap. // if ( PrivateHeap != NULL ) { return (PrivateHeap -> Heap.KnownArea( Address )); } else { return (SMP_HEAP_TYPE::KnownArea( Address )); } } else { return false; } } /********************************************************************/ /* */ /* Claim all the heap locks. */ /* */ /* We claim all of the heap locks so that it is safe to do */ /* operations like walking all of the heaps. */ /* */ /********************************************************************/ void SMP_HEAP::LockAll( VOID ) { // // Although it is very rare there is a chance // that we failed to build the basic heap structures. // if ( Active ) { REGISTER PRIVATE_HEAP *Current; // // Claim a process wide shared lock // to ensure the list of heaps does // not change until we have finished. // Sharelock.ClaimShareLock(); // // Nasty: We may actually create or delete // a heap between locking all the heaps and // unlocking them. Thus, we need to keep a // count of the outstanding locks to keep // this all consistent. // ASSEMBLY::AtomicIncrement( ((SBIT32*) & ActiveLocks) ); // // You just have to hope the user knows // what they are doing as we claim all // of the heap locks. // for ( Current = ((PRIVATE_HEAP*) ActiveHeaps -> First()); (Current != NULL); Current = ((PRIVATE_HEAP*) Current -> Next()) ) { Current -> Heap.LockAll(); } // // Release the lock. // Sharelock.ReleaseShareLock(); } } /********************************************************************/ /* */ /* Multiple memory deallocations. */ /* */ /* When we delete multiple allocations we try to delete them on */ /* the private per thread heap if it exists. */ /* */ /********************************************************************/ bool SMP_HEAP::MultipleDelete ( int Actual, void *Array[], int Size ) { // // Although it is very rare there is a chance // that we failed to build the basic heap structures. // if ( Active ) { REGISTER PRIVATE_HEAP *PrivateHeap = ((PRIVATE_HEAP*) Tls -> GetPointer()); // // We need to examine the TLS pointer to make // sure we have a heap for the current thread. // If not we just use the internal heap. // if ( PrivateHeap != NULL ) { return (PrivateHeap -> Heap.MultipleDelete(Actual,Array,Size)); } else { return (SMP_HEAP_TYPE::MultipleDelete( Actual,Array,Size )); } } else { return false; } } /********************************************************************/ /* */ /* Multiple memory allocations. */ /* */ /* We allocate space for the current thread from the local */ /* private per thread heap. If we do not have a local private */ /* per thread heap then we create one and use it. */ /* */ /********************************************************************/ bool SMP_HEAP::MultipleNew ( int *Actual, void *Array[], int Requested, int Size, int *Space, bool Zero ) { // // Although it is very rare there is a chance // that we failed to build the basic heap structures. // if ( Active ) { REGISTER PRIVATE_HEAP *PrivateHeap = GetPrivateHeap(); // // We need to examine private heap to make // sure we have a heap for the current thread. // if ( PrivateHeap != NULL ) { // // Allocate the memory requested on the local // private per thread heap. // return ( PrivateHeap -> Heap.MultipleNew ( Actual, Array, Requested, Size, Space, Zero ) ); } else { // // We were unable to create a new heap // so exit. // (*Actual) = 0; return false; } } else { // // We are not active yet so exit. // (*Actual) = 0; return false; } } /********************************************************************/ /* */ /* Memory allocation. */ /* */ /* We allocate space for the current thread from the local */ /* private per thread heap. If we do not have a local private */ /* per thread heap then we create one and use it. */ /* */ /********************************************************************/ void *SMP_HEAP::New( int Size,int *Space,bool Zero ) { // // Although it is very rare there is a chance // that we failed to build the basic heap structures. // if ( Active ) { REGISTER PRIVATE_HEAP *PrivateHeap = GetPrivateHeap(); // // We need to examine private heap to make // sure we have a heap for the current thread. // if ( PrivateHeap != NULL ) { return (PrivateHeap -> Heap.New( Size,Space,Zero )); } else { return NULL; } } else { return NULL; } } /********************************************************************/ /* */ /* Memory reallocation. */ /* */ /* We reallocate space for the current thread on the local */ /* private per thread heap. If we do not have a local private */ /* per thread heap then we create one and use it. */ /* */ /********************************************************************/ void *SMP_HEAP::Resize ( void *Address, int NewSize, int Move, int *Space, bool NoDelete, bool Zero ) { // // Although it is very rare there is a chance // that we failed to build the basic heap structures. // if ( Active ) { REGISTER PRIVATE_HEAP *PrivateHeap = GetPrivateHeap(); // // We need to examine private heap to make // sure we have a heap for the current thread. // if ( PrivateHeap != NULL ) { // // Reallocate the memory requested on // the local private per thread heap. // return ( PrivateHeap -> Heap.Resize ( Address, NewSize, Move, Space, NoDelete, Zero ) ); } else { return NULL; } } else { return NULL; } } /********************************************************************/ /* */ /* Special memory allocation. */ /* */ /* We sometimes need to allocate some memory from the internal */ /* memory allocator which lives for the lifetime of the heap. */ /* */ /********************************************************************/ void *SMP_HEAP::SpecialNew( int Size ) { return SMP_HEAP_TYPE::New( Size ); } /********************************************************************/ /* */ /* Delete a local heap. */ /* */ /* Delete a local per thread heap and return all the outstanding */ /* memory to the operating system. */ /* */ /********************************************************************/ void SMP_HEAP::ThreadDetach( void *Parameter,int Reason ) { // // We only take any action on a thread detach // notification. All other notifications are // not actioned. // if ( Reason == DLL_THREAD_DETACH ) { REGISTER SMP_HEAP *SmpHeap = ((SMP_HEAP*) Parameter); // // Claim a process wide exclusive lock // to ensure the list of heaps does // not change until we have finished. // Sharelock.ClaimExclusiveLock(); // // There is a nasty situation where the // destructor is called before a thread // fully terminates so ensure the heap // is still active. // if ( SmpHeap -> Active ) { REGISTER PRIVATE_HEAP *PrivateHeap = ((PRIVATE_HEAP*) SmpHeap -> Tls -> GetPointer()); // // We need to examine the TLS pointer to make // sure we have a heap for the current thread. // If not we just use the internal heap. // if ( PrivateHeap != NULL ) { // // Update the TLS pointer. // SmpHeap -> Tls -> SetPointer( NULL ); // // Insert the new heap in the list // of active heaps. // PrivateHeap -> Delete( SmpHeap -> ActiveHeaps ); // // When we are not allowed to delete // the heap we put it on the free list. // if ( ! SmpHeap -> DeleteOnExit ) { PrivateHeap -> Insert( SmpHeap -> FreeHeaps ); } // // Nasty: we may have an outstanding lock // on this heap. If so then free it. // if ( SmpHeap -> ActiveLocks > 0 ) { PrivateHeap -> Heap.UnlockAll(); } // // Release the lock. // Sharelock.ReleaseExclusiveLock(); // // When we are allowed to delete the // heap we do it here. // if ( ! SmpHeap -> DeleteOnExit ) { // // Truncate the heap to remove any // unwanted space. // PrivateHeap -> Heap.Truncate( 0 ); } else { #ifdef COMPLAIN_ABOUT_SMP_HEAP_LEAKS // // We have finished with the private // heap so now is a good time to complain // about leaks. // PrivateHeap -> Heap.HeapLeaks(); #endif // // We have finished with the private // heap so delete it. // PLACEMENT_DELETE( PrivateHeap,PRIVATE_HEAP ); SmpHeap -> SMP_HEAP_TYPE::Delete( PrivateHeap ); } } else { Sharelock.ReleaseExclusiveLock(); } } else { Sharelock.ReleaseExclusiveLock(); } } } /********************************************************************/ /* */ /* Truncate the heap. */ /* */ /* We need to truncate the heap. This is pretty much a null */ /* call as we do this as we go along anyway. The only thing we */ /* can do is free any space the user suggested keeping earlier. */ /* */ /********************************************************************/ bool SMP_HEAP::Truncate( int MaxFreeSpace ) { REGISTER bool Result = true; // // Although it is very rare there is a chance // that we failed to build the basic heap structures. // if ( Active ) { REGISTER PRIVATE_HEAP *Current; // // Claim a process wide shared lock // to ensure the list of heaps does // not change until we have finished. // Sharelock.ClaimShareLock(); // // You just have to hope the user knows // what they are doing as we are truncating // all of the heaps. // for ( Current = ((PRIVATE_HEAP*) ActiveHeaps -> First()); (Current != NULL); Current = ((PRIVATE_HEAP*) Current -> Next()) ) { // // If faulty delete is noted during the // cache flushes then exit with the // correct status. // if ( ! Current -> Heap.Truncate( MaxFreeSpace ) ) { Result = false; } } // // Release the lock. // Sharelock.ReleaseShareLock(); } return Result; } /********************************************************************/ /* */ /* Release all the heap locks. */ /* */ /* We unlock all of the heap locks so normal processing can */ /* continue on the heaps. */ /* */ /********************************************************************/ void SMP_HEAP::UnlockAll( VOID ) { // // Although it is very rare there is a chance // that we failed to build the basic heap structures. // if ( Active ) { REGISTER PRIVATE_HEAP *Current; // // Claim a process wide shared lock // to ensure the list of heaps does // not change until we have finished. // Sharelock.ClaimShareLock(); // // You just have to hope the user knows // what they are doing as we claim all // of the heap locks. // for ( Current = ((PRIVATE_HEAP*) ActiveHeaps -> First()); (Current != NULL); Current = ((PRIVATE_HEAP*) Current -> Next()) ) { Current -> Heap.UnlockAll(); } // // Nasty: We may actually create or delete // a private heap for a thread between // locking an 'SMP_HEAP' and unlocking it. // Thus, we need to keep a count of the // outstanding locks to keep this all // consistent. // ASSEMBLY::AtomicDecrement( ((SBIT32*) & ActiveLocks) ); // // Release the lock. // Sharelock.ReleaseShareLock(); } } /********************************************************************/ /* */ /* Verify a memory allocation details. */ /* */ /* When we verify an allocation we try to verify it in the */ /* private per thread heap if it exists. */ /* */ /********************************************************************/ bool SMP_HEAP::Verify( void *Address,int *Space ) { // // Although it is very rare there is a chance // that we failed to build the basic heap structures. // if ( Active ) { REGISTER PRIVATE_HEAP *PrivateHeap = ((PRIVATE_HEAP*) Tls -> GetPointer()); // // We need to examine the TLS pointer to make // sure we have a heap for the current thread. // If not we just use the internal heap. // if ( PrivateHeap != NULL ) { return (PrivateHeap -> Heap.Verify( Address,Space )); } else { return (SMP_HEAP_TYPE::Verify( Address,Space )); } } else { return false; } } /********************************************************************/ /* */ /* Walk the heap. */ /* */ /* We have been asked to walk the heap. It is hard to know */ /* why anybody might want to do this given the rest of the */ /* functionality available. Nonetheless, we just do what is */ /* required to keep everyone happy. */ /* */ /********************************************************************/ bool SMP_HEAP::Walk( bool *Activity,void **Address,int *Space ) { // // Claim a process wide shared lock // to ensure the list of heaps does // not change until we have finished. // Sharelock.ClaimShareLock(); // // Nasty, in 'SMP_HEAP' we have multiple heaps // to walk so if we don't have a current heap // then just select the first available. // if ( ((*Address) == NULL) || (HeapWalk == NULL) ) { HeapWalk = ((PRIVATE_HEAP*) ActiveHeaps -> First()); } // // Walk the heap. When we come to the end of // the current heap then move on to the next // heap. // while ( (HeapWalk != NULL) && (! HeapWalk -> Heap.Walk( Activity,Address,Space )) ) { HeapWalk = ((PRIVATE_HEAP*) HeapWalk -> Next()); } // // Release the lock. // Sharelock.ReleaseShareLock(); return (HeapWalk != NULL); } /********************************************************************/ /* */ /* Class destructor. */ /* */ /* Destory the heap. */ /* */ /********************************************************************/ SMP_HEAP::~SMP_HEAP( void ) { // // Although it is very rare there is a chance // that we failed to build the basic heap structures. // if ( Active ) { REGISTER PRIVATE_HEAP *Current; // // Deactivate the heap. // Active = false; // // Claim a process wide exclusive lock // to ensure the list of heaps does // not change until we have finished. // Sharelock.ClaimExclusiveLock(); // // We walk the active list of heaps and // delete everything. // for ( Current = ((PRIVATE_HEAP*) ActiveHeaps -> First()); Current != NULL; Current = ((PRIVATE_HEAP*) ActiveHeaps -> First()) ) { // // Delete each heap from the active list, // call the destructor and delete any // associated space. // Current -> Delete( ActiveHeaps ); #ifdef COMPLAIN_ABOUT_SMP_HEAP_LEAKS // // We have finished with the private // heap so now is a good time to complain // about leaks. // Current -> Heap.HeapLeaks(); #endif // // We have finished with the private // heap so delete it. // PLACEMENT_DELETE( Current,PRIVATE_HEAP ); SMP_HEAP_TYPE::Delete( Current ); } // // We walk the free list of heaps and // delete everything. // for ( Current = ((PRIVATE_HEAP*) FreeHeaps -> First()); Current != NULL; Current = ((PRIVATE_HEAP*) FreeHeaps -> First()) ) { // // Delete each heap from the active list, // call the destructor and delete any // associated space. // Current -> Delete( FreeHeaps ); #ifdef COMPLAIN_ABOUT_SMP_HEAP_LEAKS // // We have finished with the private // heap so now is a good time to complain // about leaks. // Current -> Heap.HeapLeaks(); #endif // // We have finished with the private // heap so delete it. // PLACEMENT_DELETE( Current,PRIVATE_HEAP ); SMP_HEAP_TYPE::Delete( Current ); } // // Release the lock. // Sharelock.ReleaseExclusiveLock(); // // Call the list and TLS destructors. // PLACEMENT_DELETE( Tls,TLS ); PLACEMENT_DELETE( FreeHeaps,LIST ); #ifdef COMPILING_ROCKALL_DLL PLACEMENT_DELETE( DllEvents,DLL ); #endif PLACEMENT_DELETE( ActiveHeaps,LIST ); // // Delete the space. // SMP_HEAP_TYPE::Delete( Tls ); SMP_HEAP_TYPE::Delete( FreeHeaps ); SMP_HEAP_TYPE::Delete( DllEvents ); SMP_HEAP_TYPE::Delete( ActiveHeaps ); // // Zero the pointers just to be tidy. // Tls = NULL; HeapWalk = NULL; FreeHeaps = NULL; DllEvents = NULL; ActiveHeaps = NULL; } }