Windows NT 4.0 source code leak
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

1058 lines
26 KiB

/*++
Copyright (c) 1995 Microsoft Corporation
Module Name :
cache.cxx
Abstract:
This module contains the tsunami caching routines
Author:
Murali R. Krishnan ( MuraliK ) 16-Jan-1995
--*/
#include "TsunamiP.Hxx"
#pragma hdrstop
#include <dbgutil.h>
//
// Items in a Bin list beyond this position will get moved to the front
// on an object cache hit
//
#define REORDER_LIST_THRESHOLD 5
//
// Current count of cached file handles across a UNC connection
//
DWORD cCachedUNCHandles = 0;
VOID
CheckCacheSize(
DWORD cbNewBlobSize
);
CACHE_TABLE CacheTable;
BOOL
Cache_Initialize(
IN DWORD CacheSize
)
{
int index;
//
// Initialize configuration block
//
ZeroMemory(&Configuration,sizeof( Configuration ));
Configuration.cbMaximumSize = CacheSize;
InitializeCriticalSection( &CacheTable.CriticalSection );
InitializeListHead( &CacheTable.MruList );
for ( index=0; index<MAX_BINS; index++ ) {
InitializeListHead( &CacheTable.Items[ index ] );
}
return( TRUE );
} // Cache_Initialize
BOOL
TsCacheDirectoryBlobA(
IN const TSVC_CACHE &TSvcCache,
IN PCSTR pszDirectoryName,
IN ULONG iDemultiplexor,
IN PVOID pvBlob,
IN ULONG cbBlobSize,
IN BOOLEAN bKeepCheckedOut
)
{
WCHAR awchPath[ MAX_PATH + 1 ];
DWORD cch;
BOOL bSuccess;
//
// Convert directory name to UNICODE.
//
cch = MultiByteToWideChar( CP_ACP,
MB_PRECOMPOSED,
pszDirectoryName,
-1,
awchPath,
sizeof( awchPath ) / sizeof(WCHAR) );
if ( !cch )
return FALSE;
bSuccess = TsCacheDirectoryBlobW( TSvcCache,
awchPath,
iDemultiplexor,
pvBlob,
cbBlobSize,
bKeepCheckedOut );
return( bSuccess );
} // TsCacheDirectoryBlobA
BOOL
TsCacheDirectoryBlobW(
IN const TSVC_CACHE &TSvcCache,
IN PCWSTR pwszDirectoryName,
IN ULONG iDemultiplexor,
IN PVOID pvBlob,
IN ULONG cbBlobSize,
IN BOOLEAN bKeepCheckedOut
)
/*++
Routine Description:
This function associates the Blob given as input with the specified
directory and demultiplexing number. Services should use this
function to add a Blob to the cache.
Callers must not cache the same Blob twice. Once a Blob is cached,
its contents must not be modified, and it must not be freed or re-cached.
Arguments
cbBlobSize is the externally allocated size of the logical object being
cached (i.e., the memory to store a directory listing for example).
This will be charged against the cache quota.
--*/
{
CACHE_OBJECT *cache = NULL;
PBLOB_HEADER pbhBlob;
BOOLEAN bSuccess;
ULONG iBin;
PLIST_ENTRY pEntry;
PCACHE_OBJECT pCache;
ASSERT( TSvcCache.IsValid() );
ASSERT( pwszDirectoryName != NULL );
ASSERT( pvBlob != NULL );
IF_DEBUG( CACHE) {
DBGPRINTF( (DBG_CONTEXT,
"TsCacheDirectoryBlobW called with"
" Dir=%S, DeMux=%u, PvBlob=%08x, BlbSz=%d, ChkedOut=%d\n",
pwszDirectoryName,
iDemultiplexor,
pvBlob,
cbBlobSize,
bKeepCheckedOut
));
}
//
// The caller will have passed their pointer to the usable area of the
// Blob, so we have to adjust it to point to the beginning.
//
pbhBlob = (( PBLOB_HEADER )pvBlob ) - 1;
//
// If the Blob is too large to cache, free it and return success. It
// will never be found in the cache, but this is not an error.
//
if ( pbhBlob->cbSize > Configuration.cbMaximumSize ) {
IF_DEBUG(CACHE) {
DBGPRINTF((DBG_CONTEXT,
" Blob(%d) to large for cache( max=%d). Dont cache\n",
pbhBlob->cbSize,
Configuration.cbMaximumSize));
}
goto Cannot_Cache;
}
ASSERT( !pbhBlob->IsCached );
ASSERT( pbhBlob->cbSize > 0 );
//
// Allocate the cache object.
//
cache = (PCACHE_OBJECT)ALLOC( sizeof(CACHE_OBJECT) );
if ( cache == NULL ) {
IF_DEBUG( CACHE) {
DBGPRINTF( ( DBG_CONTEXT,
"Unable to alloc Cache Object. Failure.\n"));
}
goto Cannot_Cache;
}
cache->Signature = CACHE_OBJ_SIGNATURE;
//
// Store the Blob in the new object.
//
cache->pbhBlob = pbhBlob;
//
// We need to be able to find the cache entry from the Blob.
//
pbhBlob->pCache = cache;
//
// Hash the directory name.
//
cache->hash = CalculateHashAndLengthOfPathName( pwszDirectoryName,
&cache->cchLength );
//
// Initialize the check-out count.
//
cache->references = ( bKeepCheckedOut) ? 2 : 1;
cache->iDemux = iDemultiplexor;
cache->dwService = TSvcCache.GetServiceId();
cache->UserValue = cbBlobSize;
cache->TTL = 1;
InitializeListHead( &cache->DirChangeList );
//
// Copy the directory name to the cache object.
//
wcscpy( cache->wszPath, pwszDirectoryName );
//
// Lock the cache table against changes. We need to take the lock
// before we add the new object to the directory change death list,
// so that a directory change that kills this object will not find
// the cache table without the object present.
//
EnterCriticalSection( &CacheTable.CriticalSection );
//
// Add the object to the directory change expiry list.
//
bSuccess = DirectoryChangeManager_NewItem( cache );
if ( !bSuccess )
{
//
// For whatever reason, we cannot get notifications of changes
// in the directory containing the to-be-cached item. We won't
// be adding this object to the cache table, so we unlock the
// table and jump to the failure-handling code.
//
LeaveCriticalSection( &CacheTable.CriticalSection );
IF_DEBUG( CACHE) {
DBGPRINTF( ( DBG_CONTEXT,
" Unable to cache. Due to rejection by DirChngMgr\n"));
}
goto Cannot_Cache;
}
//
// Mark this blob as cached, since we'll either cache it or throw it
// away hereafter.
//
pbhBlob->IsCached = TRUE;
//
// Add the object to the cache table, as the most-recently-used object.
//
iBin = HASH_TO_BIN( cache->hash );
//
// Look for a previously cached object for the same directory. If we
// find one, remove it.
//
for ( pEntry = CacheTable.Items[ iBin ].Flink;
pEntry != &CacheTable.Items[ iBin ];
pEntry = pEntry->Flink )
{
pCache = CONTAINING_RECORD( pEntry, CACHE_OBJECT, BinList );
if ( pCache->cchLength == cache->cchLength &&
pCache->hash == cache->hash &&
pCache->iDemux == cache->iDemux &&
pCache->dwService == cache->dwService &&
!_wcsicmp( cache->wszPath, pCache->wszPath ) )
{
//
// We found a matching cache object. We remove it, since it
// has been replaced by this new object.
//
DeCache( pCache, FALSE );
IF_DEBUG( CACHE) {
DBGPRINTF( ( DBG_CONTEXT,
" Matching cache object found."
" Throwing that object ( %08x) out of cache\n",
pEntry));
}
break;
}
}
//
// Add this object to the cache.
//
InsertHeadList( &CacheTable.Items[ iBin ], &cache->BinList );
//
// Since this object was just added, put it at the head of the MRU list.
//
InsertHeadList( &CacheTable.MruList, &cache->MruList );
//
// Remove least recently-used cache entries until the memory plus the
// new blob is under the limit. We don't add the new item until the
// space is available to avoid spiking the memory used counter
//
CheckCacheSize( cbBlobSize );
//
// Increase the running size of cached objects by the size of the one
// just cached.
//
CacheTable.MemoryInUse += pbhBlob->cbSize + cbBlobSize;
//
// Unlock the cache table.
//
LeaveCriticalSection( &CacheTable.CriticalSection );
ASSERT( BLOB_IS_OR_WAS_CACHED( pvBlob ) );
//
// Return success.
//
IF_DEBUG( CACHE) {
DBGPRINTF( ( DBG_CONTEXT,
" Cached object(%08x) contains Blob (%08x)."
" Returning TRUE\n",
cache, pvBlob));
}
return( TRUE );
Cannot_Cache:
//
// The cleanup code does not cleanup a directory change item.
//
if ( cache != NULL )
{
cache->Signature = 0;
FREE( cache );
cache = NULL;
}
ASSERT( !BLOB_IS_OR_WAS_CACHED( pvBlob ) );
IF_DEBUG( CACHE) {
DBGPRINTF( (DBG_CONTEXT, " Failure to cache the object ( %08x)\n",
pvBlob));
}
return( FALSE );
} // TsCacheDirectoryBlobW
BOOL
TsCheckOutCachedBlobA(
IN const TSVC_CACHE &TSvcCache,
IN PCSTR pszDirectoryName,
IN ULONG iDemultiplexor,
IN PVOID * ppvBlob,
IN OPTIONAL PULONG pcbBlobSize
)
{
DWORD cch;
WCHAR awchPath[ MAX_PATH + 1 ];
BOOL bSuccess;
//
// Convert directory name to UNICODE.
//
cch = MultiByteToWideChar( CP_ACP,
MB_PRECOMPOSED,
pszDirectoryName,
-1,
awchPath,
sizeof( awchPath ) / sizeof(WCHAR) );
if ( !cch )
return FALSE;
bSuccess = TsCheckOutCachedBlobW( TSvcCache,
awchPath,
iDemultiplexor,
ppvBlob,
pcbBlobSize );
return( bSuccess );
} // TsCheckOutCachedBlobA
BOOL
TsCheckOutCachedBlobW(
IN const TSVC_CACHE &TSvcCache,
IN PCWSTR pwszDirectoryName,
IN ULONG iDemultiplexor,
IN PVOID * ppvBlob,
IN OPTIONAL PULONG pcbBlobSize
)
{
HASH_TYPE hash;
ULONG cchLength;
int iBin;
BOOL Result;
PLIST_ENTRY pEntry;
PCACHE_OBJECT pCache;
DWORD Position = 0;
ASSERT( TSvcCache.IsValid() );
ASSERT( pwszDirectoryName != NULL );
ASSERT( ppvBlob != NULL );
//
// Prepare the return value such that we fail by default.
//
Result = FALSE;
//
// Calculate the hash and length of the path name.
//
hash = CalculateHashAndLengthOfPathName( pwszDirectoryName, &cchLength );
//
// Calculate the bin of the hash table that should head the list
// containing the sought-after item.
//
iBin = HASH_TO_BIN( hash );
EnterCriticalSection( &CacheTable.CriticalSection );
__try
{
//
// Look for a previously cached object for the same directory. If we
// find one, return it.
//
for ( pEntry = CacheTable.Items[ iBin ].Flink;
pEntry != &CacheTable.Items[ iBin ];
pEntry = pEntry->Flink, Position++ )
{
pCache = CONTAINING_RECORD( pEntry, CACHE_OBJECT, BinList );
ASSERT( pCache->Signature == CACHE_OBJ_SIGNATURE );
ASSERT( pCache->pbhBlob->IsCached );
ASSERT( pCache->pbhBlob->pCache == pCache );
if ( pCache->cchLength == cchLength &&
pCache->hash == hash &&
pCache->iDemux == iDemultiplexor &&
pCache->dwService == TSvcCache.GetServiceId() &&
!_wcsicmp( pCache->wszPath, pwszDirectoryName ) )
{
//
// We found a matching cache object. We return it and increase
// its reference count.
//
*ppvBlob = pCache->pbhBlob + 1;
if ( ARGUMENT_PRESENT( pcbBlobSize ) )
{
*pcbBlobSize = pCache->UserValue;
}
ASSERT( pCache->pbhBlob->IsCached );
//
// Increase the reference count of the cached object, to prevent
// it from expiration while it is checked out.
//
InterlockedIncrement( ( LONG * )( &(pCache->references ) ) );
pCache->TTL = 1;
Result = TRUE;
//
// If the found item is far enough back in the list, move
// it to the front so the next hit will be quicker
//
if ( Position > REORDER_LIST_THRESHOLD )
{
RemoveEntryList( pEntry );
InsertHeadList( &CacheTable.Items[ iBin ], pEntry );
IF_DEBUG( CACHE ) {
DBGPRINTF(( DBG_CONTEXT,
"[TsCheckOutCachedBlobW] Reordered list for item at %d position\n",
Position ));
}
}
break;
}
}
}
__except( EXCEPTION_EXECUTE_HANDLER )
{
//
// As far as I can see, the only way we can end up here with
// Result == TRUE is an exception on LeaveCriticalSection(). If
// that happens, we're toast anyway, since noone will ever get to
// the CacheTable again.
//
ASSERT( !Result );
Result = FALSE;
}
LeaveCriticalSection( &CacheTable.CriticalSection );
if ( Result) {
INC_COUNTER( TSvcCache.GetServiceId(), CacheHits );
} else {
INC_COUNTER( TSvcCache.GetServiceId(), CacheMisses );
}
return( Result );
} // TsCheckOutCachedBlobW
BOOL
TsCheckInCachedBlob(
IN const TSVC_CACHE &TSvcCache,
IN PVOID pvBlob
)
{
PBLOB_HEADER pbhBlob;
PCACHE_OBJECT pCache;
BOOL bEjected;
pbhBlob = (( PBLOB_HEADER )pvBlob ) - 1;
ASSERT( pbhBlob->IsCached );
pCache = pbhBlob->pCache;
ASSERT( pCache->Signature == CACHE_OBJ_SIGNATURE );
ASSERT( pCache->pbhBlob == pbhBlob );
ASSERT( pCache->references > 0 );
TsDereferenceCacheObj( pCache );
return( TRUE );
} // TsCheckInCachedBlob
BOOL
TsExpireCachedBlob(
IN const TSVC_CACHE &TSvcCache,
IN PVOID pvBlob
)
{
PBLOB_HEADER pbhBlob;
PCACHE_OBJECT pCache;
pbhBlob = (( PBLOB_HEADER )pvBlob ) - 1;
ASSERT( pbhBlob->IsCached );
pCache = pbhBlob->pCache;
ASSERT( pCache->Signature == CACHE_OBJ_SIGNATURE );
ASSERT( pCache->pbhBlob == pbhBlob );
ASSERT( pCache->references > 0 );
return( DeCache( pCache, TRUE ) );
} // TsExpireCachedBlob
VOID
TsDereferenceCacheObj(
IN PCACHE_OBJECT pCache
)
{
ASSERT( pCache->Signature == CACHE_OBJ_SIGNATURE );
ASSERT( pCache->references > 0 );
ASSERT( pCache->pbhBlob->IsCached );
if ( !InterlockedDecrement( (LONG *) &pCache->references ))
{
//
// We best not be on a list if we're about to be freed here
//
ASSERT( IsListEmpty( &pCache->BinList ) );
//
// We really want to call TsFree here, but we don't have a TsvcCache
//
IF_DEBUG( CACHE )
{
DBGPRINTF(( DBG_CONTEXT,
"[DeCache] Free routine: 0x%lx, Blob: 0x%lx Cache obj: 0x%lx\n",
pCache->pbhBlob->pfnFreeRoutine,
pCache->pbhBlob,
pCache ));
}
if ( pCache->pbhBlob->pfnFreeRoutine )
pCache->pbhBlob->pfnFreeRoutine( pCache->pbhBlob + 1);
CacheTable.MemoryInUse -= pCache->pbhBlob->cbSize +
pCache->UserValue;
ASSERT( !(CacheTable.MemoryInUse & 0x80000000 ));
DEC_COUNTER( pCache->dwService,
CurrentObjects );
FREE( pCache->pbhBlob );
FREE( pCache );
}
} // TsDereferenceCacheObj
VOID
CheckCacheSize(
DWORD cbNewBlobSize
)
/*++
Routine Description:
Checks to see if the cache size limit has been exceeded and throws out
objects until it is below the limit.
THE CACHE TABLE LOCK MUST BE TAKEN PRIOR TO CALLING THIS FUNCTION
Arguments:
cbNewBlobSize - Optional blob that is about to be added to the cache
--*/
{
while ( (CacheTable.MemoryInUse + cbNewBlobSize) > Configuration.cbMaximumSize &&
!IsListEmpty( &CacheTable.MruList ) )
{
//
// The least recently used entry is the one at the tail of the MRU
// list.
//
PCACHE_OBJECT pCacheObject =
CONTAINING_RECORD( CacheTable.MruList.Blink,
CACHE_OBJECT,
MruList );
DeCache( pCacheObject, FALSE );
IF_DEBUG( CACHE) {
DBGPRINTF( ( DBG_CONTEXT,
" Throwing out object ( %08x) to reduce cache size\n",
pCacheObject));
}
}
} // CheckCacheSize
BOOL
TsAdjustCachedBlobSize(
IN PVOID pvBlob,
IN INT cbSize
)
/*++
Routine Description:
Adjusts the user supplied cache size for this specified cache blob
Arguments:
pvBlob - Blob to adjust
cbSize - positive or negative size to adjust by
Returns:
TRUE if the size was adjust successfully, FALSE if the blob should not
be added to the cache because it own't fit
--*/
{
PBLOB_HEADER pbhBlob;
PCACHE_OBJECT pCache;
pbhBlob = (( PBLOB_HEADER )pvBlob ) - 1;
ASSERT( pbhBlob->IsCached );
pCache = pbhBlob->pCache;
ASSERT( pCache->Signature == CACHE_OBJ_SIGNATURE );
ASSERT( pCache->pbhBlob == pbhBlob );
ASSERT( pCache->references > 0 );
//
// If the size of this blob would never fit, indicate to the client
// they shouldn't do what they are about to do
//
if ( cbSize > (INT) Configuration.cbMaximumSize )
{
return FALSE;
}
//
// Take the table lock to decache items from the MRU and to adjust the
// MemoryInuse field of the CacheTable
//
EnterCriticalSection( &CacheTable.CriticalSection );
if ( (DWORD)((INT) CacheTable.MemoryInUse + cbSize) >
Configuration.cbMaximumSize &&
cbSize > 0 )
{
CheckCacheSize( (DWORD) cbSize );
}
CacheTable.MemoryInUse += cbSize;
pbhBlob->cbSize += cbSize;
LeaveCriticalSection( &CacheTable.CriticalSection );
return TRUE;
} // TsAdjustCachedBlobSize
VOID
TsCacheSetSize(
DWORD cbMemoryCacheSize
)
/*++
Routine Description:
Sets the new maximum size of the memory cache and flushes items till we
meet the new size requirements.
Arguments:
cbMemoryCacheSize - New memory cache size to set
--*/
{
if ( DisableTsunamiCaching ) {
ASSERT(Configuration.cbMaximumSize == 0);
return;
}
EnterCriticalSection( &CacheTable.CriticalSection );
Configuration.cbMaximumSize = cbMemoryCacheSize;
//
// Throw out any thing that won't fit under out new size
//
CheckCacheSize( 0 );
LeaveCriticalSection( &CacheTable.CriticalSection );
} // TsCacheSetSize
extern
DWORD
TsCacheQuerySize(
VOID
)
/*++
Routine Description:
Returns the current maximum size of the memory cache
--*/
{
return Configuration.cbMaximumSize;
} // TsCacheQuerySize
BOOL
TsCacheQueryStatistics(
IN DWORD Level,
IN DWORD dwServerMask,
IN INETA_CACHE_STATISTICS * pCacheCtrs
)
/*++
Routine Description:
This function returns the statistics for the global cache or for the
individual services
Arguments:
Level - Only valid value is 0
dwServerMask - Server mask to retrieve statistics for or 0 for the sum
of the services
pCacheCtrs - Receives the statistics for cache
Notes:
CacheBytesTotal and CacheBytesInUse are not kept on a per-server basis
so they are only returned when retrieving summary statistics.
Returns:
TRUE on success, FALSE on failure
--*/
{
if ( Level != 0 ||
dwServerMask > LAST_PERF_CTR_SVC ||
!pCacheCtrs )
{
SetLastError( ERROR_INVALID_PARAMETER );
return FALSE;
}
if ( dwServerMask )
{
memcpy( pCacheCtrs,
&Configuration.Stats[ MaskIndex(dwServerMask) ],
sizeof( Configuration.Stats[ 0 ] ) );
}
else
{
//
// Add up all of the statistics
//
memset( pCacheCtrs, 0, sizeof( *pCacheCtrs ));
pCacheCtrs->CacheBytesTotal = Configuration.cbMaximumSize;
pCacheCtrs->CacheBytesInUse = CacheTable.MemoryInUse;
for ( int i = 0; i < MAX_PERF_CTR_SVCS; i++ )
{
DWORD index = MaskIndex( 1 << i );
pCacheCtrs->CurrentOpenFileHandles+= Configuration.Stats[index].CurrentOpenFileHandles;
pCacheCtrs->CurrentDirLists += Configuration.Stats[index].CurrentDirLists;
pCacheCtrs->CurrentObjects += Configuration.Stats[index].CurrentObjects;
pCacheCtrs->FlushesFromDirChanges += Configuration.Stats[index].FlushesFromDirChanges;
pCacheCtrs->CacheHits += Configuration.Stats[index].CacheHits;
pCacheCtrs->CacheMisses += Configuration.Stats[index].CacheMisses;
}
}
return TRUE;
}
BOOL
TsCacheClearStatistics(
IN DWORD dwServerMask
)
/*++
Routine Description:
Clears the the specified service's statistics
--*/
{
if ( dwServerMask > LAST_PERF_CTR_SVC )
{
SetLastError( ERROR_INVALID_PARAMETER );
return FALSE;
}
//
// Currently this function isn't supported
//
SetLastError( ERROR_NOT_SUPPORTED );
return FALSE;
} // TsCacheClearStatistics
BOOL
TsCacheFlush(
IN DWORD dwServerMask
)
/*++
Routine Description:
This function flushes the cache of all items for the specified service
or for all services if dwServerMask is zero.
--*/
{
LIST_ENTRY * pEntry;
LIST_ENTRY * pNext;
EnterCriticalSection( &CacheTable.CriticalSection );
for ( pEntry = CacheTable.MruList.Flink;
pEntry != &CacheTable.MruList;
)
{
pNext = pEntry->Flink;
PCACHE_OBJECT pCacheObject =
CONTAINING_RECORD( pEntry,
CACHE_OBJECT,
MruList );
if ( !dwServerMask ||
(dwServerMask & pCacheObject->dwService))
{
DeCache( pCacheObject, FALSE );
IF_DEBUG( CACHE) {
DBGPRINTF( ( DBG_CONTEXT,
" Throwing out object ( %08x) due to manual flush\n",
pCacheObject));
}
}
pEntry = pNext;
}
LeaveCriticalSection( &CacheTable.CriticalSection );
return TRUE;
} // TsCacheFlush
BOOL
TsCacheFlushUser(
IN HANDLE hUserToken,
IN BOOL fDefer
)
/*++
Routine Description:
This function flushes all file handles associated the passed user context
Arguments:
hUserToken - User token to flush from the cache
fDefer - Build list but close handles later in worker thread (Not supported)
--*/
{
LIST_ENTRY * pEntry;
LIST_ENTRY * pNext;
ASSERT( !fDefer );
EnterCriticalSection( &CacheTable.CriticalSection );
for ( pEntry = CacheTable.MruList.Flink;
pEntry != &CacheTable.MruList;
)
{
pNext = pEntry->Flink;
PCACHE_OBJECT pCacheObject = CONTAINING_RECORD( pEntry,
CACHE_OBJECT,
MruList );
ASSERT( pCacheObject->Signature == CACHE_OBJ_SIGNATURE );
//
// Find all occurrences of the matching user token in the cache and
// decache them
//
if ( pCacheObject->iDemux == RESERVED_DEMUX_OPEN_FILE &&
((TS_OPEN_FILE_INFO *)(pCacheObject->pbhBlob + 1))->
QueryOpeningUser() == hUserToken )
{
ASSERT( pCacheObject->pbhBlob->cbSize == sizeof(TS_OPEN_FILE_INFO));
DeCache( pCacheObject, FALSE );
IF_DEBUG( CACHE) {
DBGPRINTF( ( DBG_CONTEXT,
" Throwing out object ( %08x) due to user token flush\n",
pCacheObject));
}
}
else if ( pCacheObject->iDemux == RESERVED_DEMUX_DIRECTORY_LISTING &&
((TS_DIRECTORY_HEADER *)(pCacheObject->pbhBlob + 1))->
QueryListingUser() == hUserToken )
{
ASSERT( pCacheObject->pbhBlob->cbSize == sizeof(TS_DIRECTORY_HEADER));
DeCache( pCacheObject, FALSE );
IF_DEBUG( CACHE) {
DBGPRINTF( ( DBG_CONTEXT,
" Throwing out object ( %08x) due to user token flush\n",
pCacheObject));
}
}
pEntry = pNext;
}
LeaveCriticalSection( &CacheTable.CriticalSection );
return TRUE;
} // TsCacheFlushUser