Leaked source code of windows server 2003
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.
 
 
 
 
 
 

2003 lines
60 KiB

/*-----------------------------------------------------------------------------
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;
BOOL CTemplateCacheManager::m_fFailedToInitPersistCache = FALSE;
char CTemplateCacheManager::m_szPersistCacheDir[MAX_PATH];
DWORD g_nScavengedPersisted = 0;
DWORD g_nScavengedPurged = 0;
DWORD g_nScavengedPersistFailed = 0;
extern BOOL g_fUNCChangeNotificationEnabled;
/*===================================================================
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;
m_dwTemplateCacheTag = 0;
m_hOnInitCleanupThread = NULL;
m_cCleanupThreads = 0;
ZeroMemory (&m_hCleanupThreads, sizeof(m_hCleanupThreads));
}
/*===================================================================
CTemplateCacheManager::~CTemplateCacheManager
Parameters: N/A
Returns: N/A
===================================================================*/
CTemplateCacheManager::~CTemplateCacheManager()
{
if (m_hOnInitCleanupThread != NULL) {
WaitForSingleObject(m_hOnInitCleanupThread, INFINITE);
CloseHandle(m_hOnInitCleanupThread);
m_hOnInitCleanupThread = NULL;
}
if (!m_fFailedToInitPersistCache) {
RemoveDirectoryA(m_szPersistCacheDir);
}
}
/*===================================================================
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;
// Initialize the Cache Tag.
m_dwTemplateCacheTag = GetTickCount();
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;
// give any flush threads a chance to finish. This is necessary
// to prevent an AV by LKRHash.
LockTemplateCache();
if (m_cCleanupThreads)
{
WaitForMultipleObjects(
m_cCleanupThreads,
m_hCleanupThreads,
TRUE, // wait for ALL event
INFINITE); // Wait for as long as it takes.
while( m_cCleanupThreads ) {
CloseHandle(m_hCleanupThreads[ --m_cCleanupThreads ]);
}
}
UnLockTemplateCache();
#ifndef PERF_DISABLE
g_PerfData.Zero_MEMORYTEMPLCACHE();
g_PerfData.Zero_TEMPLCACHE();
#endif
DeleteCriticalSection(&m_csUpdate);
return S_OK;
}
/*===================================================================
CTemplateCacheManager::FindCached
Get a template from the cache only
NOTE: FindCached just checks for the existance of the template. It will not check if the files that make up the
template are valid or not like Load does. Please keep this assumption in mind while using this Method.
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?
BOOL fNeedsCheck = FALSE;
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,&fNeedsCheck);
}
if (*ppTemplate != NULL)
{
// Template found in cache -> use it
(*ppTemplate)->AddRef();
*pfTemplateInCache = TRUE;
(*ppTemplate)->IncrUseCount();
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,&fNeedsCheck);
if (*ppTemplate != NULL)
{
// Template found in cache -> use it
(*ppTemplate)->AddRef();
(*ppTemplate)->IncrUseCount();
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;
}
if (SUCCEEDED(hr) && fNeedsCheck && *ppTemplate != NULL)
{
if (!(*ppTemplate)->ValidateSourceFiles(pHitObj->PIReq()))
{
// Template is invalid (out of date)
LockTemplateCache();
if (m_pHashTemplates->RemoveTemplate(*ppTemplate) == LK_SUCCESS)
ZapTemplate (*ppTemplate);
UnLockTemplateCache();
}
}
LockTemplateCache();
BOOL bTemplateRemoved = FALSE;
// 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());
// don't call ScavengePersistCache in this call. We'll call it once at the end
m_pHashTemplates->RemoveTemplate(pOldTemplate, TRUE, FALSE);
bTemplateRemoved = 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);
}
}
// call ScavengePersistCache() once here...
if (bTemplateRemoved)
m_pHashTemplates->ScavengePersistCache();
UnLockTemplateCache();
// Store a pointer to the template with the application
// if we haven't already done so
if (SUCCEEDED(hr) && *ppTemplate && fRunGlobalAsp && pHitObj->PAppln()->PGlobalTemplate() == NULL)
pHitObj->PAppln()->SetGlobalTemplate(*ppTemplate);
// If we are shutting down, don't request change notification
if (!IsShutDownInProgress() && *ppTemplate)
{
// If running on NT, and we just compiled the template
// register all the directories used by this template
// for change notification
if (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 (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(BOOL fDoLazyFlush)
{
if (fDoLazyFlush)
{
m_dwTemplateCacheTag = GetTickCount();
DBGPRINTF ((DBG_CONTEXT,"Using new Cache Tag to Invalidate Template\n"));
return;
}
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
//
DWORD nThreadIndex;
//
// if no threads started yet, use the first slot
//
if (m_cCleanupThreads) {
nThreadIndex = 0;
goto create_new_thread;
}
//
// see if there is a thread that terminated
//
nThreadIndex = WaitForMultipleObjects(
m_cCleanupThreads,
m_hCleanupThreads,
FALSE, // wait for any event
0); // return immediately
Assert(nThreadIndex == WAIT_TIMEOUT);
if (m_cCleanupThreads < MAX_CLEANUP_THREADS)
{
//
// just get the next index
//
nThreadIndex = m_cCleanupThreads;
goto create_new_thread;
}
goto Cleanup;
create_new_thread:
hnd = CreateThread(NULL, 0, CTemplateCacheManager::FlushHashTableThread, m_pHashTemplates, 0, NULL);
if (hnd)
{
//
// close the previous handle if we are reusing an entry
//
if (nThreadIndex < m_cCleanupThreads)
{
//
// we are reusing a slot from a terminated thread
//
CloseHandle(m_hCleanupThreads[ nThreadIndex ]);
} else {
//
// we are using a new slot
//
Assert(nThreadIndex == m_cCleanupThreads);
m_cCleanupThreads++;
}
m_hCleanupThreads[ nThreadIndex ] = hnd;
// 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"));
m_pHashTemplates = pNewTable;
UnLockTemplateAndIncFileCaches();
return;
}
}
Cleanup:
// 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;
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)
{
DWORD dwValue = 0;
// If the template has a UNC include or main file & we the registry key is absent (default) or the key is present and disabled.
// Then we will use the flag this file one that requires monitoring to be performed by ASP.
if (pTemplate->FIsUNC() && !g_fUNCChangeNotificationEnabled)
{
pTemplate->m_fNeedsMonitoring = TRUE;
pTemplate->m_dwLastMonitored = GetTickCount ();
return TRUE;
}
//else (Local File or UNC file with ChangeNotification Enabled)
STACK_BUFFER( tempPath, MAX_PATH );
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, unless disabled through the registry, flag the file for manual monitoring after releasing previously registered DME's
if (SUCCEEDED(g_AspRegistryParams.GetFileMonitoringEnabled(&dwValue)) && dwValue == 0)
continue;
if (i > 0) {
while (--i) {
pTemplate->m_rgpFilemaps[i]->m_pDME->Release();
pTemplate->m_rgpFilemaps[i]->m_pDME = NULL;
}
}
pTemplate->m_fNeedsMonitoring = TRUE;
pTemplate->m_dwLastMonitored = GetTickCount ();
return TRUE;
}
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;
}
}
// Without the monitoring magic we would return FALSE here but we give
// it one more chance to live, except if the registry parameter is set to disable it.
// if the registry setting asks for this to be disabled disable it.
if (SUCCEEDED(g_AspRegistryParams.GetFileMonitoringEnabled(&dwValue)) && dwValue == 0)
return FALSE;
pTemplate->m_fNeedsMonitoring = TRUE;
pTemplate->m_dwLastMonitored = GetTickCount ();
return TRUE;
}
}
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 );
// 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;
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
{
//
// Init can fail with E_OUTOFMEMORY on call to SmallAlloc or due to a failure to init critical section.
// Cleanup allocated memory for ppIncFile if that is the case.
//
delete *ppIncFile;
*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, HANDLE hFile, FILETIME& ftPrevWriteTime)
{
BOOL fRet = FALSE; // return value
FILETIME ftLastWriteTime;
if (FAILED(AspGetFileAttributes(szFile, hFile, &ftLastWriteTime)))
{
// assume file was changed if get file attributes failed
fRet = TRUE;
}
if( 0 != CompareFileTime( &ftPrevWriteTime, &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) {
BOOL fDeleteRecord = FALSE;
if (pTemplate->m_cUseCount == 1) {
// not going to continue to cache or persist a template that
// was used just once.
fDeleteRecord = TRUE;
g_nScavengedPurged++;
}
else if (pTemplate->PersistData(m_szPersistCacheDir) != S_OK) {
// a failure will result in the record being deleted.
fDeleteRecord = TRUE;
g_nScavengedPersistFailed++;
}
else {
g_nScavengedPersisted++;
// remove the memory
CTemplate::LargeFree(pTemplate->m_pbStart);
pTemplate->m_pbStart = NULL;
}
if (fDeleteRecord) {
if (RemoveTemplate(pTemplate, FALSE, FALSE) == LK_SUCCESS)
ZapTemplate(pTemplate);
}
}
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::OnInitCleanup
Parameters:
[none]
Returns:
BOOL to indicate if the init was successful
===================================================================*/
DWORD CTemplateCacheManager::OnInitCleanup(VOID *p)
{
// we are using a single character buffer to construct all directory and file names we use.
CHAR szDirBuffer[ MAX_PATH + 32];
// first build the template for the cache directories
INT iLen = _snprintf(szDirBuffer,
MAX_PATH,
"%s\\PID*.TMP",
Glob(pszPersistTemplateDir),
GetCurrentProcessId());
if ((iLen <= 0) || (iLen >= MAX_PATH)) {
return ERROR_INSUFFICIENT_BUFFER;
}
// outer loop: find all directories, retrieve PID, if process not exist, go to inner
// inner loop: delete all ASP cache files
WIN32_FIND_DATAA RootDir_FindData;
HANDLE hRootDir = FindFirstFileA( szDirBuffer, &RootDir_FindData );
WIN32_FIND_DATAA FileDir_FindData;
HANDLE hFileDir;
if ( hRootDir != INVALID_HANDLE_VALUE ) {
// point to where the cache subdirectory begins
CHAR *pDir = szDirBuffer + strlen(Glob(pszPersistTemplateDir)) + 1;
CHAR *pFile;
CHAR *pc;
DWORD pid;
HANDLE hProcess;
do {
// validate file name is what we expect and extract the PID
// we know the first letters are 'PID' as that was the search criteria
pc = RootDir_FindData.cFileName + 3;
for(pid = 0; isdigit(*pc); pid = pid*10 + *pc++ - '0');
if (pid == 0) {
continue;
}
hProcess = OpenProcess(SYNCHRONIZE, FALSE, pid);
if (hProcess) {
// the process is alive, so leave the directory alone.
CloseHandle( hProcess );
continue;
}
if (GetLastError() != ERROR_INVALID_PARAMETER) {
// we got an error other than "no such process"
Assert( GetLastError() == ERROR_ACCESS_DENIED);
continue;
}
// The process does not exist, so clean up the directory
// Add the directory name, append a '\', append the file template, and point pFile
// to the position where the file name starts
strcpy( pDir, RootDir_FindData.cFileName );
pFile = pDir + strlen(pDir);
*pFile++ = '\\';
strcpy( pFile, "ASP*.TMP" );
// search the subdirectory for asp template cache files
hFileDir = FindFirstFileA( szDirBuffer, &FileDir_FindData );
if ( hFileDir != INVALID_HANDLE_VALUE ) {
do {
// append the file name and delete the file
strcpy( pFile, FileDir_FindData.cFileName);
DeleteFileA( szDirBuffer );
} while (FindNextFileA( hFileDir, &FileDir_FindData ));
FindClose( hFileDir );
}
// Now zap the last part of the path and delete the directory
*--pFile = '\0';
RemoveDirectoryA( szDirBuffer );
} while (FindNextFileA( hRootDir, &RootDir_FindData ));
FindClose( hRootDir );
}
return S_OK;
}
/*===================================================================
CTemplateCacheManager::InitPersistCache
Parameters:
[none]
Returns:
BOOL to indicate if the init was successful
===================================================================*/
BOOL CTemplateCacheManager::InitPersistCache(CIsapiReqInfo *pIReq)
{
HANDLE hImpersonationToken = NULL;
BOOL fRevertedToSelf = FALSE;
DWORD dirAttribs;
INT iLen;
UINT uiEventSubId = 0;
DWORD dwError = ERROR_SUCCESS;
if (OpenThreadToken( GetCurrentThread(),
TOKEN_READ | TOKEN_IMPERSONATE,
TRUE,
&hImpersonationToken )) {
RevertToSelf();
fRevertedToSelf = TRUE;
}
// build the cache directory name
iLen = _snprintf(m_szPersistCacheDir,
sizeof(m_szPersistCacheDir),
"%s\\PID%d.TMP",
Glob(pszPersistTemplateDir),
GetCurrentProcessId());
if ((iLen <= 0) || (iLen >= sizeof(m_szPersistCacheDir))) {
uiEventSubId = IDS_CACHE_DIR_NAME_TOO_LONG;
goto LExit;
}
// If directory exists, rename it to something else (must be an old leftover
dirAttribs = GetFileAttributesA(m_szPersistCacheDir);
if (dirAttribs != INVALID_FILE_ATTRIBUTES) {
// recreate the same dir name with a leading 0 for the pid...
CHAR szNewDirName[ sizeof(m_szPersistCacheDir) ];
iLen = _snprintf(szNewDirName,
sizeof(szNewDirName),
"%s\\PID0%d.TMP",
Glob(pszPersistTemplateDir),
GetCurrentProcessId());
if ((iLen <= 0) || (iLen >= sizeof(szNewDirName))) {
uiEventSubId = IDS_CACHE_DIR_NAME_TOO_LONG;
goto LExit;
}
if (!MoveFileA(m_szPersistCacheDir, szNewDirName)) {
uiEventSubId = IDS_CACHE_SUBDIR_CREATION_FAILED;
dwError = GetLastError();
goto LExit;
}
}
// Now create it
if (CreateDirectoryA(m_szPersistCacheDir, NULL)) {
dirAttribs = GetFileAttributesA(m_szPersistCacheDir);
} else {
uiEventSubId = IDS_CACHE_SUBDIR_CREATION_FAILED;
dwError = GetLastError();
goto LExit;
}
if ((dirAttribs == INVALID_FILE_ATTRIBUTES)
|| !(dirAttribs & FILE_ATTRIBUTE_DIRECTORY)) {
uiEventSubId = IDS_CACHE_SUBDIR_MISSING;
goto LExit;
}
// start the scavenger thread
m_hOnInitCleanupThread = CreateThread(NULL,
0,
CTemplateCacheManager::OnInitCleanup,
NULL,
0,
NULL);
LExit:
if (uiEventSubId != 0) {
//
// an initialization error has occured. log it and indicate initialization fail.
//
MSG_Error(IDS_CACHE_INIT_FAILED, pIReq->QueryPszAppPoolIdA(), uiEventSubId, dwError);
m_fFailedToInitPersistCache = TRUE;
}
if (fRevertedToSelf) {
SetThreadToken(NULL, hImpersonationToken);
CloseHandle(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);
}
//
// Cannot write an Encrypted file to disk as we would be writing it in plaintext.
//
if (pTemplate->FIsEncrypted())
{
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))
{
//
// Ignore the value returned from TrimPersistCache as we are not concerned if we met the
// trim count or not. In anycase, there should be 1 available location to add the new template.
// Optimization may be to free up couple of places (configurable through the metabase) to achieve performance.
//
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,
BOOL fScavengePersistCache)
{
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)) {
pTemplate->m_fIsPersisted = TRUE;
// 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);
}
if (fScavengePersistCache)
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, BOOL *pfNeedsCheck)
{
#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);
}
if (pfNeedsCheck && *ppTemplate && (*ppTemplate)->FNeedsValidation() && (*ppTemplate)->FIsValid())
*pfNeedsCheck = TRUE;
ScavengePersistCache();
return rcode;
}