windows-nt/Source/XPSP1/NT/inetsrv/iis/svcs/cmp/asp/cachemgr.cpp

1665 lines
48 KiB
C++
Raw Normal View History

2020-09-26 03:20:57 -05:00
/*-----------------------------------------------------------------------------
Microsoft Denali
Microsoft Confidential
Copyright 1996 Microsoft Corporation. All Rights Reserved.
Component: Template Cache Manager
File: CacheMgr.cpp
Owner: DGottner
Template cache manager implementation
-----------------------------------------------------------------------------*/
#include "denpre.h"
#pragma hdrstop
#include "perfdata.h"
#include "memchk.h"
CTemplateCacheManager g_TemplateCache;
CIncFileMap g_IncFileMap;
LONG g_nFlushThreads = 0;
BOOL CTemplateCacheManager::m_fFailedToInitPersistCache = FALSE;
char CTemplateCacheManager::m_szPersistCacheDir[MAX_PATH];
/*===================================================================
ZapTemplate
Decrement the ref. count of a template to remove it from cache.
If the template is global.asa, that's all we do because application
manager has the last reference. Otherwise, we Release the template
by calling CTemplate::End() to also free references to it from the
debugger.
Parameters: pTemplate - template pointer to Release() from cache
Returns: new ref. count
===================================================================*/
static inline
ULONG ZapTemplate(CTemplate *pTemplate)
{
if (! pTemplate->FGlobalAsa())
return pTemplate->End();
else
return pTemplate->Release();
}
/* ****************************************************************************
CCacheManager member functions
*/
/*===================================================================
CTemplateCacheManager::CTemplateCacheManager
Parameters: N/A
Returns: N/A
===================================================================*/
CTemplateCacheManager::CTemplateCacheManager()
{
m_pHashTemplates = NULL;
m_szPersistCacheDir[0] = '\0';
m_fFailedToInitPersistCache = FALSE;
}
/*===================================================================
CTemplateCacheManager::~CTemplateCacheManager
Parameters: N/A
Returns: N/A
===================================================================*/
CTemplateCacheManager::~CTemplateCacheManager()
{
}
/*===================================================================
CTemplateCacheManager::Init
Init the template cache manager - phase 1 - that which can be done
with just default values in Glob.
Parameters: None
Returns: Completion Status
===================================================================*/
HRESULT CTemplateCacheManager::Init()
{
HRESULT hrInit;
ErrInitCriticalSection(&m_csUpdate, hrInit);
if (FAILED(hrInit))
return(hrInit);
// allocate the initial CTemplateHashTable
m_pHashTemplates = new CTemplateHashTable;
return S_OK;
}
/* ****************************************************************************
CTemplateCacheManager member functions
*/
/*===================================================================
CTemplateCacheManager::UnInit
Parameters: N/A
Returns: Completion status
===================================================================*/
HRESULT CTemplateCacheManager::UnInit()
{
if (m_pHashTemplates) {
while (! m_pHashTemplates->FMemoryTemplatesIsEmpty()) {
CTemplate *pTemplate = static_cast<CTemplate *>(m_pHashTemplates->MemoryTemplatesBegin());
m_pHashTemplates->RemoveTemplate(pTemplate);
ZapTemplate(pTemplate);
}
while (! m_pHashTemplates->FPersistTemplatesIsEmpty()) {
CTemplate *pTemplate = static_cast<CTemplate *>(m_pHashTemplates->PersistTemplatesBegin());
m_pHashTemplates->RemoveTemplate(pTemplate);
ZapTemplate(pTemplate);
}
}
delete m_pHashTemplates;
m_pHashTemplates = NULL;
#ifndef PERF_DISABLE
g_PerfData.Zero_MEMORYTEMPLCACHE();
g_PerfData.Zero_TEMPLCACHE();
#endif
DeleteCriticalSection(&m_csUpdate);
// give any flush threads a chance to finish. This is necessary
// to prevent an AV by LKRHash.
DWORD maxSecondsToWait = 60;
while (maxSecondsToWait-- && g_nFlushThreads) {
Sleep(1000);
}
return S_OK;
}
/*===================================================================
CTemplateCacheManager::FindCached
Get a template from the cache only
Parameters:
szFile - file to find in the cache
ppTemplate - [out] template object found
Returns:
HRESULT (S_OK if found, S_FALSE if noe found)
===================================================================*/
HRESULT CTemplateCacheManager::FindCached(const TCHAR *szFile, DWORD dwInstanceID, CTemplate **ppTemplate)
{
Assert(IsNormalized(szFile));
if (!ppTemplate)
return E_POINTER;
LockTemplateCache();
m_pHashTemplates->FindTemplate(CTemplateKey(szFile, dwInstanceID), ppTemplate);
if (*ppTemplate)
{
if (!(*ppTemplate)->m_fReadyForUse)
*ppTemplate = NULL; // not ready - as if nor found
else
(*ppTemplate)->AddRef(); // addref inside critical section
}
UnLockTemplateCache();
return *ppTemplate? S_OK : S_FALSE;
}
/*===================================================================
CTemplateCacheManager::Load
Get a template from the cache, or load it into cache
Parameters:
szFile - file to load into the cache
Returns: N/A
===================================================================*/
HRESULT CTemplateCacheManager::Load(BOOL fRunGlobalAsp, const TCHAR *szFile, DWORD dwInstanceID, CHitObj *pHitObj, CTemplate **ppTemplate, BOOL *pfTemplateInCache)
{
HRESULT hr = S_OK; // return value
HRESULT (CTemplate::*pmAction)(CHitObj *); // do we need to compile a new template or deliver an existing one?
Assert(IsNormalized(szFile));
BOOL fLocked = FALSE;
// If this is the GLOBAL.ASA we can pick up
// template directly from the application
if (fRunGlobalAsp && pHitObj->PAppln()->PGlobalTemplate())
{
*ppTemplate = pHitObj->PAppln()->PGlobalTemplate();
}
// see if we already have looked up the template on the I/O thread...
else if (!fRunGlobalAsp && pHitObj->GetTemplate())
{
*ppTemplate = pHitObj->GetTemplate();
pHitObj->SetTemplate(NULL);
}
else
// Otherwise we have to look for it in the cache
{
LockTemplateCache();
fLocked = TRUE;
m_pHashTemplates->FindTemplate(CTemplateKey(szFile, dwInstanceID), ppTemplate);
}
if (*ppTemplate != NULL)
{
// Template found in cache -> use it
(*ppTemplate)->AddRef();
*pfTemplateInCache = TRUE;
if (fLocked) // Global.Asa from App - no lock
UnLockTemplateCache();
pmAction = CTemplate::Deliver;
}
else
{
*pfTemplateInCache = FALSE;
Assert(fLocked); // only could get here if not found in the hash table
UnLockTemplateCache();
// Create and init new template outside of crirical section
CTemplate *pNewTemplate = new CTemplate;
if (!pNewTemplate)
hr = E_OUTOFMEMORY;
if (SUCCEEDED(hr))
hr = pNewTemplate->Init(pHitObj, !!fRunGlobalAsp, CTemplateKey(szFile, dwInstanceID));
if (SUCCEEDED(hr))
{
LockTemplateCache();
// Try to find if inserted by another thread
m_pHashTemplates->FindTemplate(CTemplateKey(szFile, dwInstanceID), ppTemplate);
if (*ppTemplate != NULL)
{
// Template found in cache -> use it
(*ppTemplate)->AddRef();
UnLockTemplateCache();
pmAction = CTemplate::Deliver;
}
else
{
// since we are creating a new template, call FlushCache to make
// sure that no script engines are cached with this name
g_ScriptManager.FlushCache(szFile);
// Insert the newly created template
*ppTemplate = pNewTemplate;
pNewTemplate = NULL; // not to be deleted later
m_pHashTemplates->InsertTemplate(*ppTemplate);
(*ppTemplate)->AddRef();
if (Glob(dwScriptFileCacheSize) == 0) {
// This is special case when a valid template
// does not get added to the cache
// Don't attach such templates to debugger
(*ppTemplate)->m_fDontAttach = TRUE;
}
UnLockTemplateCache();
pmAction = CTemplate::Compile;
}
}
// cleanup new template if created but unused
if (pNewTemplate)
pNewTemplate->Release();
}
if (FAILED(hr))
return hr;
// init succeeded: compile or deliver the template, as required
hr = ((*ppTemplate)->*pmAction)(pHitObj);
if (pmAction == CTemplate::Compile && (*ppTemplate)->m_fDontCache)
{
/* We were compiling and the compiler alerted us not to cache the failed template.
Typically, this occurs when compile failure was caused by something other than
bad template syntax (permissions failure, bad include file reference, etc.).
We need to roll back to where the template did not exist.
*/
// de-cache and release the template
// NOTE we don't nullify template ptr, because we want ExecuteRequest to do the final release
LockTemplateCache();
if (m_pHashTemplates->RemoveTemplate(*ppTemplate) == LK_SUCCESS)
ZapTemplate(*ppTemplate);
UnLockTemplateCache();
(*ppTemplate)->Release();
*ppTemplate = NULL;
}
LockTemplateCache();
// Remove old scripts from cache
while (!m_pHashTemplates->FMemoryTemplatesIsEmpty()
&& (m_pHashTemplates->InMemoryTemplates() > Glob(dwScriptFileCacheSize))) {
Assert (!m_pHashTemplates->FMemoryTemplatesIsEmpty());
CTemplate *pOldTemplate = static_cast<CTemplate *>(m_pHashTemplates->MemoryTemplatesEnd());
m_pHashTemplates->RemoveTemplate(pOldTemplate, TRUE);
// flush the corresponding script engines. But only if the template
// is valid.
if (pOldTemplate->FIsValid()) {
g_ScriptManager.FlushCache(pOldTemplate->GetSourceFileName());
}
// Only Zap the template if it is not persisted. The result of the above
// call to RemoveTemplate is that the template may have been moved from the
// memory cache to the persist cache. In which case, the template is still
// effectively cached.
if (pOldTemplate->FIsPersisted() == FALSE) {
ZapTemplate(pOldTemplate);
}
}
UnLockTemplateCache();
// Store a pointer to the template with the application
// if we haven't already done so
if (SUCCEEDED(hr) && fRunGlobalAsp && pHitObj->PAppln()->PGlobalTemplate() == NULL)
pHitObj->PAppln()->SetGlobalTemplate(*ppTemplate);
// If we are shutting down, don't request change notification
if (!IsShutDownInProgress())
{
// If running on NT, and we just compiled the template
// register all the directories used by this template
// for change notification
if (FIsWinNT() && pmAction == CTemplate::Compile && SUCCEEDED(hr)) {
if (!RegisterTemplateForChangeNotification(*ppTemplate, pHitObj->PAppln())) {
LockTemplateCache();
if (m_pHashTemplates->RemoveTemplate(*ppTemplate) == LK_SUCCESS)
ZapTemplate(*ppTemplate);
UnLockTemplateCache();
}
// also create the services config object
hr = (*ppTemplate)->CreateTransServiceConfig(pHitObj->PAppln()->QueryAppConfig()->fTrackerEnabled());
}
// If running on NT, this is a new application, and the template is a global.asa
// register this application for file change notifications
if (FIsWinNT() && SUCCEEDED(hr) && (*ppTemplate)->m_fGlobalAsa && pHitObj->FStartApplication())
{
RegisterApplicationForChangeNotification(*ppTemplate, pHitObj->PAppln());
}
}
return hr;
}
/*===================================================================
CTemplateCacheManager::Flush
Parameters:
szFile - the file to remove from cache
Returns:
None
===================================================================*/
void CTemplateCacheManager::Flush(const TCHAR *szFile, DWORD dwInstanceID)
{
LockTemplateAndIncFileCaches();
Assert (IsNormalized(szFile));
CTemplate *pTemplate;
m_pHashTemplates->FindTemplate(CTemplateKey(szFile, dwInstanceID), &pTemplate);
while (pTemplate != NULL)
{
#ifndef PERF_DISABLE
g_PerfData.Incr_TEMPLFLUSHES();
#endif
m_pHashTemplates->RemoveTemplate(pTemplate);
// Make sure anyone using this template can tell it is obsolete
pTemplate->Zombify();
// Don't flush engines if this is a global.asa file
// We'll need the engines to run Application_OnEnd
// The application will flush the engine from the cache
// when it unints
if (!FIsGlobalAsa(szFile))
{
g_ScriptManager.FlushCache(szFile);
}
ZapTemplate(pTemplate);
// If wildcard was specified in Flush for Instance ID, there may be
// more templates to remove.
m_pHashTemplates->FindTemplate(CTemplateKey(szFile, dwInstanceID), &pTemplate);
}
UnLockTemplateAndIncFileCaches();
}
/*===================================================================
CTemplateCacheManager::FlushAll
Completely empties the template cache
Parameters:
None
Returns:
None
===================================================================*/
void CTemplateCacheManager::FlushAll(VOID)
{
LockTemplateAndIncFileCaches();
CTemplateHashTable *pNewTable = NULL;
HANDLE hnd;
// note that all of the following logic works on the premise that any
// error causes the code to fall into the old mechanism of flushing
// the hash table in place...
// allocate a new table
if (!(pNewTable = new CTemplateHashTable));
// Create a thread to clean up the old table
else if (!(hnd = CreateThread(NULL, 0, CTemplateCacheManager::FlushHashTableThread, m_pHashTemplates, 0, NULL)));
else {
// all the above was successful, so note that the new table is the
// current table in the cache, cleanup and exit.
DBGPRINTF((DBG_CONTEXT, "[CTemplateCacheManager] Flushing entire cache on another thread.\n"));
InterlockedIncrement(&g_nFlushThreads);
m_pHashTemplates = pNewTable;
CloseHandle(hnd);
UnLockTemplateAndIncFileCaches();
return;
}
// delete the new table if something above failed.
if (pNewTable)
delete pNewTable;
DBGPRINTF((DBG_CONTEXT, "[CTemplateCacheManager] Flushing entire cache in place\n"));
FlushHashTable(m_pHashTemplates);
UnLockTemplateAndIncFileCaches();
return;
}
/*===================================================================
CTemplateCacheManager::FlushHashTableThread
Thread spun up by CTemplateCacheMgr::FlushAll() to flush all
templates in the cache but not while under the critical section
on the notification thread. Prevents unwanted contention on the
cache.
Parameters:
None
Returns:
None
===================================================================*/
DWORD CTemplateCacheManager::FlushHashTableThread(VOID *pArg)
{
CTemplateHashTable *pTable = (CTemplateHashTable *)pArg;
Assert(pTable);
FlushHashTable(pTable);
delete pTable;
InterlockedDecrement(&g_nFlushThreads);
return S_OK;
}
/*===================================================================
CTemplateCacheManager::FlushHashTable
Does the actual work of flushing the templates.
This routine may or may not be under the global cache manager
crit sec. It will if the flush is happening on the notification
thread. It won't be if it's happening on the FlushHashTableThread.
Parameters:
None
Returns:
None
===================================================================*/
void CTemplateCacheManager::FlushHashTable(CTemplateHashTable *pTable)
{
// Delete templates from the cache until there are no more
while (!pTable->FMemoryTemplatesIsEmpty()) {
CTemplate *pTemplate = static_cast<CTemplate *>(pTable->MemoryTemplatesEnd());
// Remove the template from its various data structures
pTable->RemoveTemplate(pTemplate);
// Make sure anyone using this template can tell it is obsolete
pTemplate->Zombify();
// Flush the engine for this template from the script engine cache
// (use hash key, in case template was previously a zombie.)
g_ScriptManager.FlushCache(pTemplate->ExtractHashKey()->szPathTranslated);
ZapTemplate(pTemplate);
}
// Delete templates from the cache until there are no more
while (!pTable->FPersistTemplatesIsEmpty()) {
CTemplate *pTemplate = static_cast<CTemplate *>(pTable->PersistTemplatesEnd());
// Remove the template from its various data structures
pTable->RemoveTemplate(pTemplate);
// Make sure anyone using this template can tell it is obsolete
pTemplate->Zombify();
// Flush the engine for this template from the script engine cache
// (use hash key, in case template was previously a zombie.)
g_ScriptManager.FlushCache(pTemplate->ExtractHashKey()->szPathTranslated);
ZapTemplate(pTemplate);
}
}
/*===================================================================
CTemplateCacheManager::FlushFiles
Empties template cache of files that match a prefix
Parameters:
None
Returns:
None
===================================================================*/
void CTemplateCacheManager::FlushFiles(const TCHAR *szFilePrefix)
{
LockTemplateAndIncFileCaches();
BOOL fDoingMemoryTemplates = TRUE;
// Delete templates from the cache until there are no more
CDblLink *pLink = m_pHashTemplates->MemoryTemplatesBegin();
while (! (fDoingMemoryTemplates
? m_pHashTemplates->FMemoryTemplatesDblLinkAtEnd(pLink)
: m_pHashTemplates->FPersistTemplatesDblLinkAtEnd(pLink))) {
CDblLink *pNextLink = pLink->PNext();
CTemplate *pTemplate = static_cast<CTemplate *>(pLink);
if (_tcsncmp(pTemplate->ExtractHashKey()->szPathTranslated, szFilePrefix, _tcslen(szFilePrefix)) == 0) {
#if UNICODE
DBGPRINTF((DBG_CONTEXT, "FlushFiles: flushing %S\n", pTemplate->ExtractHashKey()->szPathTranslated));
#else
DBGPRINTF((DBG_CONTEXT, "FlushFiles: flushing %s\n", pTemplate->ExtractHashKey()->szPathTranslated));
#endif
// Remove the template from its various data structures
m_pHashTemplates->RemoveTemplate(pTemplate);
// Make sure anyone using this template can tell it is obsolete
pTemplate->Zombify();
// Flush the engine for this template from the script engine cache
// (use hash key, in case template was previously a zombie.)
g_ScriptManager.FlushCache(pTemplate->ExtractHashKey()->szPathTranslated);
ZapTemplate(pTemplate);
#ifndef PERF_DISABLE
g_PerfData.Incr_TEMPLFLUSHES();
#endif
}
pLink = pNextLink;
if (fDoingMemoryTemplates && m_pHashTemplates->FMemoryTemplatesDblLinkAtEnd(pLink)) {
fDoingMemoryTemplates = FALSE;
pLink = m_pHashTemplates->PersistTemplatesBegin();
}
}
UnLockTemplateAndIncFileCaches();
}
/*===================================================================
CTemplateCacheManager::AddApplicationToDebuggerUI
Loop through the template cache, and create doc nodes for
all templates that belong to the application
Parameters:
pAppln - pointer to application to attach to.
Returns: N/A
===================================================================*/
void CTemplateCacheManager::AddApplicationToDebuggerUI(CAppln *pAppln)
{
CDblLink *pLink;
for (pLink = m_pHashTemplates->MemoryTemplatesBegin(); !m_pHashTemplates->FMemoryTemplatesDblLinkAtEnd(pLink); pLink = pLink->PNext())
{
// Bug 92070:
// Determine if the template is a member of pAppln by comparing
// the virtual path of the template to the application's virtual
// path (previously compared physical paths) Since a template
// can have multiple virtual paths, only the first instance wins.
// Thus the template will only appear in the application that first
// loaded it.
CTemplate *pTemplate = static_cast<CTemplate *>(pLink);
if (_tcscmp(pAppln->GetApplnPath(SOURCEPATHTYPE_VIRTUAL), pTemplate->GetApplnPath(SOURCEPATHTYPE_VIRTUAL)) == 0)
pTemplate->AttachTo(pAppln);
}
}
/*===================================================================
CTemplateCacheManager::RemoveApplicationFromDebuggerUI
Loop through the template cache, and remove doc nodes for
all templates that belong to the application
Parameters:
pAppln - pointer to application to detach from
if pAppln is NULL, detach from ALL applications
Returns: N/A
===================================================================*/
void CTemplateCacheManager::RemoveApplicationFromDebuggerUI(CAppln *pAppln)
{
CDblLink *pLink;
for (pLink = m_pHashTemplates->MemoryTemplatesBegin();
!m_pHashTemplates->FMemoryTemplatesDblLinkAtEnd(pLink);
pLink = pLink->PNext())
{
CTemplate *pTemplate = static_cast<CTemplate *>(pLink);
if (pAppln != NULL)
pTemplate->DetachFrom(pAppln);
else
pTemplate->Detach();
}
}
/*===================================================================
void CTemplateCacheManager::RegisterTemplateForChangeNotification
Request to watch template directories for file changes
Parameters:
A pointer to the template
Returns:
BOOL True if successfully registered for change notification
===================================================================*/
BOOL CTemplateCacheManager::RegisterTemplateForChangeNotification(CTemplate *pTemplate, CAppln *pApplication)
{
STACK_BUFFER( tempPath, MAX_PATH );
// Doesnt happen on Win95
if (!FIsWinNT()) {
return FALSE;
}
for (DWORD i = 0; i < pTemplate->m_cFilemaps; i++) {
// Check if this directory is already registered for change notification
// Pick out the directory portion of the path
TCHAR *szEndOfPath = _tcsrchr(pTemplate->m_rgpFilemaps[i]->m_szPathTranslated, _T('\\'));
size_t cch = DIFF(szEndOfPath - pTemplate->m_rgpFilemaps[i]->m_szPathTranslated)+1;
if (tempPath.Resize((cch * sizeof(TCHAR)) + sizeof(TCHAR)) == FALSE) {
// if failure to resize, continue registering...
continue;
}
TCHAR *szPath = (TCHAR *) tempPath.QueryPtr();
_tcsncpy(szPath, pTemplate->m_rgpFilemaps[i]->m_szPathTranslated, cch);
szPath[cch] = 0;
// if the template is within the application's physical path, then it is
// already being monitored.
CASPDirMonitorEntry *pDME = NULL;
if (pDME = pApplication->FPathMonitored(szPath)) {
pDME->AddRef();
pTemplate->m_rgpFilemaps[i]->m_pDME= pDME;
continue;
}
if (RegisterASPDirMonitorEntry(szPath, &pDME)) {
Assert(pDME);
pTemplate->m_rgpFilemaps[i]->m_pDME= pDME;
}
else {
// the current file failed to register. Release all previous DMEs
// and return FALSE...
if (i > 0) {
while (--i) {
pTemplate->m_rgpFilemaps[i]->m_pDME->Release();
pTemplate->m_rgpFilemaps[i]->m_pDME = NULL;
}
}
return FALSE;
}
}
return TRUE;
}
/*===================================================================
void CTemplateCacheManager::RegisterApplicationForChangeNotification
Request to watch template directories for file changes
Parameters:
A pointer to the template
Returns:
BOOL True if successfully registered for change notification
===================================================================*/
BOOL CTemplateCacheManager::RegisterApplicationForChangeNotification(CTemplate *pTemplate, CAppln *pApplication)
{
STACK_BUFFER( tempPath, MAX_PATH );
// Doesnt happen on Win95
if (!FIsWinNT())
{
return FALSE;
}
// Start with 1 to skip GLOBAL.ASA that is always added
// in hitobj.cpp when new application gets created
for (DWORD i = 1; i < pTemplate->m_cFilemaps; i++)
{
// Add to list of file-application mappings
g_FileAppMap.AddFileApplication(pTemplate->m_rgpFilemaps[i]->m_szPathTranslated, pApplication);
// Check if this directory is already registered for change notification
// Pick out the directory portion of the path
TCHAR *szEndOfPath = _tcsrchr(pTemplate->m_rgpFilemaps[i]->m_szPathTranslated, _T('\\'));
size_t cch = DIFF(szEndOfPath - pTemplate->m_rgpFilemaps[i]->m_szPathTranslated) + 1;
if (tempPath.Resize((cch*sizeof(TCHAR)) + sizeof(TCHAR)) == FALSE) {
// if failure, continue registering anyway...
continue;
}
TCHAR *szPath = (TCHAR *) tempPath.QueryPtr();
_tcsncpy(szPath, pTemplate->m_rgpFilemaps[i]->m_szPathTranslated, cch);
szPath[cch] = 0;
// if the template is within the application's physical path, then it is
// already being monitored.
if (pApplication->FPathMonitored(szPath)) {
continue;
}
// Register directory for monitoring
CASPDirMonitorEntry *pDME = NULL;
if (RegisterASPDirMonitorEntry(szPath, &pDME))
{
Assert(pDME);
pApplication->AddDirMonitorEntry(pDME);
}
}
return TRUE;
}
/*===================================================================
BOOL CTemplateCacheManager::ShutdownCacheChangeNotification
Turn off change notification for changes to files in the cache
Parameters:
None
Returns:
Nothing
===================================================================*/
BOOL CTemplateCacheManager::ShutdownCacheChangeNotification()
{
BOOL fDoingMemoryTemplates = TRUE;
// Doesnt happen on Win95
if (!FIsWinNT())
{
return FALSE;
}
LockTemplateCache();
CTemplate *pTemplate = static_cast<CTemplate *>(m_pHashTemplates->MemoryTemplatesBegin());
while (fDoingMemoryTemplates
? !m_pHashTemplates->FMemoryTemplatesDblLinkAtEnd(pTemplate)
: !m_pHashTemplates->FPersistTemplatesDblLinkAtEnd(pTemplate)) {
if(pTemplate->m_rgpFilemaps)
{
for(UINT i = 0; i < pTemplate->m_cFilemaps; i++)
{
// Give up our ref count on the directory monitor entry
if (pTemplate->m_rgpFilemaps[i]->m_pDME)
{
pTemplate->m_rgpFilemaps[i]->m_pDME->Release();
pTemplate->m_rgpFilemaps[i]->m_pDME = NULL;
}
}
}
pTemplate = static_cast<CTemplate *>(pTemplate->PNext());
if (fDoingMemoryTemplates && m_pHashTemplates->FMemoryTemplatesDblLinkAtEnd(pTemplate)) {
fDoingMemoryTemplates = FALSE;
pTemplate = static_cast<CTemplate *>(m_pHashTemplates->PersistTemplatesBegin());
}
}
UnLockTemplateCache();
return TRUE;
}
/* ****************************************************************************
CIncFileMap member functions
*/
/*===================================================================
CIncFileMap::CIncFileMap
Parameters: N/A
Returns: N/A
===================================================================*/
CIncFileMap::CIncFileMap()
{
}
/*===================================================================
CIncFileMap::~CIncFileMap
Parameters: N/A
Returns: N/A
===================================================================*/
CIncFileMap::~CIncFileMap()
{
}
/*===================================================================
CIncFileMap::Init
Parameters: None
Returns: Completion Status
===================================================================*/
HRESULT CIncFileMap::Init()
{
HRESULT hr;
ErrInitCriticalSection(&m_csUpdate, hr);
if (FAILED(hr))
return(hr);
return m_mpszIncFile.Init(CINCFILEBUCKETS);
}
/*===================================================================
CIncFileMap::GetIncFile
Get an inc-file from the cache, first storing it into cache if it is not yet there.
Parameters:
szIncFile - file name
ppIncFile - ptr-to-ptr to inc-file (out-parameter)
Returns: HRESULT
===================================================================*/
HRESULT CIncFileMap::GetIncFile(const TCHAR *szFile, CIncFile **ppIncFile)
{
HRESULT hrInit = S_OK; // return value
LockIncFileCache();
Assert(IsNormalized(szFile));
*ppIncFile = static_cast<CIncFile *>(m_mpszIncFile.FindElem(szFile, _tcslen(szFile)*sizeof(TCHAR)));
// if we have a cached inc-file at this stage, it must be "reliable," so we use it.
// else, if we have no cached inc-file, create a new one.
if (*ppIncFile == NULL)
{
if ((*ppIncFile = new CIncFile) == NULL)
{
UnLockIncFileCache();
return E_OUTOFMEMORY;
}
if (SUCCEEDED(hrInit = (*ppIncFile)->Init(szFile)))
{
// The hash table will hold a reference to the inc file
(*ppIncFile)->AddRef();
m_mpszIncFile.AddElem(*ppIncFile);
}
else
*ppIncFile = NULL;
}
if (SUCCEEDED(hrInit))
{
// The caller will hold a reference to the inc file
(*ppIncFile)->AddRef();
}
UnLockIncFileCache();
return hrInit;
}
/*===================================================================
CIncFileMap::UnInit
Parameters: N/A
Returns: Completion status
===================================================================*/
HRESULT CIncFileMap::UnInit()
{
CIncFile *pNukeIncFile = static_cast<CIncFile *>(m_mpszIncFile.Head());
while (pNukeIncFile != NULL)
{
CIncFile *pNext = static_cast<CIncFile *>(pNukeIncFile->m_pNext);
pNukeIncFile->OnIncFileDecache();
pNukeIncFile->Release();
pNukeIncFile = pNext;
}
DeleteCriticalSection(&m_csUpdate);
return m_mpszIncFile.UnInit();
}
/*===================================================================
CIncFileMap::Flush
Parameters:
szFile - the file to remove from cache
Returns:
None
===================================================================*/
void CIncFileMap::Flush(const TCHAR *szFile)
{
LockTemplateAndIncFileCaches();
Assert(IsNormalized(szFile));
CIncFile *pIncFile = static_cast<CIncFile *>(m_mpszIncFile.FindElem(szFile, _tcslen(szFile)*sizeof(TCHAR)));
if (pIncFile != NULL)
{
if (pIncFile->FlushTemplates())
{
// Remove from hash table
m_mpszIncFile.DeleteElem(szFile, _tcslen(szFile)*sizeof(TCHAR));
// The hash table gave up its reference
// to the incfile
pIncFile->OnIncFileDecache();
pIncFile->Release();
}
}
UnLockTemplateAndIncFileCaches();
}
/*===================================================================
CIncFileMap::FlushFiles
Parameters:
szFile - the file prefix to search for in cache
Returns:
None
===================================================================*/
void CIncFileMap::FlushFiles(const TCHAR *szFilePrefix)
{
LockTemplateAndIncFileCaches();
Assert(IsNormalized(szFilePrefix));
CIncFile *pIncFile = static_cast<CIncFile *>(m_mpszIncFile.Head());
while (pIncFile != NULL)
{
CIncFile *pNextFile = static_cast<CIncFile *>(pIncFile->m_pNext);
int cchFilePrefix = _tcslen(szFilePrefix);
if (pIncFile->m_cbKey >= (cchFilePrefix*(int)sizeof(TCHAR)) &&
_tcsncmp(reinterpret_cast<TCHAR *>(pIncFile->m_pKey), szFilePrefix, cchFilePrefix) == 0)
{
#if UNICODE
DBGPRINTF((DBG_CONTEXT, "FlushFiles: flushing %S\n", pIncFile->m_pKey));
#else
DBGPRINTF((DBG_CONTEXT, "FlushFiles: flushing %s\n", pIncFile->m_pKey));
#endif
if (pIncFile->FlushTemplates())
{
// Remove from hash table
m_mpszIncFile.DeleteElem(pIncFile->m_pKey, pIncFile->m_cbKey);
// The hash table gave up its reference
// to the incfile
pIncFile->OnIncFileDecache();
pIncFile->Release();
}
}
pIncFile = pNextFile;
}
UnLockTemplateAndIncFileCaches();
}
/* ****************************************************************************
Non-class support functions
*/
/*===================================================================
FFileChangedSinceCached
Has the file changed since it was cached?
Parameters:
szFile - file name
ftPrevWriteTime - the file's "previous write time"
(its last-write-time value when the file was cached)
Returns:
TRUE or FALSE
===================================================================*/
BOOL FFileChangedSinceCached(const TCHAR *szFile, FILETIME& ftPrevWriteTime)
{
WIN32_FILE_ATTRIBUTE_DATA fad; // win32 file attributes data structure
BOOL fRet = FALSE; // return value
// This fn doesnt exist on Win95. On Win95 we do no caching anyway, so we dont care.
if (!FIsWinNT())
return(TRUE);
if (FAILED(AspGetFileAttributes(szFile, &fad)))
{
// assume file was changed if get file attributes failed
fRet = TRUE;
}
if( 0 != CompareFileTime( &ftPrevWriteTime, &(fad.ftLastWriteTime) ) )
{
// file was changed if file times differ
fRet = TRUE;
}
return fRet;
}
/*===================================================================
CTemplateCacheManager::CTemplateHashTable::TrimPersistCache
Parameters:
dwTrimCount - the number of templates to trim from the cache
Returns:
TRUE - if dwTrimCount was actually trimmed
FALSE - if exited before dwTrimCount was met
===================================================================*/
BOOL CTemplateCacheManager::CTemplateHashTable::TrimPersistCache(DWORD dwTrimCount)
{
// enter a while loop to trim until the count is reached
while(dwTrimCount--) {
// if there isn't anything else to trim, we're done. Return FALSE
// to indicate that dwTrimCount was not met.
if (m_dwPersistedTemplates == 0) {
return(FALSE);
}
else {
CTemplate *pTemplate;
// get the oldest template from the list
pTemplate = static_cast<CTemplate *>(PersistTemplatesEnd());
// remove the template.
RemoveTemplate(pTemplate);
ZapTemplate(pTemplate);
}
}
// return TRUE to indicate that the TrimCount was met.
return(TRUE);
}
/*===================================================================
CTemplateCacheManager::CTemplateHashTable::ScavengePersistCache
Parameters:
<NONE>
Returns:
VOID
===================================================================*/
VOID CTemplateCacheManager::CTemplateHashTable::ScavengePersistCache()
{
CTemplate *pTemplate;
CTemplate *pTemplateNext;
// enter a for loop to look at all persisted templates to see if
// any memory can be freed. It's memory can be freed only if the
// ref count is 1 (the sole ref count is for the cache). Also note
// that the list is re-ordered to move templates to the head of the
// list that can't have their memory freed at this time because of
// the ref count.
for (pTemplate = static_cast<CTemplate *>(PersistTemplatesBegin());
(pTemplate != static_cast<CTemplate *>(&m_listPersistTemplates)) && (pTemplate->m_pbStart != NULL);
pTemplate = pTemplateNext) {
pTemplateNext = static_cast<CTemplate *>(pTemplate->PNext());
// this check should be safe. The only risk is that we miss a release
// of the template from 2 to 1, in which case will miss it this time
// but get it the next time through. AddRef from 1 to 2 is impossible
// to interrupt because it couldn't be on this list when it gets AddRef'd
// from 1 to 2 and moving it from this list is protected by the template
// cache lock which we should be under.
if (pTemplate->m_cRefs == 1) {
// remove the memory
CTemplate::LargeFree(pTemplate->m_pbStart);
pTemplate->m_pbStart = NULL;
}
else {
// if some is still using it, move the template to the head of the
// list so that we'll check again later.
pTemplate->PrependTo(m_listPersistTemplates);
}
}
}
/*===================================================================
GetAggregatedTemplCounter()
Returns the Template Perf Counter. To do this, initializes a private
copy of the perfmainblock and aggregates the stats into it.
===================================================================*/
static DWORD GetAggregatedTemplCounter()
{
CPerfMainBlock perfSharedBlk;
DWORD pdwCounters[C_PERF_PROC_COUNTERS];
BOOL bInited = FALSE;
memset(pdwCounters, 0, sizeof(pdwCounters));
if (!(bInited = (perfSharedBlk.Init() == S_OK)));
else {
perfSharedBlk.GetStats(pdwCounters);
}
if (bInited)
perfSharedBlk.UnInit();
return(pdwCounters[ID_TEMPLCACHE]);
}
/*===================================================================
CTemplateCacheManager::InitPersistCache
Parameters:
[none]
Returns:
BOOL to indicate if the init was successful
===================================================================*/
BOOL CTemplateCacheManager::InitPersistCache()
{
HANDLE hImpersonationToken = NULL;
BOOL fRevertedToSelf = FALSE;
HANDLE hThread;
hThread = GetCurrentThread();
if (OpenThreadToken( hThread,
TOKEN_READ | TOKEN_IMPERSONATE,
TRUE,
&hImpersonationToken )) {
RevertToSelf();
fRevertedToSelf = TRUE;
}
// get the PersistCacheDir from the globals table
strcpy(m_szPersistCacheDir,Glob(pszPersistTemplateDir));
// Check to see if the directory exists...
DWORD dirAttribs = GetFileAttributesA(m_szPersistCacheDir);
if ((dirAttribs == 0xffffffff)
|| !(dirAttribs & FILE_ATTRIBUTE_DIRECTORY)) {
MSG_Error(IDS_CACHE_DIR_MISSING);
m_fFailedToInitPersistCache = TRUE;
}
else {
// append the trailing slash to the directory name
strcat(m_szPersistCacheDir,"\\");
// next, cleanup the temp directory.
// since asp.dll can execute in many process simultaneously, we'll
// check the perf counter to see if there are any templates
// cached anywhere on the system.
if (GetAggregatedTemplCounter() == 0) {
CHAR DirectoryWildcard[MAX_PATH];
WIN32_FIND_DATAA win32FindData;
strcpy(DirectoryWildcard, m_szPersistCacheDir);
strcat(DirectoryWildcard, "ASP*.TMP");
// use FindFirstFile to begin the enumeration of all files in the
// temp directory.
HANDLE hDirectory = FindFirstFileA( DirectoryWildcard, &win32FindData );
if ( hDirectory != INVALID_HANDLE_VALUE ) {
BOOL success = TRUE;
while ( success ) {
char fileName[MAX_PATH];
// FindFirst/FindNext returns just the filename. Build up
// the entire path and then call DeleteFile. We'll ignore
// errors as there isn't much we could do with the error
// except fail the cache init.
strcpy( fileName, m_szPersistCacheDir );
strcat( fileName, win32FindData.cFileName );
DeleteFileA( fileName );
success = FindNextFileA( hDirectory, &win32FindData );
}
FindClose( hDirectory );
}
else {
GetLastError();
}
}
}
if (fRevertedToSelf) {
SetThreadToken(&hThread, hImpersonationToken);
}
return(!m_fFailedToInitPersistCache);
}
/*===================================================================
CTemplateCacheManager::CTemplateHashTable::CanPersistTemplate
Parameters:
pTemplate - The template to test for persistability
Returns:
BOOL to indicate if template can be persisted.
===================================================================*/
BOOL CTemplateCacheManager::CTemplateHashTable::CanPersistTemplate(CTemplate *pTemplate)
{
// if MaxFiles is zero, then the persist cache is disabled
if (Glob(dwPersistTemplateMaxFiles) == 0) {
return(FALSE);
}
// can't persist if the persist cache failed to init
if (m_fFailedToInitPersistCache == TRUE) {
return(FALSE);
}
// can't persist templates that are marked as debuggable. The
// script engines need access to the memory.
if (pTemplate->FDebuggable()) {
return(FALSE);
}
// at this point, we're going to return true. The next part of the code
// trims the cache as necessary.
if (m_dwPersistedTemplates >= Glob(dwPersistTemplateMaxFiles)) {
TrimPersistCache(m_dwPersistedTemplates - Glob(dwPersistTemplateMaxFiles) + 1);
}
return(TRUE);
}
/*===================================================================
CTemplateCacheManager::CTemplateHashTable::InsertTemplate
Parameters:
pTemplate - Template to insert into the memory cache
Returns:
LK_RETCODE indicating the success of the insertion
===================================================================*/
LK_RETCODE CTemplateCacheManager::CTemplateHashTable::InsertTemplate(CTemplate *pTemplate)
{
LK_RETCODE rcode = InsertRecord(pTemplate, true);
if (rcode == LK_SUCCESS) {
#ifndef PERF_DISABLE
g_PerfData.Incr_MEMORYTEMPLCACHE();
g_PerfData.Incr_TEMPLCACHE();
#endif
m_dwInMemoryTemplates++;
pTemplate->PrependTo(m_listMemoryTemplates);
pTemplate->SetHashTablePtr(this);
}
ScavengePersistCache();
return rcode;
}
/*===================================================================
CTemplateCacheManager::CTemplateHashTable::RemoveTemplate
Parameters:
pTemplate - Template to remove from cache
fPersist - indicate if memory template is a candidate for persist
Returns:
LK_RETCODE indicating the success of the removal
===================================================================*/
LK_RETCODE CTemplateCacheManager::CTemplateHashTable::RemoveTemplate(CTemplate *pTemplate, BOOL fPersist)
{
LK_RETCODE rcode = LK_SUCCESS;
#if DBG_PERSTEMPL
DBGPRINTF((DBG_CONTEXT,
"RemoveTemplate entered.\n\tTemplate = %s.\n\tfPersist = %d.\n\tFIsPersisted = %d\n",
pTemplate->GetSourceFileName(),
fPersist,
pTemplate->FIsPersisted()));
#endif
// if the template isn't in the cache, or if the template isn't on this
// particular hash table, then just bail. Nothing to
// do here. It may not be on this particular hash table because the entire
// table may have been torn off the global cache manager and scheduled for
// cleanup on the flush thread. In this case, we're checking the wrong
// table. The flush thread will eventually clean this one up.
if (pTemplate->FIsEmpty() || (pTemplate->GetHashTablePtr() != this)) {
return LK_NO_SUCH_KEY;
}
// no matter what, this template is going to be unlinked from it's
// current CDblLink
pTemplate->UnLink();
// update the appropriate counter
if (pTemplate->FIsPersisted() == FALSE) {
// decrement the number of InMemoryTemplates...
#ifndef PERF_DISABLE
g_PerfData.Decr_MEMORYTEMPLCACHE();
#endif
m_dwInMemoryTemplates--;
}
else {
m_dwPersistedTemplates--;
}
// if asked to be persisted, see if it's a candidate to be persisted.
if (fPersist && CanPersistTemplate(pTemplate)) {
// so persist it.
if (pTemplate->PersistData(m_szPersistCacheDir) != S_OK) {
// a failure will result in the record being deleted.
#ifndef PERF_DISABLE
g_PerfData.Decr_TEMPLCACHE();
#endif
rcode = DeleteRecord(pTemplate);
}
else {
// if successfully persisted, then add to the list of
// persisted templates
pTemplate->PrependTo(m_listPersistTemplates);
m_dwPersistedTemplates++;
}
}
else {
#ifndef PERF_DISABLE
g_PerfData.Decr_TEMPLCACHE();
#endif
// if not asked to persist, then delete the record.
rcode = DeleteRecord(pTemplate);
}
ScavengePersistCache();
return rcode;
}
/*===================================================================
CTemplateCacheManager::CTemplateHashTable::FindTemplate
Parameters:
rTemplate - the key for the template being looked up
Returns:
LK_RETCODE indicating the success of the look up
===================================================================*/
LK_RETCODE CTemplateCacheManager::CTemplateHashTable::FindTemplate(const CTemplateKey &rTemplateKey, CTemplate **ppTemplate)
{
#if DBG_PERSTEMPL
DBGPRINTF((DBG_CONTEXT,
"FindTemplate entered\n\tLooking for %s\n",
rTemplateKey.szPathTranslated));
#endif
#ifndef PERF_DISABLE
g_PerfData.Incr_MEMORYTEMPLCACHETRYS();
g_PerfData.Incr_TEMPLCACHETRYS();
#endif
LK_RETCODE rcode = FindKey(&rTemplateKey, ppTemplate);
// see if we found it.
if (rcode == LK_SUCCESS) {
#if DBG_PERSTEMPL
DBGPRINTF((DBG_CONTEXT,
"Template found\n\tfPersisted = %d\n",
(*ppTemplate)->FIsPersisted()));
#endif
#ifndef PERF_DISABLE
g_PerfData.Incr_TEMPLCACHEHITS();
#endif
// found it. Is it persisted?
if ((*ppTemplate)->FIsPersisted()) {
// It is persisted. Unlink it from the persisted list.
(*ppTemplate)->UnLink();
m_dwPersistedTemplates--;
// unpersist it
if ((*ppTemplate)->UnPersistData() != S_OK) {
// error occurred
// get the template out of the cache
DeleteRecord(*ppTemplate);
// release the reference that the cache had on the template
(*ppTemplate)->Release();
// NULL out *ppTemplate so that the caller doesn't think they
// got a valid template
*ppTemplate = NULL;
#ifndef PERF_DISABLE
g_PerfData.Decr_TEMPLCACHE();
#endif
// return NO_SUCH_KEY so that a new template will be built
return(LK_NO_SUCH_KEY);
}
// bump the number of in memory templates
#ifndef PERF_DISABLE
g_PerfData.Incr_MEMORYTEMPLCACHE();
#endif
m_dwInMemoryTemplates++;
}
else {
#ifndef PERF_DISABLE
g_PerfData.Incr_MEMORYTEMPLCACHEHITS();
#endif
}
// add it to, or move it to the top of, the memory templates
(*ppTemplate)->PrependTo(m_listMemoryTemplates);
}
ScavengePersistCache();
return rcode;
}