#ifndef _FIND_HPP_ #define _FIND_HPP_ // Ruler // 1 2 3 4 5 6 7 8 //345678901234567890123456789012345678901234567890123456789012345678901234567890 /********************************************************************/ /* */ /* The standard layout. */ /* */ /* The standard layout for 'hpp' files for this code is as */ /* follows: */ /* */ /* 1. Include files. */ /* 2. Constants exported from the class. */ /* 3. Data structures exported from the class. */ /* 4. Forward references to other data structures. */ /* 5. Class specifications (including inline functions). */ /* 6. Additional large inline functions. */ /* */ /* Any portion that is not required is simply omitted. */ /* */ /********************************************************************/ #include "Global.hpp" #include "Environment.hpp" #include "Common.hpp" #include "List.hpp" #include "Page.hpp" #include "Prefetch.hpp" #include "Rockall.hpp" #include "Sharelock.hpp" #include "Spinlock.hpp" #include "Tls.hpp" /********************************************************************/ /* */ /* Class forward references. */ /* */ /* We need to refer to the following classes before they are */ /* fully specified so here we list them as forward references. */ /* */ /********************************************************************/ class CACHE; class HEAP; /********************************************************************/ /* */ /* Find a memory allocation. */ /* */ /* When a memory allocation is released all we are given is . */ /* the allocation address. This is not very helpful as the */ /* allocation information is not stored relative to this */ /* address. Instead we use a hash table to map the allocation */ /* address to the allocation information. */ /* */ /********************************************************************/ class FIND : public ENVIRONMENT, public COMMON { // // Private type specifications. // // We need to do a recursive search in the find // table in order to locate the associated page // from an address. As this is expensive there // is also a cache that slaves common translations // to improve performance. // typedef struct { VOID *Address; PAGE *Page; #ifdef DEBUGGING SBIT32 Version; #endif } LOOK_ASIDE; // // Private data. // // All the page descriptions are stored in the // hash table so they can be quickly found. The // key is the address of the first byte on the // page. The 'MaxHash' is the number of elements // in the hash table and is always a power of two. // The 'HashMask' is a bit mask to remove any // unwanted part of the hash key. The 'HashShift' // is the number of bits required to shift and is // as a substitute for divide. The 'Resize' flag // indicates whether the hash table is permitted // to grow. // SBIT32 MaxHash; SBIT32 HashMask; SBIT32 HashShift; BOOLEAN Resize; // // The hash table allows addresses to be mapped to // page descriptions quickly. Nonetheless, it is // not fast enough. To enhance performance a look // aside cache slaves the hottest translations. The // 'MaxLookAside' is the number of elements in the // cache. The 'MaxAddressMask' is a mask that removes // low order bits of an address and represents the // span of each cache entry. The 'LookAsideActions' // is a simple count of requests to the cache. The // 'LookAsideMask' and 'LookAsideShift' parallel the // fields above in the hash table. The 'LookAsideThreshold' // determines at what point the cache will become // active. The 'ThreadSafe' flag indicates whether // locking is required. // SBIT32 MaxLookAside; BIT32 MaxAddressMask; BIT32 MinAddressMask; SBIT32 LookAsideActions; SBIT32 LookAsideMask; SBIT32 LookAsideShift; SBIT32 LookAsideThreshold; BOOLEAN ThreadSafe; // // When a request is made to translate an address to // a page description the cache is the first port of // call. If the translation is not found then the // hash table is tried. The 'Hash' points to the hash // table. The 'LookAside' points to the lookaside // hash table. The 'Rockall' points to the external API // to give access to the low level external allocation // functions. // LIST *Hash; LOOK_ASIDE *LookAside; ROCKALL *Rockall; // // The translation of addresses to page descriptions // is very common. So care has been taken to ensure // it is not a bottleneck when locking is enabled. The // 'Sharelock' is a fast reader/writer lock and is used // almost all the time. The 'Spinlock' is an exclusive // lock and is only used when the 'Hash' and 'LookAside' // tables are resized. // PREFETCH Prefetch; SHARELOCK Sharelock; SPINLOCK Spinlock; #ifdef ENABLE_HEAP_STATISTICS // // Statistics data. // // There is a concern with any hash table about // poor hashing keys and poor performance. The // statistics monitor various data so as to allow // the performance metrics to be monitored. The // 'Fills' counter keeps track of the number of // cache fills. The 'Hits' counter monitors the // number of cache hits. The 'MaxPages' counter // is the high water mark of hash table entries. // The 'MaxTests' is the max number of compares // done while searching an entry. The 'Misses' // counter keeps track of the number of cache misses. // The 'Scans' counter monitors the number of hash // table searches. The 'Tests' counter is the // total number of tests performed while searching // for entries. // SBIT32 Fills; SBIT32 Hits; SBIT32 MaxPages; SBIT32 MaxTests; SBIT32 Misses; SBIT32 Scans; SBIT32 Tests; #endif SBIT32 Used; #ifndef ENABLE_RECURSIVE_LOCKS // // Static private data. // // It is not uncommon for developers to have some // form of bias. I dislike recursive locks so here // I introduce a TLS value to indicate whether the // current thread has a global lock. If so all // locking in the other classes is disabled. // STATIC THREAD_LOCAL_STORE LockCount; #endif public: // // Public functions. // // The translation functionality supplied by this // class is only applicable after an allocation // has been made. Hence, all of the APIs supported // relate to the need to translate an allocation // address to the host page description. // FIND ( SBIT32 NewMaxHash, SBIT32 NewMaxLookAside, SBIT32 NewFindThreshold, ROCKALL *NewRockall, BOOLEAN NewResize, BOOLEAN NewThreadSafe ); BOOLEAN Delete( VOID *Address,CACHE *ParentCache ); VOID DeleteFromFindList( PAGE *Page ); BOOLEAN Details ( VOID *Address, SEARCH_PAGE *Details, CACHE *ParentCache, SBIT32 *Size ); PAGE *FindPage( VOID *Address,CACHE *ParentCache ); VOID InsertInFindList( PAGE *Page ); BOOLEAN KnownArea( VOID *Address,CACHE *ParentCache ); VOID ReleaseFindShareLockAndUpdate ( VOID *Address, PAGE *Page, SBIT32 Version ); BOOLEAN Walk ( BOOLEAN *Active, VOID **Address, CACHE *ParentCache, SBIT32 *Size ); VOID UpdateFind ( BIT32 NewMaxAddressMask, BIT32 NewMinAddressMask ); ~FIND( VOID ); // // Public inline functions. // // Although this class is perhaps the most self // contained. Nonetheless, there is still lots // of situations when other classes need to // interact and get information about the current // situation. // INLINE VOID ClaimFindExclusiveLock( VOID ) { if ( (ThreadSafe) && (GetLockCount() == 0) ) { Sharelock.ClaimExclusiveLock(); } } INLINE VOID ClaimFindShareLock( VOID ) { if ( (ThreadSafe) && (GetLockCount() == 0) ) { Sharelock.ClaimShareLock(); } } INLINE VOID DeleteAll( VOID ) { LookAsideActions = 0; } INLINE VOID ReleaseFindExclusiveLock( VOID ) { if ( (ThreadSafe) && (GetLockCount() == 0) ) { Sharelock.ReleaseExclusiveLock(); } } INLINE VOID ReleaseFindShareLock( VOID ) { if ( (ThreadSafe) && (GetLockCount() == 0) ) { Sharelock.ReleaseShareLock(); } } // // Static public inline functions. // // There is a strong case for removing the lock // count functionality from this class. However, // as it consists of a single declaration and the // following inline functions I have not been // driven to fix this yet. Maybe some day. // #ifndef ENABLE_RECURSIVE_LOCKS STATIC INLINE VOID DecrementLockCount( VOID ) { LockCount.SetPointer ( ((VOID*) (((SBIT32) LockCount.GetPointer()) - 1)) ); } STATIC INLINE SBIT32 GetLockCount( VOID ) { return ((SBIT32) LockCount.GetPointer()); } STATIC INLINE VOID IncrementLockCount( VOID ) { LockCount.SetPointer ( ((VOID*) (((SBIT32) LockCount.GetPointer()) + 1)) ); } #else STATIC INLINE VOID DecrementLockCount( VOID ) { /* void */ } STATIC INLINE SBIT32 GetLockCount( VOID ) { return 0; } STATIC INLINE VOID IncrementLockCount( VOID ) { /* void */ } #endif #ifdef ENABLE_HEAP_STATISTICS // // Public inline statistic functions. // // The statistics are typically provided in // debug builds to provide good information // about allocation patterns. // INLINE SBIT32 AverageHashLength( VOID ) { return (Tests / ((Scans > 0) ? Scans : 1)); } INLINE SBIT32 CacheFills( VOID ) { return Fills; } INLINE SBIT32 CacheHits( VOID ) { return Hits; } INLINE SBIT32 CacheMisses( VOID ) { return Misses; } INLINE SBIT32 MaxHashLength( VOID ) { return MaxTests; } INLINE SBIT32 MaxHashSize( VOID ) { return MaxHash; } INLINE SBIT32 MaxLookAsideSize( VOID ) { return MaxLookAside; } INLINE SBIT32 MaxUsage( VOID ) { return ((MaxPages * 100) / MaxHash); } INLINE SBIT32 TotalScans( VOID ) { return Scans; } #endif private: // // Private functions. // // Although the hashed lookup functionality is // externally visable the look aside cache is // hidden from view along with the ability to // resize the hash table. // BOOLEAN FindLookAside( VOID *Address,PAGE **Page ); VOID ResizeHashTable( VOID ); // // Private inline functions. // // Although I am not keen on code in the headers // certain functions are so small or so hot that // I have to submit to the desire to do it. // INLINE VOID ChangeToExclusiveLock( VOID ) { if ( (ThreadSafe) && (GetLockCount() == 0) ) { Sharelock.ChangeSharedLockToExclusiveLock(); } } INLINE LIST *FindHashHead( VOID *Address ) { REGISTER BIT32 Value = (((BIT32) Address) * 2964557531); return (& Hash[ ((Value >> HashShift) & HashMask) ]); } INLINE LOOK_ASIDE *FindLookAsideHead( VOID *Address ) { REGISTER BIT32 Value = (((BIT32) Address) * 2964557531); return (& LookAside[ ((Value >> LookAsideShift) & LookAsideMask) ]); } // // Disabled operations. // // All copy constructors and class assignment // operations are disabled. // FIND( CONST FIND & Copy ); VOID operator=( CONST FIND & Copy ); }; #endif