442 lines
8 KiB
C++
442 lines
8 KiB
C++
/*++
|
|
|
|
CACHEMTI.H
|
|
|
|
This file contains the definitions of all the internal classes
|
|
used to implement the multithreaded cache.
|
|
|
|
--*/
|
|
|
|
|
|
|
|
#ifndef _CACHEMTI_H_
|
|
#define _CACHEMTI_H_
|
|
|
|
|
|
//
|
|
// This class defines all the information we keep about
|
|
// objects that we are holding in our cache.
|
|
//
|
|
//
|
|
class CacheState {
|
|
private :
|
|
//
|
|
// Signature for a piece of Cache state !
|
|
//
|
|
DWORD m_dwSignature ;
|
|
|
|
//
|
|
// No Copy construction !!!
|
|
//
|
|
CacheState( CacheState& ) ;
|
|
protected :
|
|
//
|
|
// Doubly linked list of CacheEntry BLOCKS !
|
|
//
|
|
// This list is kept ordered by TTL, the newest blocks are at the end !
|
|
//
|
|
class CacheState* m_pPrev ;
|
|
class CacheState* m_pNext ;
|
|
|
|
//
|
|
// Cache State information -
|
|
// the time the item was last touched, as well as orphanage info.
|
|
//
|
|
FILETIME m_LastAccess ;
|
|
|
|
BOOL m_fOrphan ;
|
|
|
|
//
|
|
// Insert us into the list after the specified element !
|
|
//
|
|
void
|
|
InsertAfter(
|
|
class CacheState* pPrevious
|
|
) ;
|
|
|
|
//
|
|
// Insert us into the circular list before the specified element !
|
|
//
|
|
void
|
|
InsertBefore(
|
|
class CacheState* pNext
|
|
) ;
|
|
|
|
//
|
|
// Remove this element from the circular list !
|
|
//
|
|
void
|
|
Remove() ;
|
|
|
|
//
|
|
// The CacheList object maintains a list of these objects and ages them
|
|
// out !
|
|
//
|
|
friend class CacheList ;
|
|
|
|
public :
|
|
|
|
//
|
|
// Default Constructor NULLS everything out !
|
|
//
|
|
CacheState( ) :
|
|
m_dwSignature( DWORD('atSC' ) ),
|
|
m_pPrev( 0 ),
|
|
m_pNext( 0 ),
|
|
m_fOrphan( FALSE ) {
|
|
#ifdef DEBUG
|
|
InterlockedIncrement( &s_cCreated ) ;
|
|
#endif
|
|
ZeroMemory( &m_LastAccess, sizeof( m_LastAccess ) ) ;
|
|
}
|
|
|
|
#if 0
|
|
//
|
|
// Copy Constructor relinks linked list ! we place ourselves in the linked list
|
|
// after the rhs element.
|
|
//
|
|
CacheState( CacheState& rhs ) :
|
|
m_pBucket( 0 ),
|
|
m_pPrev( 0 ),
|
|
m_pNext( 0 ),
|
|
m_LastAccess( rhs.m_LastAccess ),
|
|
m_fOrphan( rhs.m_fOrphan ) {
|
|
|
|
if( rhs.m_pPrev != 0 )
|
|
InsertAfter( &rhs ) ;
|
|
#ifdef DEBUG
|
|
InterlockedIncrement( &s_cCreated ) ;
|
|
#endif
|
|
}
|
|
#endif
|
|
|
|
//
|
|
// If we are in a linked list - the default destructor needs to remove us
|
|
// from that list.
|
|
//
|
|
virtual ~CacheState() ;
|
|
|
|
//
|
|
// Need a virtual function which we use to figure out when we can
|
|
// safely remove an entry from the Cache !!
|
|
//
|
|
virtual BOOL fDeletable() { return TRUE ; }
|
|
|
|
//
|
|
// Update the LastAccess time !
|
|
//
|
|
void
|
|
Stamp( ) {
|
|
|
|
GetSystemTimeAsFileTime( &m_LastAccess ) ;
|
|
|
|
}
|
|
|
|
//
|
|
// Compare to entries by comparing their LastAccess time's !
|
|
//
|
|
BOOL
|
|
operator <= ( CacheState& rhs ) {
|
|
return CompareFileTime( &m_LastAccess, &rhs.m_LastAccess ) <= 0 ;
|
|
}
|
|
|
|
//
|
|
// Compare to entries by comparing their LastAccess time's !
|
|
//
|
|
BOOL
|
|
operator >= ( CacheState& rhs ) {
|
|
return CompareFileTime( &m_LastAccess, &rhs.m_LastAccess ) >= 0 ;
|
|
}
|
|
|
|
//
|
|
// Compare to entries by comparing their LastAccess time's !
|
|
//
|
|
BOOL
|
|
operator > ( CacheState& rhs ) {
|
|
return CompareFileTime( &m_LastAccess, &rhs.m_LastAccess ) > 0 ;
|
|
}
|
|
|
|
//
|
|
// Tell us if this entry is older than the specified time !
|
|
//
|
|
BOOL
|
|
OlderThan( FILETIME filetime ) {
|
|
return CompareFileTime( &m_LastAccess, &filetime ) < 0 ;
|
|
}
|
|
|
|
//
|
|
// Checks that the object is in a legal state !
|
|
//
|
|
virtual BOOL
|
|
IsValid( ) ;
|
|
|
|
|
|
#ifdef DEBUG
|
|
|
|
static long s_cCreated ;
|
|
|
|
#endif
|
|
|
|
} ;
|
|
|
|
|
|
//
|
|
// This class defines a couple of virtual functions
|
|
// which are to be called from CacheList objects.
|
|
//
|
|
class CacheTable {
|
|
protected :
|
|
|
|
friend class CacheList ;
|
|
|
|
//
|
|
// Remove an entry from the Cache - return TRUE if
|
|
// successful and pEntry destroyed.
|
|
//
|
|
virtual void RemoveEntry(
|
|
CacheState* pEntry
|
|
) ;
|
|
} ;
|
|
|
|
|
|
class CacheSelector : public CacheTable {
|
|
protected :
|
|
|
|
friend class CacheList ;
|
|
//
|
|
// Called to determine whether we want to remove a
|
|
// specified entry from the cache.
|
|
//
|
|
virtual BOOL QueryRemoveEntry(
|
|
CacheState* pEntry
|
|
) ;
|
|
|
|
} ;
|
|
|
|
template< class Data, class Key, BOOL fAtomic = TRUE >
|
|
class TEntry : public CacheState {
|
|
private :
|
|
//
|
|
// No copy constructor allowed !
|
|
//
|
|
TEntry( TEntry& ) ;
|
|
public :
|
|
|
|
//
|
|
// For chaining in the hash table - this is our bucket pointer !
|
|
//
|
|
TEntry* m_pBucket ;
|
|
|
|
CRefPtr< Data > m_pData ;
|
|
|
|
//
|
|
// Default constructor just passes on TTL data to CacheState object
|
|
//
|
|
TEntry( Data* pData ) :
|
|
m_pBucket( 0 ),
|
|
m_pData( pData ) {
|
|
Stamp() ;
|
|
}
|
|
|
|
//
|
|
// We do not declare a destructor as the default compiler
|
|
// provided destructor works fine !
|
|
//
|
|
// ~CacheEntry() ;
|
|
|
|
//
|
|
// Return a reference to the Key data from the data portion !
|
|
//
|
|
void GetKey(Key &k) {
|
|
m_pData->GetKey(k) ;
|
|
}
|
|
|
|
//
|
|
// Return 0 if the provided Key matches that contained in the
|
|
// data block !
|
|
//
|
|
int MatchKey( Key& key ) {
|
|
return m_pData->MatchKey( key ) ;
|
|
}
|
|
|
|
//
|
|
// Determine whether we can remove the Data represented by this
|
|
// entry from the Cache. 'fAtomic' == TRUE means that we should
|
|
// only release entries from the cache when the cache is the
|
|
// only component holding a reference to the entry.
|
|
// This ensures that two pieces of 'Data' which are located
|
|
// with the same 'Key' never exist at the same time.
|
|
//
|
|
BOOL fDeleteable() {
|
|
if( !fAtomic ) {
|
|
return TRUE ;
|
|
}
|
|
return m_pData->m_refs == 0 ;
|
|
}
|
|
|
|
BOOL
|
|
IsValid() {
|
|
|
|
if( m_pData == 0 )
|
|
return FALSE ;
|
|
|
|
return CacheState::IsValid() ;
|
|
|
|
}
|
|
} ;
|
|
|
|
|
|
class CacheList {
|
|
private :
|
|
|
|
//
|
|
// Keep this element around so we can keep a doubly linked list
|
|
// of the elements in the Cache List.
|
|
//
|
|
CacheState m_Head ;
|
|
|
|
//
|
|
// Make this private - nobody gets to copy us !
|
|
//
|
|
CacheList( CacheList& ) ;
|
|
|
|
protected :
|
|
|
|
|
|
//
|
|
// Number of Elements in the Cache !
|
|
//
|
|
long m_cEntries ;
|
|
|
|
public :
|
|
|
|
//
|
|
// Maximum number of Elements in the List !
|
|
//
|
|
long m_cMax ;
|
|
|
|
//
|
|
// Stop hint function to be called during long shutdown loops.
|
|
//
|
|
PSTOPHINT_FN m_pfnStopHint;
|
|
|
|
//
|
|
// Initialize the Cache list !
|
|
//
|
|
CacheList( long Max = 128 ) ;
|
|
|
|
#ifdef DEBUG
|
|
//
|
|
// In debug builds the destructor helps track how many of these
|
|
// are created !!
|
|
//
|
|
~CacheList() ;
|
|
#endif
|
|
|
|
//
|
|
// Append an Entry into the CacheList. We Append as this element
|
|
// should have the largest Age and most likely to be the last element expired !
|
|
//
|
|
// Returns TRUE if the number of entries is larger than our pre-ordained Maximum !
|
|
//
|
|
BOOL
|
|
Append( const CacheState* pEntry ) ;
|
|
|
|
|
|
//
|
|
// Remove an arbitrary Entry from the CacheList !
|
|
//
|
|
// Returns TRUE if the number of remaining entries is larger than our pre-ordained Maximum !
|
|
//
|
|
BOOL
|
|
Remove( const CacheState* pEntry ) ;
|
|
|
|
//
|
|
// For users who destroy elements of the list, causing the
|
|
// destructors to unlink elements and therefore skip Remove() -
|
|
// call this so that our count of elements stays in sync.
|
|
//
|
|
void
|
|
DecrementCount() {
|
|
m_cEntries-- ;
|
|
}
|
|
|
|
//
|
|
// Move an element to the end of the list.
|
|
//
|
|
void
|
|
MoveToBack(
|
|
CacheState* pEntry
|
|
) ;
|
|
|
|
|
|
//
|
|
// Walk the list and Expire any entries in there !
|
|
// This means we decrement TTL's and count up the number of
|
|
// entries in the list we could drop right now !
|
|
//
|
|
BOOL
|
|
Expire( DWORD& cReady ) ;
|
|
|
|
//
|
|
// Go find those elements who's TTL has dropped below 0 and
|
|
// get rid of them from the cache !!
|
|
//
|
|
// NOTE - if fDoCheap is set to TRUE then we use m_cReadyToDie
|
|
// to figure out if there's any work we can do !
|
|
//
|
|
BOOL
|
|
Expunge( CacheTable* ptable,
|
|
FILETIME* filetimeExpire,
|
|
DWORD& cExpunged,
|
|
BOOL fDoCheap = FALSE,
|
|
const CacheState* pProtected = 0
|
|
) ;
|
|
|
|
//
|
|
// Go remove a single element who's TTL haven't dropped below 0
|
|
// and get rid of it from the Cache !
|
|
//
|
|
BOOL
|
|
ForceExpunge( CacheTable* ptable,
|
|
const CacheState* pProtected = 0
|
|
) ;
|
|
|
|
//
|
|
// Go find those elements who's TTL has dropped below 0 and
|
|
// get rid of them from the cache !!
|
|
//
|
|
BOOL
|
|
ExpungeSpecific( CacheSelector* ptable,
|
|
BOOL fForced,
|
|
DWORD &cExpunged
|
|
) ;
|
|
|
|
//
|
|
// Check to see whether the expiration list is in a valid state !
|
|
//
|
|
BOOL
|
|
IsValid() ;
|
|
|
|
#if 0
|
|
//
|
|
// Bump the life time of the specified object up,
|
|
// as somebody is re-using it from the cache !
|
|
//
|
|
void
|
|
LiveLonger( CacheState* pEntry,
|
|
long ttl
|
|
) ;
|
|
#endif
|
|
|
|
#ifdef DEBUG
|
|
|
|
static long s_cCreated ;
|
|
|
|
#endif
|
|
|
|
} ;
|
|
|
|
|
|
#endif // _CACHEMTI_H_
|