windows-nt/Source/XPSP1/NT/inetsrv/iis/svcs/infocomm/cache/dirchngp.cxx
2020-09-26 16:20:57 +08:00

784 lines
22 KiB
C++
Raw Permalink Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*++
Copyright (c) 1995 Microsoft Corporation
Module Name :
dirchngp.cxx
Abstract:
This module contains the internal directory change routines
Author:
Murali R. Krishnan ( MuraliK ) 16-Jan-1995
--*/
#include "TsunamiP.Hxx"
#pragma hdrstop
#include "issched.hxx"
#include "dbgutil.h"
#include <lonsi.hxx>
//
// Manifests
//
//
// Globals
//
HANDLE g_hChangeWaitThread = NULL;
LONG g_nTsunamiThreads = 0;
//
// Local prototypes
//
#if DBG
VOID
DumpCacheStructures(
VOID
);
#endif
VOID
Apc_ChangeHandler(
DWORD dwErrorCode,
DWORD dwBytesWritten,
LPOVERLAPPED lpo
)
{
PVIRTUAL_ROOT_MAPPING pVrm;
PDIRECTORY_CACHING_INFO pDci;
PLIST_ENTRY pEntry;
PLIST_ENTRY pNextEntry;
BOOLEAN bSuccess;
PCACHE_OBJECT pCache;
FILE_NOTIFY_INFORMATION *pNI;
LPBYTE pMax;
//
// The cache lock must always be taken before the root lock
//
EnterCriticalSection( &CacheTable.CriticalSection );
EnterCriticalSection( &csVirtualRoots );
pVrm = (VIRTUAL_ROOT_MAPPING *) lpo->hEvent;
ASSERT( pVrm->Signature == VIRT_ROOT_SIGNATURE );
ASSERT( pVrm->cRef > 0 );
//
// Is this item still active?
//
if ( pVrm->fDeleted )
{
IF_DEBUG( DIRECTORY_CHANGE ) {
DBGPRINTF(( DBG_CONTEXT,
"Got APC for root that has been removed\n" ));
}
DereferenceRootMapping( pVrm );
goto Exit;
}
pDci = ( PDIRECTORY_CACHING_INFO )( pVrm + 1 );
//
// It's possible (though unlikely) we received a notification for the
// same item that was removed then added before we did a wait on the
// new item. This is the old notify event then.
//
if ( !pDci->fOnSystemNotifyList )
{
DBGPRINTF(( DBG_CONTEXT,
"Old APC notification for %s\n",
pVrm->pszDirectoryA ));
goto Exit;
}
ASSERT( pVrm->fCachingAllowed );
IF_DEBUG( DIRECTORY_CHANGE ) {
DBGPRINTF(( DBG_CONTEXT,
"Got APC thing for %s.\n",
pVrm->pszDirectoryA ));
}
//
// Convert NotifyInfo from Unicode to Ansi
//
if ( dwBytesWritten )
{
for ( pNI = &pDci->NotifyInfo,
pMax = (LPBYTE)pNI + sizeof(pDci->NotifyInfo) + sizeof(pDci->szPathNameBuffer) ;; )
{
CHAR achAnsi[MAX_PATH+1];
pNI->FileNameLength = WideCharToMultiByte( CP_ACP,
WC_COMPOSITECHECK,
pNI->FileName,
pNI->FileNameLength / sizeof(WCHAR),
achAnsi,
MAX_PATH,
NULL,
NULL );
memcpy( pNI->FileName, achAnsi, pNI->FileNameLength );
if ( pNI->NextEntryOffset )
{
pNI = (FILE_NOTIFY_INFORMATION*)(((LPBYTE)pNI) + pNI->NextEntryOffset);
}
else
{
break;
}
}
}
for ( pEntry = pDci->listCacheObjects.Flink;
pEntry != &pDci->listCacheObjects;
pEntry = pNextEntry )
{
pNextEntry = pEntry->Flink;
pCache = CONTAINING_RECORD( pEntry, CACHE_OBJECT, DirChangeList );
if ( dwBytesWritten &&
( pCache->iDemux == RESERVED_DEMUX_OPEN_FILE ||
pCache->iDemux == RESERVED_DEMUX_URI_INFO ) )
{
for ( pNI = &pDci->NotifyInfo ;; )
{
if ( pCache->iDemux == RESERVED_DEMUX_URI_INFO
? ( ((PW3_URI_INFO)(pCache->pbhBlob+1))->cchName - pVrm->cchDirectoryA -1 == pNI->FileNameLength &&
!_memicmp( (LPSTR)pNI->FileName,
((PW3_URI_INFO)(pCache->pbhBlob+1))->pszName + pVrm->cchDirectoryA + 1,
pNI->FileNameLength ) )
: ( pCache->cchLength - pVrm->cchDirectoryA -1 == pNI->FileNameLength &&
!_memicmp( pCache->szPath + pVrm->cchDirectoryA + 1,
pNI->FileName,
pNI->FileNameLength ) ) )
{
IF_DEBUG( DIRECTORY_CHANGE ) {
DBGPRINTF(( DBG_CONTEXT,
"Expired entry for: %s.\n", pCache->szPath ));
}
bSuccess = DeCache( pCache, FALSE );
ASSERT( bSuccess );
break;
}
if ( pNI->NextEntryOffset )
{
pNI = (FILE_NOTIFY_INFORMATION*)(((LPBYTE)pNI) + pNI->NextEntryOffset);
}
else
{
break;
}
}
}
else
if ( pCache->iDemux != RESERVED_DEMUX_PHYSICAL_OPEN_FILE )
{
IF_DEBUG( DIRECTORY_CHANGE ) {
DBGPRINTF(( DBG_CONTEXT,
"Expired entry for: %s.\n", pCache->szPath ));
}
bSuccess = DeCache( pCache, FALSE );
ASSERT( bSuccess );
}
}
INC_COUNTER( pVrm->dwServiceID,
FlushesFromDirChanges );
if ( !pfnReadDirChangesW( pDci->hDirectoryFile,
(VOID *) &pDci->NotifyInfo,
sizeof( FILE_NOTIFY_INFORMATION ) +
sizeof( pDci->szPathNameBuffer ),
TRUE,
FILE_NOTIFY_VALID_MASK &
~FILE_NOTIFY_CHANGE_LAST_ACCESS,
NULL,
&pDci->Overlapped, // hEvent used as context
Apc_ChangeHandler ))
{
DBGPRINTF(( DBG_CONTEXT,
"[ApchChangeHandler] ReadDirectoryChanges returned %d\n",
GetLastError() ));
//
// Disable caching for this directory 'cause we aren't going to get any
// more change notifications
//
pVrm->fCachingAllowed = FALSE;
CLOSE_DIRECTORY_HANDLE( pDci );
//
// Decrement the ref-count as we'll never get an APC notification
//
DereferenceRootMapping( pVrm );
}
Exit:
LeaveCriticalSection( &csVirtualRoots );
LeaveCriticalSection( &CacheTable.CriticalSection );
} // Apc_ChangeHandler
DWORD
WINAPI
ChangeWaitThread(
PVOID pvParam
)
{
PLIST_ENTRY pEntry;
PVIRTUAL_ROOT_MAPPING pVrm;
PDIRECTORY_CACHING_INFO pDci;
DWORD dwWaitResult;
HANDLE ahEvents[2];
InterlockedIncrement( &g_nTsunamiThreads );
ahEvents[0] = g_hQuit;
ahEvents[1] = g_hNewItem;
do
{
//
// Loop through the list looking for any directories which haven't
// been added yet
//
EnterCriticalSection( &csVirtualRoots );
for ( pEntry = GlobalVRootList.Flink;
pEntry != &GlobalVRootList;
pEntry = pEntry->Flink ) {
pVrm = CONTAINING_RECORD( pEntry,
VIRTUAL_ROOT_MAPPING,
GlobalListEntry );
pDci = ( PDIRECTORY_CACHING_INFO )( pVrm + 1 );
ASSERT( pVrm->Signature == VIRT_ROOT_SIGNATURE );
if ( !pDci->fOnSystemNotifyList ) {
//
// call change notify, this indicates we want change notifications
// on this set of handles. Note the wait is a one shot deal,
// once a dir has been notified, it must be readded in the
// context of this thread.
//
IF_DEBUG( DIRECTORY_CHANGE )
DBGPRINTF(( DBG_CONTEXT,
"Trying to wait on %S\n",
pVrm->pszDirectoryA ));
//
// Use the hEvent field of the overlapped structure as the
// context to pass to the change handler. This is allowed
// by the ReadDirectoryChanges API
//
pDci->Overlapped.hEvent = (HANDLE) pVrm;
//
// If the memory cache size is zero, don't worry about
// change notifications
//
if ( !pfnReadDirChangesW( pDci->hDirectoryFile,
(VOID *) &pDci->NotifyInfo,
sizeof( FILE_NOTIFY_INFORMATION ) +
sizeof( pDci->szPathNameBuffer ),
TRUE,
FILE_NOTIFY_VALID_MASK &
~FILE_NOTIFY_CHANGE_LAST_ACCESS,
NULL,
&pDci->Overlapped, // Not used
Apc_ChangeHandler ))
{
DBGPRINTF(( DBG_CONTEXT,
"[ChangeWaitThread] ReadDirectoryChanges"
" returned %d\n",
GetLastError() ));
DBG_ASSERT( pVrm->fCachingAllowed == FALSE );
if ( pDci->hDirectoryFile )
{
CLOSE_DIRECTORY_HANDLE( pDci );
DereferenceRootMapping( pVrm );
}
//
// We don't shrink the buffer because we use the
// fOnSystemNotifyList flag which is in portion we
// would want to shrink (and it doesn't give us a whole
// lot).
//
} else {
pVrm->fCachingAllowed = TRUE;
}
//
// Indicate we've attempted to add the entry to the system
// notify list. Used for new items getting processed the 1st
// time
//
pDci->fOnSystemNotifyList = TRUE;
}
}
LeaveCriticalSection( &csVirtualRoots );
Rewait:
dwWaitResult = WaitForMultipleObjectsEx( 2,
ahEvents,
FALSE,
INFINITE,
TRUE );
if ( dwWaitResult == WAIT_IO_COMPLETION )
{
//
// Nothing to do, the APC routine took care of everything
//
goto Rewait;
}
} while ( dwWaitResult == (WAIT_OBJECT_0 + 1) );
ASSERT( dwWaitResult == WAIT_OBJECT_0 );
//
// free the handles and all the heap.
//
EnterCriticalSection( &csVirtualRoots );
for ( pEntry = GlobalVRootList.Flink;
pEntry != &GlobalVRootList;
pEntry = pEntry->Flink )
{
pVrm = CONTAINING_RECORD(
pEntry,
VIRTUAL_ROOT_MAPPING,
GlobalListEntry );
if ( pVrm->fCachingAllowed ) {
pDci = (PDIRECTORY_CACHING_INFO) (pVrm + 1);
pVrm->fDeleted = TRUE;
CLOSE_DIRECTORY_HANDLE( pDci );
}
pEntry = pEntry->Blink;
RemoveEntryList( pEntry->Flink );
DereferenceRootMapping( pVrm );
}
LeaveCriticalSection( &csVirtualRoots );
InterlockedDecrement( &g_nTsunamiThreads );
return( 0 );
} // ChangeWaitThread
BOOL
TsDecacheVroot(
DIRECTORY_CACHING_INFO * pDci
)
/*++
Description:
This function decouples all of the items that need to be decached
from the associated tsunami data structures and derefences the
cache obj. This routine used to schedule this off to a worker thread
but this is no longer necessary as this only is called during vroot
destruction.
This routine assumes the cache table lock and virtual root lock are
taken
Arguments:
pDci - Directory change blob that needs to have its contents decached
Returns:
TRUE on success and FALSE if any failure.
--*/
{
LIST_ENTRY * pEntry;
CACHE_OBJECT * pCacheTmp;
while ( !IsListEmpty( &pDci->listCacheObjects ))
{
pEntry = pDci->listCacheObjects.Flink;
pCacheTmp = CONTAINING_RECORD( pEntry, CACHE_OBJECT, DirChangeList );
ASSERT( pCacheTmp->Signature == CACHE_OBJ_SIGNATURE );
if ( pCacheTmp->iDemux == RESERVED_DEMUX_PHYSICAL_OPEN_FILE ) {
continue;
}
//
// Removes this cache item from all of the lists. It had better be
// on the cache lists otherwise somebody is mucking with the list
// without taking the lock.
//
if ( !RemoveCacheObjFromLists( pCacheTmp, FALSE ) ) {
ASSERT( FALSE );
continue;
}
TsDereferenceCacheObj( pCacheTmp, FALSE );
}
return TRUE;
} // TsDecacheVroot
VOID
TsFlushTimedOutCacheObjects(
VOID
)
/*++
Description:
This function walks all cache objects and decrements the TTL of the object.
When the TTL reaches zero, the object is removed from the cache.
--*/
{
LIST_ENTRY * pEntry;
LIST_ENTRY * pNextEntry;
CACHE_OBJECT * pCacheTmp;
LIST_ENTRY ListHead;
InitializeListHead( &ListHead );
EnterCriticalSection( &CacheTable.CriticalSection );
EnterCriticalSection( &csVirtualRoots );
#if DBG
IF_DEBUG( CACHE ) {
DumpCacheStructures();
}
#endif
for ( pEntry = CacheTable.MruList.Flink;
pEntry != &CacheTable.MruList;
pEntry = pNextEntry ) {
pNextEntry = pEntry->Flink;
pCacheTmp = CONTAINING_RECORD( pEntry, CACHE_OBJECT, MruList );
ASSERT( pCacheTmp->Signature == CACHE_OBJ_SIGNATURE );
if ( pCacheTmp->iDemux == RESERVED_DEMUX_PHYSICAL_OPEN_FILE ) {
continue;
}
//
// If the object hasn't been referenced since the last TTL, throw
// it out now
//
if ( pCacheTmp->TTL == 0 ) {
//
// Removes this cache item from all of the lists. It had better be
// on the cache lists otherwise somebody is mucking with the list
// without taking the lock. We put it on a temporary list that
// we'll traverse after we release the locks
//
if ( !RemoveCacheObjFromLists( pCacheTmp, FALSE ) ) {
ASSERT( FALSE );
continue;
}
InsertTailList( &ListHead, &pCacheTmp->DirChangeList );
} else {
pCacheTmp->TTL--;
}
}
LeaveCriticalSection( &csVirtualRoots );
LeaveCriticalSection( &CacheTable.CriticalSection );
//
// Now do the dereferences which may actually close the objects now that
// we don't have to hold the locks
//
for ( pEntry = ListHead.Flink;
pEntry != &ListHead;
pEntry = pNextEntry ) {
pNextEntry = pEntry->Flink;
pCacheTmp = CONTAINING_RECORD( pEntry, CACHE_OBJECT, DirChangeList );
ASSERT( pCacheTmp->Signature == CACHE_OBJ_SIGNATURE );
TsDereferenceCacheObj( pCacheTmp, TRUE );
}
} // TsFlushTimedOutCacheObjects
#if DBG
VOID
DumpCacheStructures(
VOID
)
{
LIST_ENTRY * pEntry;
DWORD cItemsOnBin = 0;
DWORD cTotalItems = 0;
DWORD i, c;
DBGPRINTF(( DBG_CONTEXT,
"[DumpCacheStructures] CacheTable at 0x%lx, MAX_BINS = %d\n",
&CacheTable,
MAX_BINS ));
for ( i = 0; i < MAX_BINS; i++ )
{
for ( pEntry = CacheTable.Items[i].Flink, cItemsOnBin = 0;
pEntry != &CacheTable.Items[i];
pEntry = pEntry->Flink, cItemsOnBin++, cTotalItems++ )
{
;
}
if ( cItemsOnBin > 0) {
DBGPRINTF(( DBG_CONTEXT,
"Bin[%3d] %4d\n",
i,
cItemsOnBin ));
}
}
DBGPRINTF(( DBG_CONTEXT,
"Total Objects in bins: %d\n",
cTotalItems ));
DBGPRINTF(( DBG_CONTEXT,
"=====================================================\n" ));
//
// Now print the contents of each bin
//
for ( i = 0; i < MAX_BINS; i++ )
{
PCACHE_OBJECT pcobj;
if ( IsListEmpty( &CacheTable.Items[i] ))
continue;
DBGPRINTF(( DBG_CONTEXT,
"================== Bin %d ==================\n",
i ));
for ( pEntry = CacheTable.Items[i].Flink, cItemsOnBin = 0;
pEntry != &CacheTable.Items[i];
pEntry = pEntry->Flink, cItemsOnBin++ )
{
pcobj = CONTAINING_RECORD( pEntry, CACHE_OBJECT, BinList );
DBGPRINTF(( DBG_CONTEXT,
"CACHE_OBJECT[0x%lx] Service = %d, Instance = %d iDemux = 0x%lx ref = %d, TTL = %d\n"
" hash = 0x%lx, cchLength = %d\n"
" %s\n",
pcobj,
pcobj->dwService,
pcobj->dwInstance,
pcobj->iDemux,
pcobj->references,
pcobj->TTL,
pcobj->hash,
pcobj->cchLength,
pcobj->szPath ));
}
}
}
#endif //DBG
VOID
TsDumpCacheToHtml( OUT CHAR * pchBuffer, IN OUT LPDWORD lpcbBuffer )
{
LIST_ENTRY * pEntry;
DWORD cItemsOnBin = 0;
DWORD cTotalItems = 0;
DWORD i, c, cb;
EnterCriticalSection( &CacheTable.CriticalSection );
EnterCriticalSection( &csVirtualRoots );
DBG_ASSERT( lpcbBuffer != NULL && pchBuffer != NULL);
DBG_ASSERT( *lpcbBuffer > 10240);
cb = wsprintf( pchBuffer,
" CacheTable at 0x%lx, MAX_BINS=%d<br>"
"<TABLE BORDER> <TR> <TH> Bin Number </TH> "
// " <TH> # Items </TH> "
,
&CacheTable, MAX_BINS);
//
// Generate the column headings for the bins
// 0, 1, 2, ..... 9
//
for (i = 0; i < 10; i++) {
if ( *lpcbBuffer >= cb + 20) {
cb += wsprintf( pchBuffer +cb,
"<TH>%d</TH>",
i);
}
} // for
for ( i = 0; i < MAX_BINS; i++ ) {
for ( pEntry = CacheTable.Items[i].Flink, cItemsOnBin = 0;
pEntry != &CacheTable.Items[i];
pEntry = pEntry->Flink, cItemsOnBin++ )
{
// count the number of items in this bin
;
}
cTotalItems += cItemsOnBin;
if ( *lpcbBuffer >= cb + 60) {
if ( i % 10 == 0) {
//
// start a new row
//
cb += wsprintf( pchBuffer + cb,
"</TR><TR><TH>[%3d] </TH>",
i);
}
if ( cItemsOnBin > 0) {
cb += wsprintf( pchBuffer + cb,
"<TD>%4d</TD>",
cItemsOnBin);
} else {
//
// Dump nothing for zero slots
//
cb += wsprintf( pchBuffer + cb,
"<TD><font color=\"0x80808080\"> </font></TD>",
cItemsOnBin);
}
}
} // for all bins
if ( *lpcbBuffer >= cb + 50) {
cb += wsprintf( pchBuffer + cb,
"</TR></TABLE><p>"
"Total Objects in bins: %d; OpenFilesInUse(%d);"
" Max Allowed=%d. <br> <hr>"
" The cached objects: "
,
cTotalItems,
CacheTable.OpenFileInUse,
CacheTable.MaxOpenFileInUse
);
}
//
// Now print the contents of each bin
//
for ( i = 0; i < MAX_BINS; i++ ) {
PCACHE_OBJECT pcobj;
if ( IsListEmpty( &CacheTable.Items[i] ))
continue;
if ( *lpcbBuffer >= cb + 60) {
cb += wsprintf( pchBuffer + cb,
"<hr><b>============ Bin %d ==========</b><br>",
i );
}
for ( pEntry = CacheTable.Items[i].Flink, cItemsOnBin = 0;
pEntry != &CacheTable.Items[i];
pEntry = pEntry->Flink, cItemsOnBin++ )
{
pcobj = CONTAINING_RECORD( pEntry, CACHE_OBJECT, BinList );
if ( *lpcbBuffer >= cb + 300) {
cb += wsprintf( pchBuffer + cb,
"[0x%lx] Svc:Inst = %d:%d; "
"iDemux=0x%lx; ref=%d; TTL=%d; "
" hash=0x%lx; "
" (%d) %s<br>",
pcobj,
pcobj->dwService,
pcobj->dwInstance,
pcobj->iDemux,
pcobj->references,
pcobj->TTL,
pcobj->hash,
pcobj->cchLength,
pcobj->szPath
);
}
} // for each item in bin
} // for each bin
LeaveCriticalSection( &csVirtualRoots );
LeaveCriticalSection( &CacheTable.CriticalSection );
*lpcbBuffer = cb;
return;
} // TsDumpCacheToHtml()