490 lines
9.9 KiB
C
490 lines
9.9 KiB
C
|
/*++
|
||
|
|
||
|
Cache.h
|
||
|
|
||
|
This file defines some templates which implement
|
||
|
a generic cache manager.
|
||
|
|
||
|
|
||
|
In order to use the Cache manager, your class must
|
||
|
have the following :
|
||
|
|
||
|
|
||
|
|
||
|
//
|
||
|
// SampleClass derives from CRefCount as smart pointers
|
||
|
// 'CRefPtr<Data>' are used to manage the life time of cached
|
||
|
// objects.
|
||
|
//
|
||
|
|
||
|
class SampleClass : public CRefCount {
|
||
|
|
||
|
|
||
|
//
|
||
|
// Cached class has a constructor which takes two
|
||
|
// arguments, the Key used to find the Data in the Cache
|
||
|
// and the 'Constructor' (which is a type specified in
|
||
|
// the cache template) which contains whatever
|
||
|
// initialization data that needs to be passed.
|
||
|
//
|
||
|
// The Cached class should do minimal work within the
|
||
|
// constructor. The FindOrCreate() function will also
|
||
|
// call an innitialization class which finishes initialization
|
||
|
// of the object.
|
||
|
//
|
||
|
SampleClass( Key& k, Constructor &constructor ) ;
|
||
|
|
||
|
//
|
||
|
// If the Cached class does not do all of its initialization
|
||
|
// in its Constructor, then The ExclusiveLock() function must
|
||
|
// guarantee that no clients can use the object untill the
|
||
|
// lock is released.
|
||
|
//
|
||
|
// If the Cached class is completely initialized after the
|
||
|
// constructor is called then this can be a no-op function.
|
||
|
//
|
||
|
ExclusiveLock() ;
|
||
|
|
||
|
//
|
||
|
// After this is called clients may use the object.
|
||
|
//
|
||
|
ExclusiveUnlock() ;
|
||
|
|
||
|
|
||
|
//
|
||
|
// The Init() function is called by FindOrCreate()
|
||
|
// after the contructor has been invoked. This function
|
||
|
// must complete any initialization required.
|
||
|
//
|
||
|
// Member functions are called by FindOrCreate() in this order :
|
||
|
//
|
||
|
// SampleClass() // Constructor is called
|
||
|
// ExclusiveLock()
|
||
|
// Init()
|
||
|
// ExclusiveUnlock()
|
||
|
//
|
||
|
// All Cache Client code must be able to deal with objects
|
||
|
// returned from the cache which failed initialization for
|
||
|
// whatever reason.
|
||
|
//
|
||
|
BOOL Init( Constructor& constructor ) ;
|
||
|
|
||
|
//
|
||
|
// This function must return a reference to the
|
||
|
// Key value stored within the data.
|
||
|
//
|
||
|
Key& GetKey() ;
|
||
|
|
||
|
//
|
||
|
// Must return non-zero if the provided Key matches
|
||
|
// the one stored in the data .
|
||
|
//
|
||
|
int MatchKey( Key& ) ;
|
||
|
|
||
|
} ;
|
||
|
|
||
|
|
||
|
--*/
|
||
|
|
||
|
|
||
|
#ifndef _CACHE_H_
|
||
|
#define _CACHE_H_
|
||
|
|
||
|
#ifndef _ASSERT
|
||
|
#define _ASSERT(x) if(!(x)) DebugBreak() ; else
|
||
|
#endif
|
||
|
|
||
|
|
||
|
// 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)();
|
||
|
|
||
|
//
|
||
|
// cacheint.h includes all the necessary helper classes
|
||
|
// etc... that make the general cache engine work !
|
||
|
//
|
||
|
//
|
||
|
#include "cacheint.h"
|
||
|
|
||
|
//
|
||
|
// Utility class for those which wish to have pools of locks for
|
||
|
// their objects !
|
||
|
//
|
||
|
class CLockPool {
|
||
|
private :
|
||
|
|
||
|
//
|
||
|
// Array of locks to share amongst Xover data structures
|
||
|
//
|
||
|
_CACHELOCK m_locks[256] ;
|
||
|
|
||
|
//
|
||
|
//
|
||
|
//
|
||
|
long m_lockIndex ;
|
||
|
public :
|
||
|
|
||
|
_CACHELOCK* AllocateLock() {
|
||
|
return &m_locks[ DWORD(InterlockedIncrement( &m_lockIndex )) % 256 ] ;
|
||
|
}
|
||
|
} ;
|
||
|
|
||
|
//
|
||
|
// CacheCallback class - clients can pass an object derived from
|
||
|
// this into the Cache for functions which require some kind of user
|
||
|
// callback processing !
|
||
|
//
|
||
|
template < class Data >
|
||
|
class CacheCallback {
|
||
|
public :
|
||
|
virtual BOOL fRemoveCacheItem( Data& d ) {
|
||
|
return FALSE ;
|
||
|
}
|
||
|
} ;
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
//
|
||
|
// Call these functions to initialize the Cache Library
|
||
|
//
|
||
|
extern BOOL __stdcall CacheLibraryInit() ;
|
||
|
extern BOOL __stdcall CacheLibraryTerm() ;
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
template < class Data,
|
||
|
class Key,
|
||
|
class Constructor,
|
||
|
BOOL fAtomic = TRUE
|
||
|
>
|
||
|
class Cache : public CScheduleThread,
|
||
|
CacheTable {
|
||
|
private :
|
||
|
|
||
|
//
|
||
|
// Define a 'CACHEENTRY' object which holds all the
|
||
|
// necessary data for each object which is placed in the cache !
|
||
|
//
|
||
|
typedef CacheEntry< Data, Key, fAtomic > CACHEENTRY ;
|
||
|
|
||
|
//
|
||
|
// Define callback objects for Expunge Operations.
|
||
|
//
|
||
|
typedef CacheCallback< Data > CALLBACKOBJ;
|
||
|
|
||
|
//
|
||
|
// Is the 'Cache' initialized and in a valid state !
|
||
|
//
|
||
|
BOOL m_fValid ;
|
||
|
|
||
|
//
|
||
|
// A list of everything in the Cache, used for TTL processing
|
||
|
//
|
||
|
CacheList m_ExpireList ;
|
||
|
|
||
|
//
|
||
|
// A hash table we use to find things within the Cache
|
||
|
//
|
||
|
TFHash< CACHEENTRY, Key > 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 ;
|
||
|
|
||
|
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
|
||
|
) ;
|
||
|
|
||
|
|
||
|
//
|
||
|
// Function which can remove a specific item from the Cache
|
||
|
// or walk the Cache deleting all the expired items.
|
||
|
//
|
||
|
//
|
||
|
BOOL
|
||
|
ExpungeInternal(
|
||
|
const CACHEENTRY* pProtected = 0,
|
||
|
BOOL fDoCheap = FALSE,
|
||
|
Key* key=0,
|
||
|
CACHEENTRY* pData = 0
|
||
|
) ;
|
||
|
|
||
|
|
||
|
public :
|
||
|
|
||
|
//
|
||
|
// 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 through pDataOut no matter what !
|
||
|
//
|
||
|
BOOL
|
||
|
FindOrCreateInternal(
|
||
|
DWORD dwHash,
|
||
|
Key& key,
|
||
|
Constructor& constructor,
|
||
|
CRefPtr< Data >& pDataOut
|
||
|
) ;
|
||
|
|
||
|
|
||
|
|
||
|
//
|
||
|
// Constructor - cMax specifies the maximum number of entries
|
||
|
// we should hold in the cache.
|
||
|
//
|
||
|
Cache( ) ;
|
||
|
|
||
|
//
|
||
|
// Destructor - remove ourselves from schedule list before continuing !
|
||
|
//
|
||
|
~Cache() ;
|
||
|
|
||
|
//
|
||
|
// 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(
|
||
|
DWORD (*pfnHash)( const Key& ),
|
||
|
DWORD dwLifetimeSeconds,
|
||
|
DWORD cMaxInstances,
|
||
|
PSTOPHINT_FN pfnStopHint = NULL
|
||
|
) ;
|
||
|
|
||
|
//
|
||
|
// 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
|
||
|
Expunge(
|
||
|
Key* key = 0,
|
||
|
CACHEENTRY* pData = 0
|
||
|
) ;
|
||
|
|
||
|
//
|
||
|
// Function which can be passed a function pointer to determine
|
||
|
// exactly what items are to be removed from the Cache.
|
||
|
// if fForced == TRUE then items are removed from the Cache
|
||
|
// no matter what.
|
||
|
//
|
||
|
BOOL
|
||
|
ExpungeSpecific(
|
||
|
CALLBACKOBJ* pCallback,
|
||
|
BOOL fForced
|
||
|
) ;
|
||
|
|
||
|
|
||
|
|
||
|
|
||
|
//
|
||
|
// 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 !
|
||
|
//
|
||
|
BOOL
|
||
|
FindOrCreate(
|
||
|
Key& key,
|
||
|
Constructor& constructor,
|
||
|
CRefPtr< Data >& pDataOut
|
||
|
) ;
|
||
|
|
||
|
|
||
|
#ifdef DEBUG
|
||
|
|
||
|
static long s_cCreated ;
|
||
|
|
||
|
#endif
|
||
|
|
||
|
} ;
|
||
|
|
||
|
|
||
|
|
||
|
template < class Data,
|
||
|
class Key,
|
||
|
class Constructor,
|
||
|
BOOL fAtomic = TRUE
|
||
|
>
|
||
|
class MultiCache {
|
||
|
private :
|
||
|
|
||
|
//
|
||
|
// Define a 'CACHEENTRY' object which holds all the
|
||
|
// necessary data for each object which is placed in the cache !
|
||
|
//
|
||
|
typedef CacheEntry< Data, Key, fAtomic > CACHEENTRY ;
|
||
|
|
||
|
//
|
||
|
// Define callback objects for Expunge Operations.
|
||
|
//
|
||
|
typedef CacheCallback< Data > CALLBACKOBJ;
|
||
|
|
||
|
//
|
||
|
// Define the type for a single instance !
|
||
|
//
|
||
|
typedef Cache< Data, Key, Constructor, fAtomic > 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 !
|
||
|
//
|
||
|
DWORD (*m_pfnHash)( const Key& ) ;
|
||
|
|
||
|
//
|
||
|
// Return the correct cache instance to hold the selected piece of data !
|
||
|
//
|
||
|
//DWORD ChooseInstance( Key& k ) ;
|
||
|
DWORD ChooseInstance( DWORD dwHash ) ;
|
||
|
|
||
|
public :
|
||
|
|
||
|
//
|
||
|
// Constructor - cMax specifies the maximum number of entries
|
||
|
// we should hold in the cache.
|
||
|
//
|
||
|
MultiCache( ) ;
|
||
|
|
||
|
//
|
||
|
// Destructor - destroys are various sub cache's
|
||
|
//
|
||
|
~MultiCache() ;
|
||
|
|
||
|
//
|
||
|
// 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(
|
||
|
DWORD (*pfnHash)( const Key& ),
|
||
|
DWORD dwLifetimeSeconds,
|
||
|
DWORD cSubCaches,
|
||
|
DWORD cMaxElements,
|
||
|
PSTOPHINT_FN pfnStopHint = NULL
|
||
|
) ;
|
||
|
|
||
|
//
|
||
|
// 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
|
||
|
Expunge(
|
||
|
Key* key = 0,
|
||
|
CACHEENTRY* pData = 0
|
||
|
) ;
|
||
|
|
||
|
|
||
|
//
|
||
|
// Function which can be passed a function pointer to determine
|
||
|
// exactly what items are to be removed from the Cache.
|
||
|
// if fForced == TRUE then items are removed from the Cache
|
||
|
// no matter what.
|
||
|
//
|
||
|
BOOL
|
||
|
ExpungeSpecific(
|
||
|
CALLBACKOBJ* pCallback,
|
||
|
BOOL fForced
|
||
|
) ;
|
||
|
|
||
|
//
|
||
|
// 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 !
|
||
|
//
|
||
|
BOOL
|
||
|
FindOrCreate(
|
||
|
Key& key,
|
||
|
Constructor& constructor,
|
||
|
CRefPtr< Data >& pDataOut
|
||
|
) ;
|
||
|
|
||
|
//
|
||
|
// 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 !
|
||
|
//
|
||
|
BOOL
|
||
|
FindOrCreate(
|
||
|
Key& key,
|
||
|
DWORD dwHash,
|
||
|
Constructor& constructor,
|
||
|
CRefPtr< Data >& pDataOut
|
||
|
) ;
|
||
|
|
||
|
|
||
|
} ;
|
||
|
|
||
|
|
||
|
|
||
|
#include "cacheimp.h"
|
||
|
|
||
|
#endif // _CACHE_H_
|