|
|
/*++
Copyright (c) 1999 Microsoft Corporation
Module Name : usercache.cxx
Abstract: Implements the common code shared by all the caches (file, meta, uri, token, ul-cached-response, etc) Author: Bilal Alam (balam) 11-Nov-2000
Environment: Win32 - User Mode
Project: ULW3.DLL --*/
#include "precomp.hxx"
#include "usercache.hxx"
#include "cachehint.hxx"
//static
void CACHE_ENTRY_HASH::AddRefRecord( CACHE_ENTRY * pCacheEntry, int nIncr ) /*++
Routine Description:
Dereference and possibly delete the cache entry. Note the destructor for the cache entry is private. Only this code should ever delete the cache entry
Arguments:
None
Return Value:
None
--*/ { DBG_ASSERT( pCacheEntry->CheckSignature() ); if ( nIncr == +1 ) { pCacheEntry->ReferenceCacheEntry(); } else if ( nIncr == -1 ) { pCacheEntry->SetFlushed(); pCacheEntry->QueryCache()->IncFlushes(); pCacheEntry->QueryCache()->DecEntriesCached(); pCacheEntry->DereferenceCacheEntry(); } else { DBG_ASSERT( FALSE ); } }
//
// CACHE_ENTRY definitions
//
CACHE_ENTRY::CACHE_ENTRY( OBJECT_CACHE * pObjectCache, DWORD cTTLOverride ) { _cRefs = 1; _fFlushed = FALSE; _fCached = FALSE; if ( cTTLOverride != 0 ) { _cConfiguredTTL = cTTLOverride; } else { _cConfiguredTTL = pObjectCache->QueryConfiguredTTL(); } _cTTL = _cConfiguredTTL; _pObjectCache = pObjectCache; _pDirmonInvalidator = NULL; _dwSignature = CACHE_ENTRY_SIGNATURE; }
CACHE_ENTRY::~CACHE_ENTRY( VOID ) { DBG_ASSERT( _cRefs == 0 );
if ( _pDirmonInvalidator != NULL ) { _pDirmonInvalidator->Release(); _pDirmonInvalidator = NULL; } if ( _fFlushed ) { DBG_ASSERT( QueryCache() != NULL );
QueryCache()->DecActiveFlushedEntries(); } _dwSignature = CACHE_ENTRY_SIGNATURE_FREE; }
VOID CACHE_ENTRY::ReferenceCacheEntry( VOID ) /*++
Routine Description:
Reference the given entry (duh)
Arguments:
None
Return Value:
None
--*/ { LONG cRefs; cRefs = InterlockedIncrement( &_cRefs ); DBG_ASSERT( QueryCache() != NULL ); QueryCache()->DoReferenceTrace( this, cRefs ); }
VOID CACHE_ENTRY::DereferenceCacheEntry( VOID ) /*++
Routine Description:
Dereference and possibly delete the cache entry. Note the destructor for the cache entry is private. Only this code should ever delete the cache entry
Arguments:
None
Return Value:
None
--*/ { LONG cRefs; OBJECT_CACHE * pObjectCache = QueryCache(); DBG_ASSERT( pObjectCache != NULL ); cRefs = InterlockedDecrement( &_cRefs );
pObjectCache->DoReferenceTrace( this, cRefs ); if ( cRefs == 0 ) { delete this; } }
HRESULT CACHE_ENTRY::AddDirmonInvalidator( DIRMON_CONFIG * pDirmonConfig ) /*++
Routine Description:
Setup dirmon invalidation for this cache entry
Arguments:
pDirmonConfig - path/token for use in monitoring directory
Return Value:
HRESULT
--*/ { CDirMonitorEntry * pDME = NULL; HRESULT hr; hr = QueryCache()->AddDirmonInvalidator( pDirmonConfig, &pDME ); if ( FAILED( hr ) ) { return hr; } DBG_ASSERT( pDME != NULL );
//
// Cleanup any old dir monitor entry
//
if ( _pDirmonInvalidator != NULL ) { _pDirmonInvalidator->Release(); } _pDirmonInvalidator = pDME; return NO_ERROR; }
BOOL CACHE_ENTRY::Checkout( CACHE_USER * ) /*++
Routine Description:
Checkout a cache entry Arguments:
None
Return Value:
TRUE if the checkout was successful, else FALSE
--*/ { ReferenceCacheEntry(); if ( QueryIsFlushed() ) { DereferenceCacheEntry(); QueryCache()->IncMisses(); return FALSE; } else { InterlockedExchange( &_cTTL, _cConfiguredTTL ); QueryCache()->IncHits(); return TRUE; } }
BOOL CACHE_ENTRY::QueryIsOkToFlushTTL( VOID ) /*++
Routine Description: Called when the cache scavenger is invoked. This routine returns whether it is OK to flush this entry due to TTL
Arguments:
None
Return Value:
TRUE if it is OK to flush by TTL, else FALSE
--*/ { //
// Only do the TTL thing if the hash table holds the only reference to
// the cache entry. We can be loose with this check as this is just an
// optimization to prevent overzealous flushing
//
if ( _cRefs > 1 ) { return FALSE; } if ( InterlockedDecrement( &_cTTL ) == 0 ) { //
// TTL has expired. However, we let the cache entry override this
// expiry it wants to. Check that now
//
//
// Entry be gone!
//
return TRUE; } else { return FALSE; } }
BOOL CACHE_ENTRY::QueryIsOkToFlushMetadata( WCHAR * pszMetaPath, DWORD cchMetaPath ) /*++
Routine Description:
Called whem metadata has changed. This routine returns whether the current cache entry should be flushed
Arguments:
pszMetaPath - Metabase path which changed cchMetaPath - Size of metabase path
Return Value:
TRUE if we should flush
--*/ { BOOL fRet;
DBG_ASSERT( pszMetaPath != NULL ); DBG_ASSERT( cchMetaPath != 0 ); if ( pszMetaPath[ cchMetaPath - 1 ] == L'/' ) { cchMetaPath--; } DBG_ASSERT( QueryCache()->QuerySupportsMetadataFlush() );
if ( QueryMetadataPath() == NULL ) { fRet = TRUE; } else if ( QueryMetadataPath()->QueryCCH() < cchMetaPath ) { fRet = FALSE; } else if ( _wcsnicmp( QueryMetadataPath()->QueryStr(), pszMetaPath, cchMetaPath ) == 0 ) { fRet = TRUE; } else { fRet = FALSE; } return fRet; }
//
// OBJECT_CACHE definitions
//
OBJECT_CACHE::OBJECT_CACHE( VOID ) /*++
Routine Description:
Create an object cache. Obviously all the app-specific goo will be initialized in the derived class
Arguments:
None
Return Value:
None
--*/ { _hTimer = NULL; _pHintManager = NULL; _cmsecScavengeTime = 0; _cmsecTTL = 0; _dwSupportedInvalidation = 0;
_cCacheHits = 0; _cCacheMisses = 0; _cCacheFlushes = 0; _cActiveFlushedEntries = 0; _cFlushCalls = 0; _cEntriesCached = 0; _cTotalEntriesCached = 0;
_cPerfCacheHits = 0; _cPerfCacheMisses = 0; _cPerfCacheFlushes = 0; _cPerfFlushCalls = 0; _cPerfTotalEntriesCached = 0; #if DBG
_pTraceLog = CreateRefTraceLog( 2000, 0 ); #else
_pTraceLog = NULL; #endif
InitializeListHead( &_listEntry );
_dwSignature = OBJECT_CACHE_SIGNATURE; }
VOID OBJECT_CACHE::UnregisterScavenger( VOID ) { if ( _hTimer != NULL ) { DeleteTimerQueueTimer( NULL, _hTimer, INVALID_HANDLE_VALUE ); _hTimer = NULL; } } OBJECT_CACHE::~OBJECT_CACHE( VOID ) { _dwSignature = OBJECT_CACHE_SIGNATURE_FREE;
UnregisterScavenger(); if ( _pHintManager != NULL ) { delete _pHintManager; _pHintManager = NULL; } if ( _pTraceLog != NULL ) { DestroyRefTraceLog( _pTraceLog ); _pTraceLog = NULL; } }
VOID OBJECT_CACHE::CleanupCacheEntryListItems( LIST_ENTRY * pListHead ) { CACHE_ENTRY * pCacheEntry;
//
// Free cache entry list items
//
while ( !IsListEmpty( pListHead ) ) { pCacheEntry = CONTAINING_RECORD( pListHead->Flink, CACHE_ENTRY, _listEntry );
RemoveEntryList( &pCacheEntry->_listEntry ); pCacheEntry->_listEntry.Flink = NULL;
pCacheEntry->DereferenceCacheEntry(); } }
//static
VOID WINAPI OBJECT_CACHE::ScavengerCallback( PVOID pParam, BOOLEAN ) { OBJECT_CACHE * pObjectCache; LIST_ENTRY listHead; pObjectCache = (OBJECT_CACHE*) pParam; DBG_ASSERT( pObjectCache != NULL ); DBG_ASSERT( pObjectCache->CheckSignature() ); InitializeListHead( &listHead);
pObjectCache->FlushByTTL( &listHead ); //
// Remove all cache entries in the cache entry list
//
pObjectCache->CleanupCacheEntryListItems( &listHead ); }
//static
LK_PREDICATE OBJECT_CACHE::CacheFlushByRegChange( CACHE_ENTRY * pCacheEntry, VOID * pvState ) /*++
Routine Description:
Determine whether given entry should be deleted due to TTL change.
Arguments:
pCacheEntry - Cache entry to check pvState - Pointer to cache
Return Value:
LKP_PERFORM - do the delete, LKP_NO_ACTION - do nothing
--*/ { LIST_ENTRY * pListHead = static_cast<LIST_ENTRY *>( pvState ); DBG_ASSERT( pCacheEntry != NULL ); DBG_ASSERT( pCacheEntry->CheckSignature() ); pCacheEntry->ReferenceCacheEntry(); InsertHeadList( pListHead, &pCacheEntry->_listEntry );
return LKP_PERFORM; }
//static
LK_PREDICATE OBJECT_CACHE::CacheFlushByTTL( CACHE_ENTRY * pCacheEntry, VOID * pvState ) /*++
Routine Description:
Determine whether given entry should be deleted due to TTL
Arguments:
pCacheEntry - Cache entry to check pvState - Pointer to cache
Return Value:
LKP_PERFORM - do the delete, LKP_NO_ACTION - do nothing
--*/ { LIST_ENTRY * pListHead = static_cast<LIST_ENTRY *>( pvState ); DBG_ASSERT( pCacheEntry != NULL ); DBG_ASSERT( pCacheEntry->CheckSignature() ); if ( pCacheEntry->QueryIsOkToFlushTTL() ) { pCacheEntry->ReferenceCacheEntry(); InsertHeadList( pListHead, &pCacheEntry->_listEntry );
return LKP_PERFORM; } else { return LKP_NO_ACTION; } }
//static
LK_PREDICATE OBJECT_CACHE::CacheFlushByDirmon( CACHE_ENTRY * pCacheEntry, VOID * pvState ) /*++
Routine Description:
Determine whether given entry should be deleted due to dir mon
Arguments:
pCacheEntry - Cache entry to check pvState - STRU of path which changed
Return Value:
LKP_PERFORM - do the delete, LKP_NO_ACTION - do nothing
--*/ { STRU * pstrPath = (STRU*) pvState; OBJECT_CACHE * pCache; DBG_ASSERT( pCacheEntry != NULL ); DBG_ASSERT( pCacheEntry->CheckSignature() ); pCache = pCacheEntry->QueryCache(); DBG_ASSERT( pCache->CheckSignature() ); if ( pCacheEntry->QueryIsOkToFlushDirmon( pstrPath->QueryStr(), pstrPath->QueryCCH() ) ) { return LKP_PERFORM; } else { return LKP_NO_ACTION; } }
//static
LK_PREDICATE OBJECT_CACHE::CacheFlushByMetadata( CACHE_ENTRY * pCacheEntry, VOID * pvState ) /*++
Routine Description:
Determine whether given entry should be deleted due to metadata change
Arguments:
pCacheEntry - Cache entry to check pvState - STRU with metapath which changed
Return Value:
LKP_PERFORM - do the delete, LKP_NO_ACTION - do nothing
--*/ { STRU * pstrPath = (STRU*) pvState; OBJECT_CACHE * pCache; DBG_ASSERT( pCacheEntry != NULL ); DBG_ASSERT( pCacheEntry->CheckSignature() ); pCache = pCacheEntry->QueryCache(); DBG_ASSERT( pCache->CheckSignature() ); if ( pCacheEntry->QueryIsOkToFlushMetadata( pstrPath->QueryStr(), pstrPath->QueryCCH() ) ) { return LKP_PERFORM; } else { return LKP_NO_ACTION; } }
VOID OBJECT_CACHE::FlushByRegChange( LIST_ENTRY * pListHead ) /*++
Routine Description:
Flush any inactive cache entries who have outlived their TTL
Arguments:
pListHead - Head of the cache entry list to be deleted
Return Value:
None
--*/ { IncFlushCalls(); //
// Iterate the hash table, deleting expired itmes
//
_hashTable.DeleteIf( CacheFlushByRegChange, pListHead ); }
VOID OBJECT_CACHE::FlushByTTL( LIST_ENTRY * pListHead ) /*++
Routine Description:
Flush any inactive cache entries who have outlived their TTL
Arguments:
pListHead - Head of the cache entry list to be deleted
Return Value:
None
--*/ { IncFlushCalls(); //
// Iterate the hash table, deleting expired itmes
//
_hashTable.DeleteIf( CacheFlushByTTL, pListHead ); }
VOID OBJECT_CACHE::DoDirmonInvalidationFlush( WCHAR * pszPath ) /*++
Routine Description:
Flush all appropriate entries due to dirmon change notification
Arguments:
pszPath - Path that changed
Return Value:
None
--*/ { STACK_STRU( strPath, 256 ); IncFlushCalls(); if ( SUCCEEDED( strPath.Copy( pszPath ) ) ) { _hashTable.DeleteIf( CacheFlushByDirmon, (VOID*) &strPath ); } }
VOID OBJECT_CACHE::DoMetadataInvalidationFlush( WCHAR * pszMetaPath ) /*++
Routine Description:
Flush all appropriate entries due to metadata change notification
Arguments:
pszMetaPath - Metabase path which changed
Return Value:
None
--*/ { STACK_STRU( strPath, 256 ); IncFlushCalls(); if ( SUCCEEDED( strPath.Copy( pszMetaPath ) ) ) { _hashTable.DeleteIf( CacheFlushByMetadata, (VOID*) &strPath ); } }
HRESULT OBJECT_CACHE::SetCacheConfiguration( DWORD cmsecScavengeTime, DWORD cmsecTTL, DWORD dwSupportedInvalidation, CACHE_HINT_CONFIG * pCacheHintConfig ) /*++
Routine Description:
Do the general cache initialization here
Arguments:
cmsecScavengeTime - How often should a scavenger be run for this cache (should be no larger than the TTL expected for entries in this cache) cmsecTTL - TTL for entries in this cache dwSupportedInvalidation - How can the cache be invalidated? pCacheHintConfig - Cache hint configuration (NULL for no cache hints) Return Value: HRESULT
--*/ { BOOL fRet; HRESULT hr;
DBG_ASSERT( cmsecTTL >= cmsecScavengeTime ); //
// Create a timer which fires every cmsecScavengeTime
//
_cmsecScavengeTime = cmsecScavengeTime; fRet = CreateTimerQueueTimer( &_hTimer, NULL, OBJECT_CACHE::ScavengerCallback, this, _cmsecScavengeTime, _cmsecScavengeTime, WT_EXECUTELONGFUNCTION ); if ( !fRet ) { return HRESULT_FROM_WIN32( GetLastError() ); } _cmsecTTL = cmsecTTL; _dwSupportedInvalidation = dwSupportedInvalidation;
//
// Should we setup a cache hint table
//
if ( pCacheHintConfig != NULL ) { _pHintManager = new CACHE_HINT_MANAGER; if ( _pHintManager == NULL ) { hr = HRESULT_FROM_WIN32( GetLastError() ); DeleteTimerQueueTimer( NULL, _hTimer, INVALID_HANDLE_VALUE ); _hTimer = NULL; return hr; }
hr = _pHintManager->Initialize( pCacheHintConfig ); if ( FAILED( hr ) ) { delete _pHintManager; _pHintManager = NULL;
DeleteTimerQueueTimer( NULL, _hTimer, INVALID_HANDLE_VALUE ); _hTimer = NULL; return hr; } }
return NO_ERROR; }
HRESULT OBJECT_CACHE::FindCacheEntry( CACHE_KEY * pCacheKey, CACHE_ENTRY ** ppCacheEntry, BOOL * pfShouldCache ) /*++
Routine Description:
Lookup key in cache
Arguments:
pCacheKey - Cache key to lookup ppCacheEntry - Points to cache entry on success pfShouldCache - Provides a hint if possible on whether we should cache (can be NULL indicating no hint is needed)
Return Value:
HRESULT
--*/ { LK_RETCODE lkrc; HRESULT hr; CACHE_ENTRY * pCacheEntry = NULL; if ( ppCacheEntry == NULL || pCacheKey == NULL ) { DBG_ASSERT( FALSE ); return HRESULT_FROM_WIN32( ERROR_INVALID_PARAMETER ); } *ppCacheEntry = NULL;
//
// First do a lookup
//
lkrc = _hashTable.FindKey( pCacheKey, &pCacheEntry ); if ( lkrc == LK_SUCCESS ) { //
// If this entry has been flushed, then it really isn't a hit
//
if ( pCacheEntry->QueryIsFlushed() ) { pCacheEntry->DereferenceCacheEntry(); } else { IncHits(); DBG_ASSERT( pCacheEntry != NULL ); *ppCacheEntry = pCacheEntry; return NO_ERROR; } } IncMisses(); //
// Is a hint requested?
//
if ( pfShouldCache != NULL ) { *pfShouldCache = TRUE; if ( _pHintManager != NULL ) { hr = _pHintManager->ShouldCacheEntry( pCacheKey, pfShouldCache ); //
// Ignore error
//
} }
return HRESULT_FROM_WIN32( ERROR_FILE_NOT_FOUND ); }
HRESULT OBJECT_CACHE::FlushCacheEntry( CACHE_KEY * pCacheKey ) /*++
Routine Description:
Flush given cache key
Arguments:
pCacheKey - Key to flush
Return Value:
HRESULT
--*/ { LK_RETCODE lkrc; CACHE_ENTRY * pCacheEntry; if ( pCacheKey == NULL ) { DBG_ASSERT( FALSE ); return HRESULT_FROM_WIN32( ERROR_INVALID_PARAMETER ); } //
// First do a lookup
//
lkrc = _hashTable.FindKey( pCacheKey, &pCacheEntry ); if ( lkrc == LK_SUCCESS ) { DBG_ASSERT( pCacheEntry != NULL ); if ( !pCacheEntry->QueryIsFlushed() ) { _hashTable.DeleteRecord( pCacheEntry ); } pCacheEntry->DereferenceCacheEntry(); } return NO_ERROR; }
HRESULT OBJECT_CACHE::AddCacheEntry( CACHE_ENTRY * pCacheEntry ) /*++
Routine Description:
Lookup key in cache
Arguments:
pCacheEntry - Points to cache entry on success
Return Value:
HRESULT
--*/ { LK_RETCODE lkrc; if ( pCacheEntry == NULL ) { DBG_ASSERT( FALSE ); return HRESULT_FROM_WIN32( ERROR_INVALID_PARAMETER ); } DBG_ASSERT( pCacheEntry->QueryCached() == FALSE ); //
// Now try to insert into hash table
//
pCacheEntry->SetCached( TRUE );
//
// Replace a stale entry, if present
//
lkrc = _hashTable.InsertRecord( pCacheEntry, TRUE ); if ( lkrc == LK_SUCCESS ) { IncEntriesCached(); } else { pCacheEntry->SetCached( FALSE ); }
return NO_ERROR; }
HRESULT OBJECT_CACHE::AddDirmonInvalidator( DIRMON_CONFIG * pDirmonConfig, CDirMonitorEntry ** ppDME ) /*++
Routine Description:
Add dirmon invalidator for this cache
Arguments:
pDirmonConfig - Configuration of dir monitor ppDME - filled with dir monitor entry to be attached to cache entry
Return Value:
HRESULT
--*/ { HRESULT hr = NO_ERROR; if ( !QuerySupportsDirmonSpecific() && !QuerySupportsDirmonFlush() ) { return HRESULT_FROM_WIN32( ERROR_NOT_SUPPORTED ); } //
// Start monitoring
//
DBG_ASSERT( g_pCacheManager != NULL ); hr = g_pCacheManager->MonitorDirectory( pDirmonConfig, ppDME ); if ( FAILED( hr ) ) { return hr; } DBG_ASSERT( *ppDME != NULL ); return NO_ERROR; }
|