windows-nt/Source/XPSP1/NT/inetsrv/iis/iisrearc/iisplus/w3cache/usercache.cxx
2020-09-26 16:20:57 +08:00

1017 lines
18 KiB
C++

/*++
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
)
{
_cRefs = 1;
_fFlushed = FALSE;
_fCached = FALSE;
_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(
VOID
)
/*++
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
{
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;
}
OBJECT_CACHE::~OBJECT_CACHE(
VOID
)
{
if ( _hTimer != NULL )
{
DeleteTimerQueueTimer( NULL,
_hTimer,
INVALID_HANDLE_VALUE );
_hTimer = NULL;
}
//
// So why do I set the free sig here instead of at the top? Because
// waiting for the timer queue to go away may cause a timer completion
// to fire and we don't want an assert there.
//
_dwSignature = OBJECT_CACHE_SIGNATURE_FREE;
if ( _pHintManager != NULL )
{
delete _pHintManager;
_pHintManager = NULL;
}
if ( _pTraceLog != NULL )
{
DestroyRefTraceLog( _pTraceLog );
_pTraceLog = NULL;
}
}
//static
VOID
WINAPI
OBJECT_CACHE::ScavengerCallback(
PVOID pParam,
BOOLEAN TimerOrWaitFired
)
{
OBJECT_CACHE * pObjectCache;
pObjectCache = (OBJECT_CACHE*) pParam;
DBG_ASSERT( pObjectCache != NULL );
DBG_ASSERT( pObjectCache->CheckSignature() );
pObjectCache->FlushByTTL();
}
//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
--*/
{
OBJECT_CACHE * pCache = (OBJECT_CACHE*) pvState;
DBG_ASSERT( pCache != NULL );
DBG_ASSERT( pCache->CheckSignature() );
DBG_ASSERT( pCacheEntry != NULL );
DBG_ASSERT( pCacheEntry->CheckSignature() );
if ( pCacheEntry->QueryIsOkToFlushTTL() )
{
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::FlushByTTL(
VOID
)
/*++
Routine Description:
Flush any inactive cache entries who have outlived they TTL
Arguments:
None
Return Value:
None
--*/
{
IncFlushCalls();
//
// Iterate the hash table, deleting expired itmes
//
_hashTable.DeleteIf( CacheFlushByTTL, this );
}
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
//
DBG_ASSERT( cmsecScavengeTime != 0 );
_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;
HRESULT hr;
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 );
lkrc = _hashTable.InsertRecord( pCacheEntry );
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;
}