/*++ Cache2.h This header file defines an LRU0 cache template that can be used to hold arbitrary objects ! Items in the Cache must have the following format : class DataItem { ICacheRefInterface* m_pCacheRefInterface ; } ; class Constructor { DATA* Create( KEY&, PERCACHEDATA& ) void Release( DATA*, PERCACHEDATA* ) void StaticRelease( DATA*, PERCACHEDATA* ) } --*/ #ifndef _CACHE2_H_ #define _CACHE2_H_ #include "randfail.h" #include "fdlhash.h" #include "lockq.h" #include "tfdlist.h" #include "rwnew.h" #include "refptr2.h" typedef CShareLockNH CACHELOCK ; class CAllocatorCache { /*++ Class Description : This class provides a Memory Allocation cache - we work with an operator new provide below. We exist to provide some optimizations for allocations of the elements of the caches specified in this module. NOTE : We assume the caller provides all locking ! --*/ private : // // The structurure we use to keep our free list ! // struct FreeSpace { struct FreeSpace* m_pNext ; } ; // // Size of each element - clients must not ask for something bigger ! // DWORD m_cbSize ; // // Number of elements in our list at this moment ! // DWORD m_cElements ; // // The maximum number of elements we should hold ! // DWORD m_cMaxElements ; // // Top of the stack ! // struct FreeSpace* m_pHead ; // // Make the following private - nobody is allowed to use these ! // CAllocatorCache( CAllocatorCache& ) ; CAllocatorCache& operator=( CAllocatorCache& ) ; public : // // Initialize the Allocation Cache ! // CAllocatorCache( DWORD cbSize, DWORD cMaxElements = 512 ) ; // // Destroy the Allocation Cache - release extra memory back to system ! // ~CAllocatorCache() ; // // Allocate a block of memory // returns NULL if Out of Memory ! // void* Allocate( size_t cb ) ; // // Return some memory back to the system heap ! // void Free( void* pv ) ; } ; class ICacheRefInterface : public CQElement { /*++ Class Description : This class defines the interface for Cache References - the mechanism that allows multiple caches to reference a single data item. --*/ protected : // // Add an item to the list of caches referencing // this cache item ! // virtual BOOL AddCacheReference( class ICacheRefInterface*, void* pv, BOOL ) = 0 ; // // Remove an item from the list of caches referencing // this cache item ! // virtual BOOL RemoveCacheReference( BOOL fQueue ) = 0 ; // // Remove all references to the cache item ! // virtual BOOL RemoveAllReferences( ) = 0 ; } ; #include "cintrnl.h" // This callback function is used to issue a stop hint during a // long spin while shutting down so that the shutdown won't time // out. typedef void (*PSTOPHINT_FN)(); extern CRITICAL_SECTION g_CacheShutdown ; // // Call these functions to initialize the Cache Library // extern BOOL __stdcall CacheLibraryInit() ; extern BOOL __stdcall CacheLibraryTerm() ; template < class Data, class Key > class CacheExpungeObject { public : // // This function is called to determine whether we should remove // the item from the cache. // // pKey - Pointer to the Key of the item in the cache // pData - Pointer to the data for the item in the cache // cOutstandingReferences - The number of times of outstanding check-outs on the item ! // fMultipleReferenced - TRUE if there is more than one cache that contains // this item ! // virtual BOOL fRemoveCacheItem( Key* pKey, Data* pData ) = 0 ; } ; template < class Data > class CacheCallback { public : virtual BOOL fRemoveCacheItem( Data& d ) { return FALSE ; } } ; class CacheStats : public CHashStats { public : enum COUNTER { ITEMS, // Number of items in the cache CLRU, // Number of items in the LRU List EXPIRED, // Number of items that have been expired ! INSERTS, // Number of items inserted over time READHITS, // Number of times we've had a cache hit needing only readlocks during FindOrCreate()! SUCCESSSEARCH, // Number of times we've successfully searched for an item ! FAILSEARCH, // Number of times we've failed to find an item ! RESEARCH, // Number of times we've had to search a second time for an item WRITEHITS, // Number of times we've had a cache hit requiring a PartialLock() PARTIALCREATES, // Number of times we've created an item with only a PartialLock EXCLUSIVECREATES, // Number of times we've created an item with an Exclusive Lock ! CEFAILS, // Number of times we've failed to allocate a CACHEENTRY structure CLIENTALLOCFAILS, // Number of times we've failed to allocate a Data object CLIENTINITFAILS, // Number of times a client object has failed to initialize ! MAXCOUNTER // A Invalid Counter - all values smaller than this ! } ; // // Array of longs to hold different values ! // long m_cCounters[MAXCOUNTER] ; CacheStats() { ZeroMemory( m_cCounters, sizeof(m_cCounters) ) ; } } ; typedef CacheStats CACHESTATS ; inline void IncrementStat( CacheStats* p, CACHESTATS::COUNTER c ) { _ASSERT( c < CACHESTATS::MAXCOUNTER ) ; if( p != 0 ) { InterlockedIncrement( &p->m_cCounters[c] ) ; } } inline void AddStat( CacheStats*p, CACHESTATS::COUNTER c, long l ) { _ASSERT( c < CACHESTATS::MAXCOUNTER ) ; if( p != 0 ) { InterlockedExchangeAdd( &p->m_cCounters[c], l ) ; } } inline void DecrementStat( CacheStats* p, CACHESTATS::COUNTER c ) { _ASSERT( c < CACHESTATS::MAXCOUNTER ) ; if( p != 0 ) { InterlockedDecrement( &p->m_cCounters[c] ) ; } } template < class Data, class Key, class Constructor, class PerCacheData = LPVOID > class CacheEx : public CacheTable { public : // // For compare, hash functions etc.... we will use this type ! // typedef Data DATA ; typedef Key KEY ; typedef Key* PKEY ; // // Hash Computation function // typedef DWORD (*PFNHASH)( PKEY ) ; // // Key Comparison function - to be provided by caller ! // typedef int (*PKEYCOMPARE)(PKEY, PKEY) ; // // Callback objects for Expunge Operations ! // typedef CacheCallback< DATA > CALLBACKOBJ ; // // Objects that the user can give to the cache to manage the removal of items ! // typedef CacheExpungeObject< DATA, KEY > EXPUNGEOBJECT ; private : // // Define a 'CACHEENTRY' object which holds all the // necessary data for each object which is placed in the cache ! // typedef CCacheItemKey< DATA, KEY, Constructor, PerCacheData > CACHEENTRY ; // // Define the helper class for Hash Tables // typedef TFDLHash< CACHEENTRY, PKEY, &CacheState::HashDLIST > HASHTABLE ; // // An iterator that lets us walk everything in the hash table ! // typedef TFDLHashIterator< HASHTABLE > HASHITER ; // // Is the 'Cache' initialized and in a valid state ! // BOOL m_fValid ; // // An object to collect statistics about cache operations ! // This may be NULL ! // class CacheStats* m_pStats ; // // A list of everything in the Cache, used for TTL processing // CLRUList m_ExpireList ; // // A hash table we use to find things within the Cache // HASHTABLE m_Lookup ; // // Pointer to a runtime-user provided function which is used // to determine what things should be removed from the Cache // // BOOL (* m_pfnExpungeSpecific )( Data & ) ; // // Pointer to a runtime-user provided object derived from CacheCallback< Data > // which lets the user invoke some function for each item in the Cache ! // CALLBACKOBJ* m_pCallbackObject ; // // Reader writer lock which protects all these data structures ! // CACHELOCK m_Lock ; // // The initial TTL we should assign to all newly cached objects ! // DWORD m_TTL ; // // The cache used for creation/deletion of our CACHEENTRY objects ! // CAllocatorCache m_Cache ; protected : // // Virtual function called by CScheduleThread's thread which // we use to bump TTL counters // void Schedule(); // // Function which removes an Entry from the Cache ! // BOOL RemoveEntry( CacheState* pEntry ) ; // // Virtual Function called by CacheList when we pass call // CacheList::ExpungeSpecific // BOOL QueryRemoveEntry( CacheState* pEntry ) ; // // Virtual Function part of CacheTable interface - used // by LRUList to do appropriate locking ! // CACHELOCK& GetLock() { return m_Lock ; } public : // // This is the users extra data - we will provide it on calls // to constructor objects so that they can track some state sync'd // with the cache locks ! // PerCacheData m_PerCacheData ; // // This function is used to return an item to the cache - // it will bump down a ref count for the number of clients // currently using the item ! // static void CheckIn( DATA* ) ; // // This function is provided for cases when the client needs // to check-in an item from a Cache Callback function (i.e. Expunge) // // static void CheckInNoLocks( DATA* ) ; // // This function is used to add a client reference to an item in the cache ! // static void CheckOut( DATA*, long cClientRefs = 1 ) ; // // Constructor - cMax specifies the maximum number of entries // we should hold in the cache. // CacheEx( ) ; // // Destructor - remove ourselves from schedule list before continuing ! // ~CacheEx() ; // // Initialization function - take pointer to function // which should be used to compute hash values on Key's // Also takes the number of seconds objects should live in // the cache ! // BOOL Init( PFNHASH pfnHash, PKEYCOMPARE pKeyCompare, DWORD dwLifetimeSeconds, DWORD cMaxInstances, CACHESTATS* pStats, PSTOPHINT_FN pfnStopHint = NULL ) { /*++ Routine Description : This function initializes the cache so that it is ready to take entries. Arguments : pfnHash - function to be used to compute hash values on keys dwLifetimeSeconds - The number of seconds objects should live in the Cache pfnStopHint - function to be used to send stop hints during long spins so shutdown's don't time out. Return Value : TRUE if successfull --*/ m_pStats = pStats ; m_ExpireList.Init( cMaxInstances, dwLifetimeSeconds ) ; return m_fValid = m_Lookup.Init( 256, 128, 4, pfnHash, &CACHEENTRY::GetKey, pKeyCompare, 0, pStats ) ; } void Expire() { EnterCriticalSection( &g_CacheShutdown ) ; DWORD c = 0 ; m_ExpireList.Expire( this, &m_Cache, c, &m_PerCacheData ) ; LeaveCriticalSection( &g_CacheShutdown ) ; } // // Called to remove all items from the cache ! // BOOL EmptyCache() ; BOOL ExpungeItems( EXPUNGEOBJECT* pExpunge ) ; // // Function which can be used to remove items from the Cache // If default args are used we pick an expired item in the Cache // to remove // BOOL ExpungeKey( DWORD dwHash, PKEY key ) ; // // Either find an item in the cache or Construct a new item // and place it in the Cache. // return the result through pDataOut no matter what ! // // // INTERNAL API's - These are public for convenience - not intended // for Use outside of cachelib !! // // // Either find an item in the cache or Construct a new item // and place it in the Cache. // return the result ! // // // BOOL FindOrCreateInternal( DWORD dwHash, KEY& key, Constructor& constructor, DATA* &pData, BOOL fEarlyCreate = FALSE /* Best Perf if this is FALSE - but required by some users !*/ ) ; // // Find the item if it is in the cache ! // DATA* FindInternal( DWORD dwHash, KEY& key ) ; // // Insert a new item into the cache - // We get to specify whether and what kind of reference // we will hold outside of the cache ! // BOOL InsertInternal( DWORD dwHash, KEY& key, DATA* pData, long cClientRefs = 0 ) ; #ifdef DEBUG static long s_cCreated ; #endif } ; template < class Data, class Key, class Constructor, class PerCacheData = LPVOID > class MultiCacheEx { public: typedef Data DATA ; typedef Key KEY ; typedef Key* PKEY ; // // Hash Computation function // typedef DWORD (*PFNHASH)( PKEY ) ; // // Key Comparison function - to be provided by caller ! // typedef int (*PKEYCOMPARE)(PKEY, PKEY) ; // // Callback objects for Expunge Operations ! // typedef CacheCallback< DATA > CALLBACKOBJ ; // // Objects that the user can give to the cache to manage the removal of items ! // typedef CacheExpungeObject< DATA, KEY > EXPUNGEOBJECT ; private : // // Define a 'CACHEENTRY' object which holds all the // necessary data for each object which is placed in the cache ! // typedef CCacheItemKey< DATA, KEY, Constructor, PerCacheData > CACHEENTRY ; // // Define the type for a single instance ! // typedef CacheEx< Data, Key, Constructor, PerCacheData > CACHEINSTANCE ; // // Is the 'Cache' initialized and in a valid state ! // BOOL m_fValid ; // // Pointer to the various Cache's we subdivide our work into // CACHEINSTANCE *m_pCaches ; // // Number of sub cache's we use to split up the work ! // DWORD m_cSubCaches ; // // We use the hash function to choose which of our subcaches to work with ! // typename CACHEINSTANCE::PFNHASH m_pfnHash ; // // Return the correct cache instance to hold the selected piece of data ! // DWORD ChooseInstance( DWORD dwHash ) ; public : // // Constructor - cMax specifies the maximum number of entries // we should hold in the cache. // MultiCacheEx( ) ; // // Destructor - destroys are various sub cache's // ~MultiCacheEx() ; // // Initialization function - take pointer to function // which should be used to compute hash values on Key's // Also takes the number of seconds objects should live in // the cache ! // BOOL Init( PFNHASH pfnHash, PKEYCOMPARE pfnCompare, DWORD dwLifetimeSeconds, DWORD cMaxElements, DWORD cSubCaches, CACHESTATS* pStats, PSTOPHINT_FN pfnStopHint = NULL ) ; // // Expire items in the cache ! // void Expire() ; // // Called to remove all items from the cache ! // BOOL EmptyCache() ; // // The user wants to remove a large set of items from the cache ! // BOOL ExpungeItems( EXPUNGEOBJECT* pExpunge ) ; // // Function which can be used to remove items from the Cache // If default args are used we pick an expired item in the Cache // to remove // BOOL ExpungeKey( PKEY key ) ; // // Either find an item in the cache or Construct a new item // and place it in the Cache. // return the result through pDataOut no matter what ! // Data* FindOrCreate( Key& key, Constructor& constructor, BOOL fEarlyCreate = FALSE ) ; // // Either find an item in the cache or Construct a new item // and place it in the Cache. // return the result through pDataOut no matter what ! // NOTE : This is for use when the caller has a cheaper // way to compute the hash value then us - in debug we // need to assure that the caller correctly computes this ! // Data* FindOrCreate( DWORD dwHash, Key& key, Constructor& constructor, BOOL fEarlyCreate = FALSE ) ; // // Find an item in the cache - hash of key is precomputed ! // Data* Find( DWORD dwHash, KEY& key ) ; // // Find an item in the cache // Data* Find( KEY& key ) ; // // Insert a new item into the cache - // We get to specify whether and what kind of reference // we will hold outside of the cache ! // BOOL Insert( DWORD dwHash, KEY& key, Data* pData, long cClientRefs = 0 ) ; // // Insert a new item into the cache - // We get to specify whether and what kind of reference // we will hold outside of the cache ! // BOOL Insert( KEY& key, Data* pData, long cClientRefs = 0 ) ; // // This function is used to return an item to the cache - // it will bump down a ref count for the number of clients // currently using the item ! // static void CheckIn( DATA* ) ; // // This function is provided for cases when the client needs // to check-in an item from a Cache Callback function (i.e. Expunge) // // static void CheckInNoLocks( DATA* ) ; // // This function is used to add a client reference to an item in the cache ! // static void CheckOut( DATA*, long cClientRefs = 1 ) ; } ; #include "cache2i.h" #endif // _CACHE2_H_