windows-nt/Source/XPSP1/NT/inetsrv/iis/staxinc/cacheimp.h
2020-09-26 16:20:57 +08:00

937 lines
16 KiB
C++

/*++
cacheimp.h -
This file contains all the template function definitions required
to make the cache manager work.
--*/
template < class Data,
class Key,
class Constructor,
BOOL fAtomic
>
void
Cache< Data, Key, Constructor, fAtomic >::Schedule() {
/*++
Routine Description :
This function runs through all the items in the Cache
bumping TTL's. If there are any items ready to go
then we dump them from the Cache.
Arguments :
None.
Return Value :
Nothing
--*/
if( !m_fValid )
return ;
m_Lock.ShareLock() ;
DWORD cExpungable = 0 ;
BOOL fExpunge = m_ExpireList.Expire( cExpungable ) ;
m_Lock.ShareUnlock() ;
if( fExpunge ) {
Expunge() ;
}
}
template < class Data,
class Key,
class Constructor,
BOOL fAtomic
>
BOOL
Cache<Data, Key, Constructor, fAtomic>::RemoveEntry(
CacheState* pEntry
) {
/*++
Routine Description :
This function removes an entry from the Cache.
We call our hash table to delete the item.
the CacheState destructor automagically removes
the item from our linked lists.
CALLER MUST HOLD APPROPRIATE LOCKS!
Arguments :
pEntry - item to be removed from cache
Return Value :
TRUE if successfully removed !
--*/
CACHEENTRY *pCacheEntry = (CACHEENTRY*)pEntry ;
return m_Lookup.DeleteData( pCacheEntry->GetKey(), pCacheEntry ) ;
}
template < class Data,
class Key,
class Constructor,
BOOL fAtomic
>
BOOL
Cache<Data, Key, Constructor, fAtomic>::QueryRemoveEntry(
CacheState* pEntry ) {
/*++
Routine Description :
This function is called from CacheList object to
determine whether we want to remove an item from the Cache.
This function is used to implement the ExpungeSpecific
function available to clients.
CALLER MUST HOLD APPROPRIATE LOCKS!
Arguments :
pEntry - item we want to determine whether it should remain !
Return Value :
TRUE if successfully removed !
--*/
CACHEENTRY *pCacheEntry = (CACHEENTRY*) pEntry ;
if( m_pCallbackObject ) {
return m_pCallbackObject->fRemoveCacheItem( *pCacheEntry->m_pData ) ;
}
return FALSE ;
}
template < class Data,
class Key,
class Constructor,
BOOL fAtomic
>
BOOL
Cache<Data, Key, Constructor, fAtomic>::ExpungeInternal(
const CACHEENTRY* pProtected,
BOOL fDoCheap,
Key* key,
CACHEENTRY* pData
) {
/*++
Routine Description :
This function is called when we want to force some thing
out of the cache. The caller can provide a key
to force a particular item out of the cache.
CALLER MUST HOLD APPROPRIATE LOCKS!
Arguments :
pProtected - an Element we want to make sure is not removed !
key - Pointer to the key identifying the item to be removed
pData - pointer to the CACHEENTRY object containing the data
and key we wish to remove.
Return Value :
TRUE something is successfully removed from the cache.
--*/
if( key != 0 ) {
return m_Lookup.DeleteData( *key, pData ) ;
}
return m_ExpireList.Expunge( this, fDoCheap, pProtected ) ;
}
#ifdef DEBUG
template < class Data,
class Key,
class Constructor,
BOOL fAtomic
>
long Cache<Data, Key, Constructor, fAtomic>::s_cCreated = 0 ;
#endif
template < class Data,
class Key,
class Constructor,
BOOL fAtomic
>
Cache<Data, Key, Constructor, fAtomic>::Cache( ) : m_fValid( FALSE ) {
/*++
Routine Description :
This function initializes our member variables.
Arguments :
cMax - maximum number of elements the cache should hold
Return Value :
Nothing
--*/
#ifdef DEBUG
InterlockedIncrement( &s_cCreated ) ;
#endif
AddToSchedule() ;
}
template < class Data,
class Key,
class Constructor,
BOOL fAtomic
>
Cache<Data, Key, Constructor, fAtomic>::~Cache( ) {
/*++
Routine Description :
This function destroys a Cache object !
Arguments :
None
Return Value :
Nothing
--*/
RemoveFromSchedule() ;
//
// Member and Base class destruction follows !!
//
#ifdef DEBUG
InterlockedDecrement( &s_cCreated ) ;
#endif
}
template < class Data,
class Key,
class Constructor,
BOOL fAtomic
>
BOOL
Cache<Data, Key, Constructor, fAtomic>::Init(
DWORD (*pfnHash)( const Key& ),
DWORD dwLifetimeSeconds,
DWORD cMaxInstances,
PSTOPHINT_FN pfnStopHint
) {
/*++
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_ExpireList.m_cMax = long(cMaxInstances) ;
m_ExpireList.m_pfnStopHint = pfnStopHint;
m_TTL = 1 + (dwLifetimeSeconds / CScheduleThread::dwNotificationSeconds) ;
return m_fValid = m_Lookup.Init( 100, 100, pfnHash ) ;
}
template < class Data,
class Key,
class Constructor,
BOOL fAtomic
>
BOOL
Cache<Data, Key, Constructor, fAtomic>::Expunge(
Key* key,
CACHEENTRY* pData
) {
/*++
Routine Description :
This function is called when we want to force some thing
out of the cache. The caller can provide a key
to force a particular item out of the cache.
WE WILL GRAB THE NECESSARY LOCKS !
Arguments :
pfnHash - function to be used to compute hash values on keys
Return Value :
TRUE if successfull
--*/
_ASSERT( m_fValid ) ;
m_Lock.ExclusiveLock() ;
BOOL fReturn = ExpungeInternal( 0, FALSE, key, pData ) ;
m_Lock.ExclusiveUnlock() ;
return fReturn ;
}
template < class Data,
class Key,
class Constructor,
BOOL fAtomic
>
BOOL
Cache<Data, Key, Constructor, fAtomic>::ExpungeSpecific(
CALLBACKOBJ* pCallbackObject,
BOOL fForced
) {
/*++
Routine Description :
This function is called when we want to force some thing
out of the cache. The caller can provide a key
to force a particular item out of the cache.
WE WILL GRAB THE NECESSARY LOCKS !
Arguments :
pfn - callback function used to determine what to remove
from the Cache.
fForced - if TRUE then we will remove from the Cache no matter
what, even if other threads are using the object !
Return Value :
TRUE if successfull
--*/
m_Lock.ExclusiveLock() ;
m_pCallbackObject = pCallbackObject ;
BOOL fReturn = m_ExpireList.ExpungeSpecific( this, fForced ) ;
m_Lock.ExclusiveUnlock() ;
return fReturn ;
}
template < class Data,
class Key,
class Constructor,
BOOL fAtomic
>
BOOL
Cache<Data, Key, Constructor, fAtomic>::FindOrCreateInternal(
DWORD dwHash,
Key& key,
Constructor& constructor,
CRefPtr< Data >& pDataOut
) {
/*++
Routine Description :
This function is called when we want something out
of the Cache. We will either find the object or create it.
WE WILL GRAB THE NECESSARY LOCKS !
Arguments :
key - The unique key used to find the item in the Cache.
constructor - An object to pass to the Data's constructor and Init
functions.
pDataOut - Smart pointer which receives the result
Return Value :
TRUE if successfull
--*/
BOOL fReturn = FALSE ;
pDataOut = 0 ;
if( !m_fValid ) {
SetLastError( 0 ) ;
return FALSE ;
} else {
m_Lock.ShareLock() ;
//
// See if we can find an Entry in the cache for the item we want.
//
CACHEENTRY *pEntry = m_Lookup.SearchKeyHash( dwHash, key ) ;
if( pEntry ) {
pDataOut = pEntry->m_pData ;
fReturn = TRUE ;
m_ExpireList.LiveLonger( pEntry, (m_TTL/2)+1 ) ;
}
m_Lock.ShareUnlock() ;
if( pDataOut == 0 ) {
m_Lock.ExclusiveLock() ;
//
// Check that the Item we want in the Cache didn't make it
// into the Cache in the brief moment we switched from a shared
// to an exclusive lock.
//
pEntry = m_Lookup.SearchKeyHash( dwHash, key ) ;
if( pEntry != 0 ) {
//
// It's in the cache - return the cached object !
//
pDataOut = pEntry->m_pData ;
fReturn = TRUE ;
m_ExpireList.LiveLonger( pEntry, (m_TTL/2)+1 ) ;
m_Lock.ExclusiveUnlock() ;
} else {
//
// We're going to have to create one of the elements that goes
// in the cache. Let's do that now !
//
const CACHEENTRY *pCacheEntry = 0 ;
BOOL fOverFlow = FALSE ;
Data *pData = 0 ;
//
// We Add another brace here because we want to explicitly manage
// the lifetime of tempCacheEntry
//
{
//
// tempCacheEntry will be copied by the FHash template into
// another CACHEENTRY object when it does its insertion.
//
CACHEENTRY tempCacheEntry( m_TTL ) ;
//
// The constructor of the Data object is expected to initialize
// only the 'key' data of the Data object so that the data can be
// found in the Cache.
//
tempCacheEntry.m_pData = pData = new Data( key, constructor ) ;
if( tempCacheEntry.m_pData ) {
pCacheEntry = m_Lookup.InsertDataHash( dwHash, tempCacheEntry ) ;
if( pCacheEntry ) {
if( m_ExpireList.Append( pCacheEntry ) ) {
if( !ExpungeInternal( pCacheEntry, TRUE ) ) {
m_ExpireList.ForceExpunge( this, pCacheEntry ) ;
}
}
pDataOut = pCacheEntry->m_pData ;
pDataOut->ExclusiveLock() ;
} else {
//
// The Insertion failed !
// Falling through correctly handles this error case !
//
}
}
//
// Delete tempCacheEntry's reference to the Data object.
// if an error occurred inserting into hash tables etc...
// this will automatically destroy the data object.
//
tempCacheEntry.m_pData = 0 ;
}
//
// Release the cache's lock. We do this now - the constructor
// for the 'Data' object should do minimal work. We will not call
// the init function for the Data object, with the 'cache' locks
// relingquished. This lets others use the cache while expensive
// construction operations continue.
//
m_Lock.ExclusiveUnlock() ;
//
// Complete the initialization of the new Data item if it exists !
//
if( pDataOut != 0 ) {
BOOL fSuccess = pDataOut->Init( constructor ) ;
pDataOut->ExclusiveUnlock() ;
if( fSuccess ) {
fReturn = TRUE ;
} else {
pDataOut = 0 ;
//
// Need to Expire the entry we just placed in the Cache !
//
// NOTE : Expunge() manages its own locks !!!
//
Expunge( &key ) ;
}
}
}
}
}
return fReturn ;
}
template < class Data,
class Key,
class Constructor,
BOOL fAtomic
>
inline BOOL
Cache<Data, Key, Constructor, fAtomic>::FindOrCreate(
Key& key,
Constructor& constructor,
CRefPtr< Data >& pDataOut ) {
/*++
Routine Description :
This function is called when we want something out
of the Cache. We will either find the object or create it.
WE WILL GRAB THE NECESSARY LOCKS !
Arguments :
key - The unique key used to find the item in the Cache.
constructor - An object to pass to the Data's constructor and Init
functions.
pDataOut - Smart pointer which receives the result
Return Value :
TRUE if successfull
--*/
DWORD dw = m_pfnHash( key ) ;
return FindOrCreateInternal( dw,
key,
constructor,
pDataOut
) ;
}
template < class Data,
class Key,
class Constructor,
BOOL fAtomic
>
MultiCache< Data, Key, Constructor, fAtomic >::MultiCache() :
m_fValid( FALSE ),
m_pCaches( 0 ) ,
m_cSubCaches( 0 ),
m_pfnHash( 0 ) {
/*++
Routine Description :
This function initializes the MultiCache's data structures
Arguments :
None.
Return Value :
Nothing
--*/
}
template < class Data,
class Key,
class Constructor,
BOOL fAtomic
>
MultiCache< Data, Key, Constructor, fAtomic >::~MultiCache() {
/*++
Routine Description :
This function destroys all of our data structures - release
all of our subcaches !
Arguments :
None.
Return Value :
Nothing
--*/
if( m_pCaches ) {
delete[] m_pCaches ;
}
}
template < class Data,
class Key,
class Constructor,
BOOL fAtomic
>
BOOL
MultiCache< Data, Key, Constructor, fAtomic >::Init(
DWORD (*pfnHash)( const Key & ),
DWORD dwLifetimeSeconds,
DWORD cSubCaches,
DWORD cMaxElements,
PSTOPHINT_FN pfnStopHint) {
/*++
Routine Description :
This function initializes the MultiCache - we use
multiple independent Caches to split the work
of caching all the data.
Arguments :
None.
Return Value :
Nothing
--*/
//
// Check that we're in the right state for this !
//
_ASSERT( !m_fValid ) ;
_ASSERT( m_pCaches == 0 ) ;
//
// Validate our arguments !!!
//
_ASSERT( pfnHash != 0 ) ;
_ASSERT( dwLifetimeSeconds != 0 ) ;
_ASSERT( cSubCaches != 0 ) ;
_ASSERT( cMaxElements != 0 ) ;
m_pfnHash = pfnHash ;
m_cSubCaches = cSubCaches ;
//
// Allocate the necessary subcaches !
//
m_pCaches = new CACHEINSTANCE[m_cSubCaches] ;
if( !m_pCaches ) {
return FALSE ;
} else {
for( DWORD i=0; i<cSubCaches; i++ ) {
if( !m_pCaches[i].Init( m_pfnHash, dwLifetimeSeconds, (cMaxElements / cSubCaches) + 1, pfnStopHint ) ) {
delete m_pCaches ;
return FALSE ;
}
}
}
m_fValid = TRUE ;
return TRUE ;
}
template < class Data,
class Key,
class Constructor,
BOOL fAtomic
>
DWORD
MultiCache< Data, Key, Constructor, fAtomic >::ChooseInstance(
DWORD dwHash
) {
/*++
Routine Description :
Given a Key figure out which of our subinstances we wish to use.
Arguments :
None.
Return Value :
Nothing
--*/
//
// Check that we're in the right state for this !
//
_ASSERT( m_fValid ) ;
_ASSERT( m_pCaches != 0 ) ;
//
// Validate our arguments !!!
//
_ASSERT( m_pfnHash != 0 ) ;
_ASSERT( m_cSubCaches != 0 ) ;
//DWORD dwHash = m_pfnHash( k ) ;
//
// Constants below stolen from C-runtime rand() function !
//
dwHash = (((dwHash * 214013) +2531011) >> 8) % m_cSubCaches ;
return dwHash ;
}
template < class Data,
class Key,
class Constructor,
BOOL fAtomic
>
BOOL
MultiCache< Data, Key, Constructor, fAtomic >::Expunge(
Key* pk,
CACHEENTRY* pData
) {
/*++
Routine Description :
Either force a specified element out of the cache,
or force out all those that are ready to go !
Arguments :
None.
Return Value :
Nothing
--*/
if( pk != 0 ) {
DWORD dw = m_pfnHash( *pk ) ;
CACHEINSTANCE* pInstance = &m_pCacges[ChooseInstance( dw )] ;
return pInstance->Expunge( pk, pData ) ;
} else {
BOOL fReturn = TRUE ;
for( DWORD i=0; i<m_cSubCaches; i++ ) {
fReturn &= m_pCaches[i].Expunge() ;
}
}
return fReturn ;
}
template < class Data,
class Key,
class Constructor,
BOOL fAtomic
>
BOOL
MultiCache< Data, Key, Constructor, fAtomic >::ExpungeSpecific(
CALLBACKOBJ* pCallbackObject,
BOOL fForced
) {
/*++
Routine Description :
Either force a specified element out of the cache,
or force out all those that are ready to go !
Arguments :
None.
Return Value :
Nothing
--*/
BOOL fReturn = TRUE ;
for( DWORD i=0; i<m_cSubCaches; i++ ) {
fReturn &= m_pCaches[i].ExpungeSpecific( pCallbackObject, fForced ) ;
}
return fReturn ;
}
template < class Data,
class Key,
class Constructor,
BOOL fAtomic
>
BOOL
MultiCache< Data, Key, Constructor, fAtomic >::FindOrCreate(
Key& key,
Constructor& constructor,
CRefPtr< Data >& pDataOut
) {
/*++
Routine Description :
Arguments :
None.
Return Value :
Nothing
--*/
DWORD dw = m_pfnHash( key ) ;
CACHEINSTANCE* pInstance = &m_pCaches[ChooseInstance(dw)] ;
return pInstance->FindOrCreateInternal( dw, key, constructor, pDataOut ) ;
}
template < class Data,
class Key,
class Constructor,
BOOL fAtomic
>
BOOL
MultiCache< Data, Key, Constructor, fAtomic >::FindOrCreate(
Key& key,
DWORD dw,
Constructor& constructor,
CRefPtr< Data >& pDataOut
) {
/*++
Routine Description :
Arguments :
None.
Return Value :
Nothing
--*/
_ASSERT( dw == m_pfnHash( key ) ) ;
CACHEINSTANCE* pInstance = &m_pCaches[ChooseInstance(dw)] ;
return pInstance->FindOrCreateInternal( dw, key, constructor, pDataOut ) ;
}