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

  1. /*-----------------------------------------------------------------------------
  2. Microsoft Denali
  3. Microsoft Confidential
  4. Copyright 1996 Microsoft Corporation. All Rights Reserved.
  5. Component: Template Cache Manager
  6. File: CacheMgr.cpp
  7. Owner: DGottner
  8. Template cache manager implementation
  9. -----------------------------------------------------------------------------*/
  10. #include "denpre.h"
  11. #pragma hdrstop
  12. #include "perfdata.h"
  13. #include "memchk.h"
  14. CTemplateCacheManager g_TemplateCache;
  15. CIncFileMap g_IncFileMap;
  16. BOOL CTemplateCacheManager::m_fFailedToInitPersistCache = FALSE;
  17. char CTemplateCacheManager::m_szPersistCacheDir[MAX_PATH];
  18. DWORD g_nScavengedPersisted = 0;
  19. DWORD g_nScavengedPurged = 0;
  20. DWORD g_nScavengedPersistFailed = 0;
  21. extern BOOL g_fUNCChangeNotificationEnabled;
  22. /*===================================================================
  23. ZapTemplate
  24. Decrement the ref. count of a template to remove it from cache.
  25. If the template is global.asa, that's all we do because application
  26. manager has the last reference. Otherwise, we Release the template
  27. by calling CTemplate::End() to also free references to it from the
  28. debugger.
  29. Parameters: pTemplate - template pointer to Release() from cache
  30. Returns: new ref. count
  31. ===================================================================*/
  32. static inline
  33. ULONG ZapTemplate(CTemplate *pTemplate)
  34. {
  35. if (! pTemplate->FGlobalAsa())
  36. return pTemplate->End();
  37. else
  38. return pTemplate->Release();
  39. }
  40. /* ****************************************************************************
  41. CCacheManager member functions
  42. */
  43. /*===================================================================
  44. CTemplateCacheManager::CTemplateCacheManager
  45. Parameters: N/A
  46. Returns: N/A
  47. ===================================================================*/
  48. CTemplateCacheManager::CTemplateCacheManager()
  49. {
  50. m_pHashTemplates = NULL;
  51. m_szPersistCacheDir[0] = '\0';
  52. m_fFailedToInitPersistCache = FALSE;
  53. m_dwTemplateCacheTag = 0;
  54. m_hOnInitCleanupThread = NULL;
  55. m_cCleanupThreads = 0;
  56. ZeroMemory (&m_hCleanupThreads, sizeof(m_hCleanupThreads));
  57. }
  58. /*===================================================================
  59. CTemplateCacheManager::~CTemplateCacheManager
  60. Parameters: N/A
  61. Returns: N/A
  62. ===================================================================*/
  63. CTemplateCacheManager::~CTemplateCacheManager()
  64. {
  65. if (m_hOnInitCleanupThread != NULL) {
  66. WaitForSingleObject(m_hOnInitCleanupThread, INFINITE);
  67. CloseHandle(m_hOnInitCleanupThread);
  68. m_hOnInitCleanupThread = NULL;
  69. }
  70. if (!m_fFailedToInitPersistCache) {
  71. RemoveDirectoryA(m_szPersistCacheDir);
  72. }
  73. }
  74. /*===================================================================
  75. CTemplateCacheManager::Init
  76. Init the template cache manager - phase 1 - that which can be done
  77. with just default values in Glob.
  78. Parameters: None
  79. Returns: Completion Status
  80. ===================================================================*/
  81. HRESULT CTemplateCacheManager::Init()
  82. {
  83. HRESULT hrInit;
  84. ErrInitCriticalSection(&m_csUpdate, hrInit);
  85. if (FAILED(hrInit))
  86. return(hrInit);
  87. // allocate the initial CTemplateHashTable
  88. m_pHashTemplates = new CTemplateHashTable;
  89. // Initialize the Cache Tag.
  90. m_dwTemplateCacheTag = GetTickCount();
  91. return S_OK;
  92. }
  93. /* ****************************************************************************
  94. CTemplateCacheManager member functions
  95. */
  96. /*===================================================================
  97. CTemplateCacheManager::UnInit
  98. Parameters: N/A
  99. Returns: Completion status
  100. ===================================================================*/
  101. HRESULT CTemplateCacheManager::UnInit()
  102. {
  103. if (m_pHashTemplates) {
  104. while (! m_pHashTemplates->FMemoryTemplatesIsEmpty()) {
  105. CTemplate *pTemplate = static_cast<CTemplate *>(m_pHashTemplates->MemoryTemplatesBegin());
  106. m_pHashTemplates->RemoveTemplate(pTemplate);
  107. ZapTemplate(pTemplate);
  108. }
  109. while (! m_pHashTemplates->FPersistTemplatesIsEmpty()) {
  110. CTemplate *pTemplate = static_cast<CTemplate *>(m_pHashTemplates->PersistTemplatesBegin());
  111. m_pHashTemplates->RemoveTemplate(pTemplate);
  112. ZapTemplate(pTemplate);
  113. }
  114. }
  115. delete m_pHashTemplates;
  116. m_pHashTemplates = NULL;
  117. // give any flush threads a chance to finish. This is necessary
  118. // to prevent an AV by LKRHash.
  119. LockTemplateCache();
  120. if (m_cCleanupThreads)
  121. {
  122. WaitForMultipleObjects(
  123. m_cCleanupThreads,
  124. m_hCleanupThreads,
  125. TRUE, // wait for ALL event
  126. INFINITE); // Wait for as long as it takes.
  127. while( m_cCleanupThreads ) {
  128. CloseHandle(m_hCleanupThreads[ --m_cCleanupThreads ]);
  129. }
  130. }
  131. UnLockTemplateCache();
  132. #ifndef PERF_DISABLE
  133. g_PerfData.Zero_MEMORYTEMPLCACHE();
  134. g_PerfData.Zero_TEMPLCACHE();
  135. #endif
  136. DeleteCriticalSection(&m_csUpdate);
  137. return S_OK;
  138. }
  139. /*===================================================================
  140. CTemplateCacheManager::FindCached
  141. Get a template from the cache only
  142. NOTE: FindCached just checks for the existance of the template. It will not check if the files that make up the
  143. template are valid or not like Load does. Please keep this assumption in mind while using this Method.
  144. Parameters:
  145. szFile - file to find in the cache
  146. ppTemplate - [out] template object found
  147. Returns:
  148. HRESULT (S_OK if found, S_FALSE if noe found)
  149. ===================================================================*/
  150. HRESULT CTemplateCacheManager::FindCached(const TCHAR *szFile, DWORD dwInstanceID, CTemplate **ppTemplate)
  151. {
  152. Assert(IsNormalized(szFile));
  153. if (!ppTemplate)
  154. return E_POINTER;
  155. LockTemplateCache();
  156. m_pHashTemplates->FindTemplate(CTemplateKey(szFile, dwInstanceID), ppTemplate);
  157. if (*ppTemplate)
  158. {
  159. if (!(*ppTemplate)->m_fReadyForUse)
  160. *ppTemplate = NULL; // not ready - as if nor found
  161. else
  162. (*ppTemplate)->AddRef(); // addref inside critical section
  163. }
  164. UnLockTemplateCache();
  165. return *ppTemplate? S_OK : S_FALSE;
  166. }
  167. /*===================================================================
  168. CTemplateCacheManager::Load
  169. Get a template from the cache, or load it into cache
  170. Parameters:
  171. szFile - file to load into the cache
  172. Returns: N/A
  173. ===================================================================*/
  174. HRESULT CTemplateCacheManager::Load(BOOL fRunGlobalAsp, const TCHAR *szFile, DWORD dwInstanceID, CHitObj *pHitObj, CTemplate **ppTemplate, BOOL *pfTemplateInCache)
  175. {
  176. HRESULT hr = S_OK; // return value
  177. HRESULT (CTemplate::*pmAction)(CHitObj *); // do we need to compile a new template or deliver an existing one?
  178. BOOL fNeedsCheck = FALSE;
  179. Assert(IsNormalized(szFile));
  180. BOOL fLocked = FALSE;
  181. // If this is the GLOBAL.ASA we can pick up
  182. // template directly from the application
  183. if (fRunGlobalAsp && pHitObj->PAppln()->PGlobalTemplate())
  184. {
  185. *ppTemplate = pHitObj->PAppln()->PGlobalTemplate();
  186. }
  187. // see if we already have looked up the template on the I/O thread...
  188. else if (!fRunGlobalAsp && pHitObj->GetTemplate())
  189. {
  190. *ppTemplate = pHitObj->GetTemplate();
  191. pHitObj->SetTemplate(NULL);
  192. }
  193. else
  194. // Otherwise we have to look for it in the cache
  195. {
  196. LockTemplateCache();
  197. fLocked = TRUE;
  198. m_pHashTemplates->FindTemplate(CTemplateKey(szFile, dwInstanceID), ppTemplate,&fNeedsCheck);
  199. }
  200. if (*ppTemplate != NULL)
  201. {
  202. // Template found in cache -> use it
  203. (*ppTemplate)->AddRef();
  204. *pfTemplateInCache = TRUE;
  205. (*ppTemplate)->IncrUseCount();
  206. if (fLocked) // Global.Asa from App - no lock
  207. UnLockTemplateCache();
  208. pmAction = CTemplate::Deliver;
  209. }
  210. else
  211. {
  212. *pfTemplateInCache = FALSE;
  213. Assert(fLocked); // only could get here if not found in the hash table
  214. UnLockTemplateCache();
  215. // Create and init new template outside of crirical section
  216. CTemplate *pNewTemplate = new CTemplate;
  217. if (!pNewTemplate)
  218. hr = E_OUTOFMEMORY;
  219. if (SUCCEEDED(hr))
  220. hr = pNewTemplate->Init(pHitObj, !!fRunGlobalAsp, CTemplateKey(szFile, dwInstanceID));
  221. if (SUCCEEDED(hr))
  222. {
  223. LockTemplateCache();
  224. // Try to find if inserted by another thread
  225. m_pHashTemplates->FindTemplate(CTemplateKey(szFile, dwInstanceID), ppTemplate,&fNeedsCheck);
  226. if (*ppTemplate != NULL)
  227. {
  228. // Template found in cache -> use it
  229. (*ppTemplate)->AddRef();
  230. (*ppTemplate)->IncrUseCount();
  231. UnLockTemplateCache();
  232. pmAction = CTemplate::Deliver;
  233. }
  234. else
  235. {
  236. // since we are creating a new template, call FlushCache to make
  237. // sure that no script engines are cached with this name
  238. g_ScriptManager.FlushCache(szFile);
  239. // Insert the newly created template
  240. *ppTemplate = pNewTemplate;
  241. pNewTemplate = NULL; // not to be deleted later
  242. m_pHashTemplates->InsertTemplate(*ppTemplate);
  243. (*ppTemplate)->AddRef();
  244. if (Glob(dwScriptFileCacheSize) == 0) {
  245. // This is special case when a valid template
  246. // does not get added to the cache
  247. // Don't attach such templates to debugger
  248. (*ppTemplate)->m_fDontAttach = TRUE;
  249. }
  250. UnLockTemplateCache();
  251. pmAction = CTemplate::Compile;
  252. }
  253. }
  254. // cleanup new template if created but unused
  255. if (pNewTemplate)
  256. pNewTemplate->Release();
  257. }
  258. if (FAILED(hr))
  259. return hr;
  260. // init succeeded: compile or deliver the template, as required
  261. hr = ((*ppTemplate)->*pmAction)(pHitObj);
  262. if (pmAction == CTemplate::Compile && (*ppTemplate)->m_fDontCache)
  263. {
  264. /* We were compiling and the compiler alerted us not to cache the failed template.
  265. Typically, this occurs when compile failure was caused by something other than
  266. bad template syntax (permissions failure, bad include file reference, etc.).
  267. We need to roll back to where the template did not exist.
  268. */
  269. // de-cache and release the template
  270. // NOTE we don't nullify template ptr, because we want ExecuteRequest to do the final release
  271. LockTemplateCache();
  272. if (m_pHashTemplates->RemoveTemplate(*ppTemplate) == LK_SUCCESS)
  273. ZapTemplate(*ppTemplate);
  274. UnLockTemplateCache();
  275. (*ppTemplate)->Release();
  276. *ppTemplate = NULL;
  277. }
  278. if (SUCCEEDED(hr) && fNeedsCheck && *ppTemplate != NULL)
  279. {
  280. if (!(*ppTemplate)->ValidateSourceFiles(pHitObj->PIReq()))
  281. {
  282. // Template is invalid (out of date)
  283. LockTemplateCache();
  284. if (m_pHashTemplates->RemoveTemplate(*ppTemplate) == LK_SUCCESS)
  285. ZapTemplate (*ppTemplate);
  286. UnLockTemplateCache();
  287. }
  288. }
  289. LockTemplateCache();
  290. BOOL bTemplateRemoved = FALSE;
  291. // Remove old scripts from cache
  292. while (!m_pHashTemplates->FMemoryTemplatesIsEmpty()
  293. && (m_pHashTemplates->InMemoryTemplates() > Glob(dwScriptFileCacheSize))) {
  294. Assert (!m_pHashTemplates->FMemoryTemplatesIsEmpty());
  295. CTemplate *pOldTemplate = static_cast<CTemplate *>(m_pHashTemplates->MemoryTemplatesEnd());
  296. // don't call ScavengePersistCache in this call. We'll call it once at the end
  297. m_pHashTemplates->RemoveTemplate(pOldTemplate, TRUE, FALSE);
  298. bTemplateRemoved = TRUE;
  299. // flush the corresponding script engines. But only if the template
  300. // is valid.
  301. if (pOldTemplate->FIsValid()) {
  302. g_ScriptManager.FlushCache(pOldTemplate->GetSourceFileName());
  303. }
  304. // Only Zap the template if it is not persisted. The result of the above
  305. // call to RemoveTemplate is that the template may have been moved from the
  306. // memory cache to the persist cache. In which case, the template is still
  307. // effectively cached.
  308. if (pOldTemplate->FIsPersisted() == FALSE) {
  309. ZapTemplate(pOldTemplate);
  310. }
  311. }
  312. // call ScavengePersistCache() once here...
  313. if (bTemplateRemoved)
  314. m_pHashTemplates->ScavengePersistCache();
  315. UnLockTemplateCache();
  316. // Store a pointer to the template with the application
  317. // if we haven't already done so
  318. if (SUCCEEDED(hr) && *ppTemplate && fRunGlobalAsp && pHitObj->PAppln()->PGlobalTemplate() == NULL)
  319. pHitObj->PAppln()->SetGlobalTemplate(*ppTemplate);
  320. // If we are shutting down, don't request change notification
  321. if (!IsShutDownInProgress() && *ppTemplate)
  322. {
  323. // If running on NT, and we just compiled the template
  324. // register all the directories used by this template
  325. // for change notification
  326. if (pmAction == CTemplate::Compile && SUCCEEDED(hr)) {
  327. if (!RegisterTemplateForChangeNotification(*ppTemplate, pHitObj->PAppln())) {
  328. LockTemplateCache();
  329. if (m_pHashTemplates->RemoveTemplate(*ppTemplate) == LK_SUCCESS)
  330. ZapTemplate(*ppTemplate);
  331. UnLockTemplateCache();
  332. }
  333. // also create the services config object
  334. hr = (*ppTemplate)->CreateTransServiceConfig(pHitObj->PAppln()->QueryAppConfig()->fTrackerEnabled());
  335. }
  336. // If running on NT, this is a new application, and the template is a global.asa
  337. // register this application for file change notifications
  338. if (SUCCEEDED(hr) && (*ppTemplate)->m_fGlobalAsa && pHitObj->FStartApplication())
  339. {
  340. RegisterApplicationForChangeNotification(*ppTemplate, pHitObj->PAppln());
  341. }
  342. }
  343. return hr;
  344. }
  345. /*===================================================================
  346. CTemplateCacheManager::Flush
  347. Parameters:
  348. szFile - the file to remove from cache
  349. Returns:
  350. None
  351. ===================================================================*/
  352. void CTemplateCacheManager::Flush(const TCHAR *szFile, DWORD dwInstanceID)
  353. {
  354. LockTemplateAndIncFileCaches();
  355. Assert (IsNormalized(szFile));
  356. CTemplate *pTemplate;
  357. m_pHashTemplates->FindTemplate(CTemplateKey(szFile, dwInstanceID), &pTemplate);
  358. while (pTemplate != NULL)
  359. {
  360. #ifndef PERF_DISABLE
  361. g_PerfData.Incr_TEMPLFLUSHES();
  362. #endif
  363. m_pHashTemplates->RemoveTemplate(pTemplate);
  364. // Make sure anyone using this template can tell it is obsolete
  365. pTemplate->Zombify();
  366. // Don't flush engines if this is a global.asa file
  367. // We'll need the engines to run Application_OnEnd
  368. // The application will flush the engine from the cache
  369. // when it unints
  370. if (!FIsGlobalAsa(szFile))
  371. {
  372. g_ScriptManager.FlushCache(szFile);
  373. }
  374. ZapTemplate(pTemplate);
  375. // If wildcard was specified in Flush for Instance ID, there may be
  376. // more templates to remove.
  377. m_pHashTemplates->FindTemplate(CTemplateKey(szFile, dwInstanceID), &pTemplate);
  378. }
  379. UnLockTemplateAndIncFileCaches();
  380. }
  381. /*===================================================================
  382. CTemplateCacheManager::FlushAll
  383. Completely empties the template cache
  384. Parameters:
  385. None
  386. Returns:
  387. None
  388. ===================================================================*/
  389. void CTemplateCacheManager::FlushAll(BOOL fDoLazyFlush)
  390. {
  391. if (fDoLazyFlush)
  392. {
  393. m_dwTemplateCacheTag = GetTickCount();
  394. DBGPRINTF ((DBG_CONTEXT,"Using new Cache Tag to Invalidate Template\n"));
  395. return;
  396. }
  397. LockTemplateAndIncFileCaches();
  398. CTemplateHashTable *pNewTable = NULL;
  399. HANDLE hnd;
  400. // note that all of the following logic works on the premise that any
  401. // error causes the code to fall into the old mechanism of flushing
  402. // the hash table in place...
  403. // allocate a new table
  404. if (pNewTable = new CTemplateHashTable)
  405. {
  406. //
  407. // Create a thread to clean up the old table
  408. //
  409. DWORD nThreadIndex;
  410. //
  411. // if no threads started yet, use the first slot
  412. //
  413. if (m_cCleanupThreads) {
  414. nThreadIndex = 0;
  415. goto create_new_thread;
  416. }
  417. //
  418. // see if there is a thread that terminated
  419. //
  420. nThreadIndex = WaitForMultipleObjects(
  421. m_cCleanupThreads,
  422. m_hCleanupThreads,
  423. FALSE, // wait for any event
  424. 0); // return immediately
  425. Assert(nThreadIndex == WAIT_TIMEOUT);
  426. if (m_cCleanupThreads < MAX_CLEANUP_THREADS)
  427. {
  428. //
  429. // just get the next index
  430. //
  431. nThreadIndex = m_cCleanupThreads;
  432. goto create_new_thread;
  433. }
  434. goto Cleanup;
  435. create_new_thread:
  436. hnd = CreateThread(NULL, 0, CTemplateCacheManager::FlushHashTableThread, m_pHashTemplates, 0, NULL);
  437. if (hnd)
  438. {
  439. //
  440. // close the previous handle if we are reusing an entry
  441. //
  442. if (nThreadIndex < m_cCleanupThreads)
  443. {
  444. //
  445. // we are reusing a slot from a terminated thread
  446. //
  447. CloseHandle(m_hCleanupThreads[ nThreadIndex ]);
  448. } else {
  449. //
  450. // we are using a new slot
  451. //
  452. Assert(nThreadIndex == m_cCleanupThreads);
  453. m_cCleanupThreads++;
  454. }
  455. m_hCleanupThreads[ nThreadIndex ] = hnd;
  456. // all the above was successful, so note that the new table is the
  457. // current table in the cache, cleanup and exit.
  458. DBGPRINTF((DBG_CONTEXT, "[CTemplateCacheManager] Flushing entire cache on another thread.\n"));
  459. m_pHashTemplates = pNewTable;
  460. UnLockTemplateAndIncFileCaches();
  461. return;
  462. }
  463. }
  464. Cleanup:
  465. // delete the new table if something above failed.
  466. if (pNewTable)
  467. delete pNewTable;
  468. DBGPRINTF((DBG_CONTEXT, "[CTemplateCacheManager] Flushing entire cache in place\n"));
  469. FlushHashTable(m_pHashTemplates);
  470. UnLockTemplateAndIncFileCaches();
  471. return;
  472. }
  473. /*===================================================================
  474. CTemplateCacheManager::FlushHashTableThread
  475. Thread spun up by CTemplateCacheMgr::FlushAll() to flush all
  476. templates in the cache but not while under the critical section
  477. on the notification thread. Prevents unwanted contention on the
  478. cache.
  479. Parameters:
  480. None
  481. Returns:
  482. None
  483. ===================================================================*/
  484. DWORD CTemplateCacheManager::FlushHashTableThread(VOID *pArg)
  485. {
  486. CTemplateHashTable *pTable = (CTemplateHashTable *)pArg;
  487. Assert(pTable);
  488. FlushHashTable(pTable);
  489. delete pTable;
  490. return S_OK;
  491. }
  492. /*===================================================================
  493. CTemplateCacheManager::FlushHashTable
  494. Does the actual work of flushing the templates.
  495. This routine may or may not be under the global cache manager
  496. crit sec. It will if the flush is happening on the notification
  497. thread. It won't be if it's happening on the FlushHashTableThread.
  498. Parameters:
  499. None
  500. Returns:
  501. None
  502. ===================================================================*/
  503. void CTemplateCacheManager::FlushHashTable(CTemplateHashTable *pTable)
  504. {
  505. // Delete templates from the cache until there are no more
  506. while (!pTable->FMemoryTemplatesIsEmpty()) {
  507. CTemplate *pTemplate = static_cast<CTemplate *>(pTable->MemoryTemplatesEnd());
  508. // Remove the template from its various data structures
  509. pTable->RemoveTemplate(pTemplate);
  510. // Make sure anyone using this template can tell it is obsolete
  511. pTemplate->Zombify();
  512. // Flush the engine for this template from the script engine cache
  513. // (use hash key, in case template was previously a zombie.)
  514. g_ScriptManager.FlushCache(pTemplate->ExtractHashKey()->szPathTranslated);
  515. ZapTemplate(pTemplate);
  516. }
  517. // Delete templates from the cache until there are no more
  518. while (!pTable->FPersistTemplatesIsEmpty()) {
  519. CTemplate *pTemplate = static_cast<CTemplate *>(pTable->PersistTemplatesEnd());
  520. // Remove the template from its various data structures
  521. pTable->RemoveTemplate(pTemplate);
  522. // Make sure anyone using this template can tell it is obsolete
  523. pTemplate->Zombify();
  524. // Flush the engine for this template from the script engine cache
  525. // (use hash key, in case template was previously a zombie.)
  526. g_ScriptManager.FlushCache(pTemplate->ExtractHashKey()->szPathTranslated);
  527. ZapTemplate(pTemplate);
  528. }
  529. }
  530. /*===================================================================
  531. CTemplateCacheManager::FlushFiles
  532. Empties template cache of files that match a prefix
  533. Parameters:
  534. None
  535. Returns:
  536. None
  537. ===================================================================*/
  538. void CTemplateCacheManager::FlushFiles(const TCHAR *szFilePrefix)
  539. {
  540. LockTemplateAndIncFileCaches();
  541. BOOL fDoingMemoryTemplates = TRUE;
  542. // Delete templates from the cache until there are no more
  543. CDblLink *pLink = m_pHashTemplates->MemoryTemplatesBegin();
  544. while (! (fDoingMemoryTemplates
  545. ? m_pHashTemplates->FMemoryTemplatesDblLinkAtEnd(pLink)
  546. : m_pHashTemplates->FPersistTemplatesDblLinkAtEnd(pLink))) {
  547. CDblLink *pNextLink = pLink->PNext();
  548. CTemplate *pTemplate = static_cast<CTemplate *>(pLink);
  549. if (_tcsncmp(pTemplate->ExtractHashKey()->szPathTranslated, szFilePrefix, _tcslen(szFilePrefix)) == 0) {
  550. #if UNICODE
  551. DBGPRINTF((DBG_CONTEXT, "FlushFiles: flushing %S\n", pTemplate->ExtractHashKey()->szPathTranslated));
  552. #else
  553. DBGPRINTF((DBG_CONTEXT, "FlushFiles: flushing %s\n", pTemplate->ExtractHashKey()->szPathTranslated));
  554. #endif
  555. // Remove the template from its various data structures
  556. m_pHashTemplates->RemoveTemplate(pTemplate);
  557. // Make sure anyone using this template can tell it is obsolete
  558. pTemplate->Zombify();
  559. // Flush the engine for this template from the script engine cache
  560. // (use hash key, in case template was previously a zombie.)
  561. g_ScriptManager.FlushCache(pTemplate->ExtractHashKey()->szPathTranslated);
  562. ZapTemplate(pTemplate);
  563. #ifndef PERF_DISABLE
  564. g_PerfData.Incr_TEMPLFLUSHES();
  565. #endif
  566. }
  567. pLink = pNextLink;
  568. if (fDoingMemoryTemplates && m_pHashTemplates->FMemoryTemplatesDblLinkAtEnd(pLink)) {
  569. fDoingMemoryTemplates = FALSE;
  570. pLink = m_pHashTemplates->PersistTemplatesBegin();
  571. }
  572. }
  573. UnLockTemplateAndIncFileCaches();
  574. }
  575. /*===================================================================
  576. CTemplateCacheManager::AddApplicationToDebuggerUI
  577. Loop through the template cache, and create doc nodes for
  578. all templates that belong to the application
  579. Parameters:
  580. pAppln - pointer to application to attach to.
  581. Returns: N/A
  582. ===================================================================*/
  583. void CTemplateCacheManager::AddApplicationToDebuggerUI(CAppln *pAppln)
  584. {
  585. CDblLink *pLink;
  586. for (pLink = m_pHashTemplates->MemoryTemplatesBegin(); !m_pHashTemplates->FMemoryTemplatesDblLinkAtEnd(pLink); pLink = pLink->PNext())
  587. {
  588. // Bug 92070:
  589. // Determine if the template is a member of pAppln by comparing
  590. // the virtual path of the template to the application's virtual
  591. // path (previously compared physical paths) Since a template
  592. // can have multiple virtual paths, only the first instance wins.
  593. // Thus the template will only appear in the application that first
  594. // loaded it.
  595. CTemplate *pTemplate = static_cast<CTemplate *>(pLink);
  596. if (_tcscmp(pAppln->GetApplnPath(SOURCEPATHTYPE_VIRTUAL), pTemplate->GetApplnPath(SOURCEPATHTYPE_VIRTUAL)) == 0)
  597. pTemplate->AttachTo(pAppln);
  598. }
  599. }
  600. /*===================================================================
  601. CTemplateCacheManager::RemoveApplicationFromDebuggerUI
  602. Loop through the template cache, and remove doc nodes for
  603. all templates that belong to the application
  604. Parameters:
  605. pAppln - pointer to application to detach from
  606. if pAppln is NULL, detach from ALL applications
  607. Returns: N/A
  608. ===================================================================*/
  609. void CTemplateCacheManager::RemoveApplicationFromDebuggerUI(CAppln *pAppln)
  610. {
  611. CDblLink *pLink;
  612. for (pLink = m_pHashTemplates->MemoryTemplatesBegin();
  613. !m_pHashTemplates->FMemoryTemplatesDblLinkAtEnd(pLink);
  614. pLink = pLink->PNext())
  615. {
  616. CTemplate *pTemplate = static_cast<CTemplate *>(pLink);
  617. if (pAppln != NULL)
  618. pTemplate->DetachFrom(pAppln);
  619. else
  620. pTemplate->Detach();
  621. }
  622. }
  623. /*===================================================================
  624. void CTemplateCacheManager::RegisterTemplateForChangeNotification
  625. Request to watch template directories for file changes
  626. Parameters:
  627. A pointer to the template
  628. Returns:
  629. BOOL True if successfully registered for change notification
  630. ===================================================================*/
  631. BOOL CTemplateCacheManager::RegisterTemplateForChangeNotification(CTemplate *pTemplate, CAppln *pApplication)
  632. {
  633. DWORD dwValue = 0;
  634. // If the template has a UNC include or main file & we the registry key is absent (default) or the key is present and disabled.
  635. // Then we will use the flag this file one that requires monitoring to be performed by ASP.
  636. if (pTemplate->FIsUNC() && !g_fUNCChangeNotificationEnabled)
  637. {
  638. pTemplate->m_fNeedsMonitoring = TRUE;
  639. pTemplate->m_dwLastMonitored = GetTickCount ();
  640. return TRUE;
  641. }
  642. //else (Local File or UNC file with ChangeNotification Enabled)
  643. STACK_BUFFER( tempPath, MAX_PATH );
  644. for (DWORD i = 0; i < pTemplate->m_cFilemaps; i++) {
  645. // Check if this directory is already registered for change notification
  646. // Pick out the directory portion of the path
  647. TCHAR *szEndOfPath = _tcsrchr(pTemplate->m_rgpFilemaps[i]->m_szPathTranslated, _T('\\'));
  648. size_t cch = DIFF(szEndOfPath - pTemplate->m_rgpFilemaps[i]->m_szPathTranslated)+1;
  649. if (tempPath.Resize((cch * sizeof(TCHAR)) + sizeof(TCHAR)) == FALSE) {
  650. // if failure to resize, unless disabled through the registry, flag the file for manual monitoring after releasing previously registered DME's
  651. if (SUCCEEDED(g_AspRegistryParams.GetFileMonitoringEnabled(&dwValue)) && dwValue == 0)
  652. continue;
  653. if (i > 0) {
  654. while (--i) {
  655. pTemplate->m_rgpFilemaps[i]->m_pDME->Release();
  656. pTemplate->m_rgpFilemaps[i]->m_pDME = NULL;
  657. }
  658. }
  659. pTemplate->m_fNeedsMonitoring = TRUE;
  660. pTemplate->m_dwLastMonitored = GetTickCount ();
  661. return TRUE;
  662. }
  663. TCHAR *szPath = (TCHAR *) tempPath.QueryPtr();
  664. _tcsncpy(szPath, pTemplate->m_rgpFilemaps[i]->m_szPathTranslated, cch);
  665. szPath[cch] = 0;
  666. // if the template is within the application's physical path, then it is
  667. // already being monitored.
  668. CASPDirMonitorEntry *pDME = NULL;
  669. if (pDME = pApplication->FPathMonitored(szPath)) {
  670. pDME->AddRef();
  671. pTemplate->m_rgpFilemaps[i]->m_pDME= pDME;
  672. continue;
  673. }
  674. if (RegisterASPDirMonitorEntry(szPath, &pDME)) {
  675. Assert(pDME);
  676. pTemplate->m_rgpFilemaps[i]->m_pDME= pDME;
  677. }
  678. else {
  679. // the current file failed to register. Release all previous DMEs
  680. // and return FALSE...
  681. if (i > 0) {
  682. while (--i) {
  683. pTemplate->m_rgpFilemaps[i]->m_pDME->Release();
  684. pTemplate->m_rgpFilemaps[i]->m_pDME = NULL;
  685. }
  686. }
  687. // Without the monitoring magic we would return FALSE here but we give
  688. // it one more chance to live, except if the registry parameter is set to disable it.
  689. // if the registry setting asks for this to be disabled disable it.
  690. if (SUCCEEDED(g_AspRegistryParams.GetFileMonitoringEnabled(&dwValue)) && dwValue == 0)
  691. return FALSE;
  692. pTemplate->m_fNeedsMonitoring = TRUE;
  693. pTemplate->m_dwLastMonitored = GetTickCount ();
  694. return TRUE;
  695. }
  696. }
  697. return TRUE;
  698. }
  699. /*===================================================================
  700. void CTemplateCacheManager::RegisterApplicationForChangeNotification
  701. Request to watch template directories for file changes
  702. Parameters:
  703. A pointer to the template
  704. Returns:
  705. BOOL True if successfully registered for change notification
  706. ===================================================================*/
  707. BOOL CTemplateCacheManager::RegisterApplicationForChangeNotification(CTemplate *pTemplate, CAppln *pApplication)
  708. {
  709. STACK_BUFFER( tempPath, MAX_PATH );
  710. // Start with 1 to skip GLOBAL.ASA that is always added
  711. // in hitobj.cpp when new application gets created
  712. for (DWORD i = 1; i < pTemplate->m_cFilemaps; i++)
  713. {
  714. // Add to list of file-application mappings
  715. g_FileAppMap.AddFileApplication(pTemplate->m_rgpFilemaps[i]->m_szPathTranslated, pApplication);
  716. // Check if this directory is already registered for change notification
  717. // Pick out the directory portion of the path
  718. TCHAR *szEndOfPath = _tcsrchr(pTemplate->m_rgpFilemaps[i]->m_szPathTranslated, _T('\\'));
  719. size_t cch = DIFF(szEndOfPath - pTemplate->m_rgpFilemaps[i]->m_szPathTranslated) + 1;
  720. if (tempPath.Resize((cch*sizeof(TCHAR)) + sizeof(TCHAR)) == FALSE) {
  721. // if failure, continue registering anyway...
  722. continue;
  723. }
  724. TCHAR *szPath = (TCHAR *) tempPath.QueryPtr();
  725. _tcsncpy(szPath, pTemplate->m_rgpFilemaps[i]->m_szPathTranslated, cch);
  726. szPath[cch] = 0;
  727. // if the template is within the application's physical path, then it is
  728. // already being monitored.
  729. if (pApplication->FPathMonitored(szPath)) {
  730. continue;
  731. }
  732. // Register directory for monitoring
  733. CASPDirMonitorEntry *pDME = NULL;
  734. if (RegisterASPDirMonitorEntry(szPath, &pDME))
  735. {
  736. Assert(pDME);
  737. pApplication->AddDirMonitorEntry(pDME);
  738. }
  739. }
  740. return TRUE;
  741. }
  742. /*===================================================================
  743. BOOL CTemplateCacheManager::ShutdownCacheChangeNotification
  744. Turn off change notification for changes to files in the cache
  745. Parameters:
  746. None
  747. Returns:
  748. Nothing
  749. ===================================================================*/
  750. BOOL CTemplateCacheManager::ShutdownCacheChangeNotification()
  751. {
  752. BOOL fDoingMemoryTemplates = TRUE;
  753. LockTemplateCache();
  754. CTemplate *pTemplate = static_cast<CTemplate *>(m_pHashTemplates->MemoryTemplatesBegin());
  755. while (fDoingMemoryTemplates
  756. ? !m_pHashTemplates->FMemoryTemplatesDblLinkAtEnd(pTemplate)
  757. : !m_pHashTemplates->FPersistTemplatesDblLinkAtEnd(pTemplate)) {
  758. if(pTemplate->m_rgpFilemaps)
  759. {
  760. for(UINT i = 0; i < pTemplate->m_cFilemaps; i++)
  761. {
  762. // Give up our ref count on the directory monitor entry
  763. if (pTemplate->m_rgpFilemaps[i]->m_pDME)
  764. {
  765. pTemplate->m_rgpFilemaps[i]->m_pDME->Release();
  766. pTemplate->m_rgpFilemaps[i]->m_pDME = NULL;
  767. }
  768. }
  769. }
  770. pTemplate = static_cast<CTemplate *>(pTemplate->PNext());
  771. if (fDoingMemoryTemplates && m_pHashTemplates->FMemoryTemplatesDblLinkAtEnd(pTemplate)) {
  772. fDoingMemoryTemplates = FALSE;
  773. pTemplate = static_cast<CTemplate *>(m_pHashTemplates->PersistTemplatesBegin());
  774. }
  775. }
  776. UnLockTemplateCache();
  777. return TRUE;
  778. }
  779. /* ****************************************************************************
  780. CIncFileMap member functions
  781. */
  782. /*===================================================================
  783. CIncFileMap::CIncFileMap
  784. Parameters: N/A
  785. Returns: N/A
  786. ===================================================================*/
  787. CIncFileMap::CIncFileMap()
  788. {
  789. }
  790. /*===================================================================
  791. CIncFileMap::~CIncFileMap
  792. Parameters: N/A
  793. Returns: N/A
  794. ===================================================================*/
  795. CIncFileMap::~CIncFileMap()
  796. {
  797. }
  798. /*===================================================================
  799. CIncFileMap::Init
  800. Parameters: None
  801. Returns: Completion Status
  802. ===================================================================*/
  803. HRESULT CIncFileMap::Init()
  804. {
  805. HRESULT hr;
  806. ErrInitCriticalSection(&m_csUpdate, hr);
  807. if (FAILED(hr))
  808. return(hr);
  809. return m_mpszIncFile.Init(CINCFILEBUCKETS);
  810. }
  811. /*===================================================================
  812. CIncFileMap::GetIncFile
  813. Get an inc-file from the cache, first storing it into cache if it is not yet there.
  814. Parameters:
  815. szIncFile - file name
  816. ppIncFile - ptr-to-ptr to inc-file (out-parameter)
  817. Returns: HRESULT
  818. ===================================================================*/
  819. HRESULT CIncFileMap::GetIncFile(const TCHAR *szFile, CIncFile **ppIncFile)
  820. {
  821. HRESULT hrInit = S_OK; // return value
  822. LockIncFileCache();
  823. Assert(IsNormalized(szFile));
  824. *ppIncFile = static_cast<CIncFile *>(m_mpszIncFile.FindElem(szFile, _tcslen(szFile)*sizeof(TCHAR)));
  825. // if we have a cached inc-file at this stage, it must be "reliable," so we use it.
  826. // else, if we have no cached inc-file, create a new one.
  827. if (*ppIncFile == NULL)
  828. {
  829. if ((*ppIncFile = new CIncFile) == NULL)
  830. {
  831. UnLockIncFileCache();
  832. return E_OUTOFMEMORY;
  833. }
  834. if (SUCCEEDED(hrInit = (*ppIncFile)->Init(szFile)))
  835. {
  836. // The hash table will hold a reference to the inc file
  837. (*ppIncFile)->AddRef();
  838. m_mpszIncFile.AddElem(*ppIncFile);
  839. }
  840. else
  841. {
  842. //
  843. // Init can fail with E_OUTOFMEMORY on call to SmallAlloc or due to a failure to init critical section.
  844. // Cleanup allocated memory for ppIncFile if that is the case.
  845. //
  846. delete *ppIncFile;
  847. *ppIncFile = NULL;
  848. }
  849. }
  850. if (SUCCEEDED(hrInit))
  851. {
  852. // The caller will hold a reference to the inc file
  853. (*ppIncFile)->AddRef();
  854. }
  855. UnLockIncFileCache();
  856. return hrInit;
  857. }
  858. /*===================================================================
  859. CIncFileMap::UnInit
  860. Parameters: N/A
  861. Returns: Completion status
  862. ===================================================================*/
  863. HRESULT CIncFileMap::UnInit()
  864. {
  865. CIncFile *pNukeIncFile = static_cast<CIncFile *>(m_mpszIncFile.Head());
  866. while (pNukeIncFile != NULL)
  867. {
  868. CIncFile *pNext = static_cast<CIncFile *>(pNukeIncFile->m_pNext);
  869. pNukeIncFile->OnIncFileDecache();
  870. pNukeIncFile->Release();
  871. pNukeIncFile = pNext;
  872. }
  873. DeleteCriticalSection(&m_csUpdate);
  874. return m_mpszIncFile.UnInit();
  875. }
  876. /*===================================================================
  877. CIncFileMap::Flush
  878. Parameters:
  879. szFile - the file to remove from cache
  880. Returns:
  881. None
  882. ===================================================================*/
  883. void CIncFileMap::Flush(const TCHAR *szFile)
  884. {
  885. LockTemplateAndIncFileCaches();
  886. Assert(IsNormalized(szFile));
  887. CIncFile *pIncFile = static_cast<CIncFile *>(m_mpszIncFile.FindElem(szFile, _tcslen(szFile)*sizeof(TCHAR)));
  888. if (pIncFile != NULL)
  889. {
  890. if (pIncFile->FlushTemplates())
  891. {
  892. // Remove from hash table
  893. m_mpszIncFile.DeleteElem(szFile, _tcslen(szFile)*sizeof(TCHAR));
  894. // The hash table gave up its reference
  895. // to the incfile
  896. pIncFile->OnIncFileDecache();
  897. pIncFile->Release();
  898. }
  899. }
  900. UnLockTemplateAndIncFileCaches();
  901. }
  902. /*===================================================================
  903. CIncFileMap::FlushFiles
  904. Parameters:
  905. szFile - the file prefix to search for in cache
  906. Returns:
  907. None
  908. ===================================================================*/
  909. void CIncFileMap::FlushFiles(const TCHAR *szFilePrefix)
  910. {
  911. LockTemplateAndIncFileCaches();
  912. Assert(IsNormalized(szFilePrefix));
  913. CIncFile *pIncFile = static_cast<CIncFile *>(m_mpszIncFile.Head());
  914. while (pIncFile != NULL)
  915. {
  916. CIncFile *pNextFile = static_cast<CIncFile *>(pIncFile->m_pNext);
  917. int cchFilePrefix = _tcslen(szFilePrefix);
  918. if (pIncFile->m_cbKey >= (cchFilePrefix*(int)sizeof(TCHAR)) &&
  919. _tcsncmp(reinterpret_cast<TCHAR *>(pIncFile->m_pKey), szFilePrefix, cchFilePrefix) == 0)
  920. {
  921. #if UNICODE
  922. DBGPRINTF((DBG_CONTEXT, "FlushFiles: flushing %S\n", pIncFile->m_pKey));
  923. #else
  924. DBGPRINTF((DBG_CONTEXT, "FlushFiles: flushing %s\n", pIncFile->m_pKey));
  925. #endif
  926. if (pIncFile->FlushTemplates())
  927. {
  928. // Remove from hash table
  929. m_mpszIncFile.DeleteElem(pIncFile->m_pKey, pIncFile->m_cbKey);
  930. // The hash table gave up its reference
  931. // to the incfile
  932. pIncFile->OnIncFileDecache();
  933. pIncFile->Release();
  934. }
  935. }
  936. pIncFile = pNextFile;
  937. }
  938. UnLockTemplateAndIncFileCaches();
  939. }
  940. /* ****************************************************************************
  941. Non-class support functions
  942. */
  943. /*===================================================================
  944. FFileChangedSinceCached
  945. Has the file changed since it was cached?
  946. Parameters:
  947. szFile - file name
  948. ftPrevWriteTime - the file's "previous write time"
  949. (its last-write-time value when the file was cached)
  950. Returns:
  951. TRUE or FALSE
  952. ===================================================================*/
  953. BOOL FFileChangedSinceCached(const TCHAR *szFile, HANDLE hFile, FILETIME& ftPrevWriteTime)
  954. {
  955. BOOL fRet = FALSE; // return value
  956. FILETIME ftLastWriteTime;
  957. if (FAILED(AspGetFileAttributes(szFile, hFile, &ftLastWriteTime)))
  958. {
  959. // assume file was changed if get file attributes failed
  960. fRet = TRUE;
  961. }
  962. if( 0 != CompareFileTime( &ftPrevWriteTime, &ftLastWriteTime) )
  963. {
  964. // file was changed if file times differ
  965. fRet = TRUE;
  966. }
  967. return fRet;
  968. }
  969. /*===================================================================
  970. CTemplateCacheManager::CTemplateHashTable::TrimPersistCache
  971. Parameters:
  972. dwTrimCount - the number of templates to trim from the cache
  973. Returns:
  974. TRUE - if dwTrimCount was actually trimmed
  975. FALSE - if exited before dwTrimCount was met
  976. ===================================================================*/
  977. BOOL CTemplateCacheManager::CTemplateHashTable::TrimPersistCache(DWORD dwTrimCount)
  978. {
  979. // enter a while loop to trim until the count is reached
  980. while(dwTrimCount--) {
  981. // if there isn't anything else to trim, we're done. Return FALSE
  982. // to indicate that dwTrimCount was not met.
  983. if (m_dwPersistedTemplates == 0) {
  984. return(FALSE);
  985. }
  986. else {
  987. CTemplate *pTemplate;
  988. // get the oldest template from the list
  989. pTemplate = static_cast<CTemplate *>(PersistTemplatesEnd());
  990. // remove the template.
  991. RemoveTemplate(pTemplate);
  992. ZapTemplate(pTemplate);
  993. }
  994. }
  995. // return TRUE to indicate that the TrimCount was met.
  996. return(TRUE);
  997. }
  998. /*===================================================================
  999. CTemplateCacheManager::CTemplateHashTable::ScavengePersistCache
  1000. Parameters:
  1001. <NONE>
  1002. Returns:
  1003. VOID
  1004. ===================================================================*/
  1005. VOID CTemplateCacheManager::CTemplateHashTable::ScavengePersistCache()
  1006. {
  1007. CTemplate *pTemplate;
  1008. CTemplate *pTemplateNext;
  1009. // enter a for loop to look at all persisted templates to see if
  1010. // any memory can be freed. It's memory can be freed only if the
  1011. // ref count is 1 (the sole ref count is for the cache). Also note
  1012. // that the list is re-ordered to move templates to the head of the
  1013. // list that can't have their memory freed at this time because of
  1014. // the ref count.
  1015. for (pTemplate = static_cast<CTemplate *>(PersistTemplatesBegin());
  1016. (pTemplate != static_cast<CTemplate *>(&m_listPersistTemplates)) && (pTemplate->m_pbStart != NULL);
  1017. pTemplate = pTemplateNext) {
  1018. pTemplateNext = static_cast<CTemplate *>(pTemplate->PNext());
  1019. // this check should be safe. The only risk is that we miss a release
  1020. // of the template from 2 to 1, in which case will miss it this time
  1021. // but get it the next time through. AddRef from 1 to 2 is impossible
  1022. // to interrupt because it couldn't be on this list when it gets AddRef'd
  1023. // from 1 to 2 and moving it from this list is protected by the template
  1024. // cache lock which we should be under.
  1025. if (pTemplate->m_cRefs == 1) {
  1026. BOOL fDeleteRecord = FALSE;
  1027. if (pTemplate->m_cUseCount == 1) {
  1028. // not going to continue to cache or persist a template that
  1029. // was used just once.
  1030. fDeleteRecord = TRUE;
  1031. g_nScavengedPurged++;
  1032. }
  1033. else if (pTemplate->PersistData(m_szPersistCacheDir) != S_OK) {
  1034. // a failure will result in the record being deleted.
  1035. fDeleteRecord = TRUE;
  1036. g_nScavengedPersistFailed++;
  1037. }
  1038. else {
  1039. g_nScavengedPersisted++;
  1040. // remove the memory
  1041. CTemplate::LargeFree(pTemplate->m_pbStart);
  1042. pTemplate->m_pbStart = NULL;
  1043. }
  1044. if (fDeleteRecord) {
  1045. if (RemoveTemplate(pTemplate, FALSE, FALSE) == LK_SUCCESS)
  1046. ZapTemplate(pTemplate);
  1047. }
  1048. }
  1049. else {
  1050. // if some is still using it, move the template to the head of the
  1051. // list so that we'll check again later.
  1052. pTemplate->PrependTo(m_listPersistTemplates);
  1053. }
  1054. }
  1055. }
  1056. /*===================================================================
  1057. GetAggregatedTemplCounter()
  1058. Returns the Template Perf Counter. To do this, initializes a private
  1059. copy of the perfmainblock and aggregates the stats into it.
  1060. ===================================================================*/
  1061. static DWORD GetAggregatedTemplCounter()
  1062. {
  1063. CPerfMainBlock perfSharedBlk;
  1064. DWORD pdwCounters[C_PERF_PROC_COUNTERS];
  1065. BOOL bInited = FALSE;
  1066. memset(pdwCounters, 0, sizeof(pdwCounters));
  1067. if (!(bInited = (perfSharedBlk.Init() == S_OK)));
  1068. else {
  1069. perfSharedBlk.GetStats(pdwCounters);
  1070. }
  1071. if (bInited)
  1072. perfSharedBlk.UnInit();
  1073. return(pdwCounters[ID_TEMPLCACHE]);
  1074. }
  1075. /*===================================================================
  1076. CTemplateCacheManager::OnInitCleanup
  1077. Parameters:
  1078. [none]
  1079. Returns:
  1080. BOOL to indicate if the init was successful
  1081. ===================================================================*/
  1082. DWORD CTemplateCacheManager::OnInitCleanup(VOID *p)
  1083. {
  1084. // we are using a single character buffer to construct all directory and file names we use.
  1085. CHAR szDirBuffer[ MAX_PATH + 32];
  1086. // first build the template for the cache directories
  1087. INT iLen = _snprintf(szDirBuffer,
  1088. MAX_PATH,
  1089. "%s\\PID*.TMP",
  1090. Glob(pszPersistTemplateDir),
  1091. GetCurrentProcessId());
  1092. if ((iLen <= 0) || (iLen >= MAX_PATH)) {
  1093. return ERROR_INSUFFICIENT_BUFFER;
  1094. }
  1095. // outer loop: find all directories, retrieve PID, if process not exist, go to inner
  1096. // inner loop: delete all ASP cache files
  1097. WIN32_FIND_DATAA RootDir_FindData;
  1098. HANDLE hRootDir = FindFirstFileA( szDirBuffer, &RootDir_FindData );
  1099. WIN32_FIND_DATAA FileDir_FindData;
  1100. HANDLE hFileDir;
  1101. if ( hRootDir != INVALID_HANDLE_VALUE ) {
  1102. // point to where the cache subdirectory begins
  1103. CHAR *pDir = szDirBuffer + strlen(Glob(pszPersistTemplateDir)) + 1;
  1104. CHAR *pFile;
  1105. CHAR *pc;
  1106. DWORD pid;
  1107. HANDLE hProcess;
  1108. do {
  1109. // validate file name is what we expect and extract the PID
  1110. // we know the first letters are 'PID' as that was the search criteria
  1111. pc = RootDir_FindData.cFileName + 3;
  1112. for(pid = 0; isdigit(*pc); pid = pid*10 + *pc++ - '0');
  1113. if (pid == 0) {
  1114. continue;
  1115. }
  1116. hProcess = OpenProcess(SYNCHRONIZE, FALSE, pid);
  1117. if (hProcess) {
  1118. // the process is alive, so leave the directory alone.
  1119. CloseHandle( hProcess );
  1120. continue;
  1121. }
  1122. if (GetLastError() != ERROR_INVALID_PARAMETER) {
  1123. // we got an error other than "no such process"
  1124. Assert( GetLastError() == ERROR_ACCESS_DENIED);
  1125. continue;
  1126. }
  1127. // The process does not exist, so clean up the directory
  1128. // Add the directory name, append a '\', append the file template, and point pFile
  1129. // to the position where the file name starts
  1130. strcpy( pDir, RootDir_FindData.cFileName );
  1131. pFile = pDir + strlen(pDir);
  1132. *pFile++ = '\\';
  1133. strcpy( pFile, "ASP*.TMP" );
  1134. // search the subdirectory for asp template cache files
  1135. hFileDir = FindFirstFileA( szDirBuffer, &FileDir_FindData );
  1136. if ( hFileDir != INVALID_HANDLE_VALUE ) {
  1137. do {
  1138. // append the file name and delete the file
  1139. strcpy( pFile, FileDir_FindData.cFileName);
  1140. DeleteFileA( szDirBuffer );
  1141. } while (FindNextFileA( hFileDir, &FileDir_FindData ));
  1142. FindClose( hFileDir );
  1143. }
  1144. // Now zap the last part of the path and delete the directory
  1145. *--pFile = '\0';
  1146. RemoveDirectoryA( szDirBuffer );
  1147. } while (FindNextFileA( hRootDir, &RootDir_FindData ));
  1148. FindClose( hRootDir );
  1149. }
  1150. return S_OK;
  1151. }
  1152. /*===================================================================
  1153. CTemplateCacheManager::InitPersistCache
  1154. Parameters:
  1155. [none]
  1156. Returns:
  1157. BOOL to indicate if the init was successful
  1158. ===================================================================*/
  1159. BOOL CTemplateCacheManager::InitPersistCache(CIsapiReqInfo *pIReq)
  1160. {
  1161. HANDLE hImpersonationToken = NULL;
  1162. BOOL fRevertedToSelf = FALSE;
  1163. DWORD dirAttribs;
  1164. INT iLen;
  1165. UINT uiEventSubId = 0;
  1166. DWORD dwError = ERROR_SUCCESS;
  1167. if (OpenThreadToken( GetCurrentThread(),
  1168. TOKEN_READ | TOKEN_IMPERSONATE,
  1169. TRUE,
  1170. &hImpersonationToken )) {
  1171. RevertToSelf();
  1172. fRevertedToSelf = TRUE;
  1173. }
  1174. // build the cache directory name
  1175. iLen = _snprintf(m_szPersistCacheDir,
  1176. sizeof(m_szPersistCacheDir),
  1177. "%s\\PID%d.TMP",
  1178. Glob(pszPersistTemplateDir),
  1179. GetCurrentProcessId());
  1180. if ((iLen <= 0) || (iLen >= sizeof(m_szPersistCacheDir))) {
  1181. uiEventSubId = IDS_CACHE_DIR_NAME_TOO_LONG;
  1182. goto LExit;
  1183. }
  1184. // If directory exists, rename it to something else (must be an old leftover
  1185. dirAttribs = GetFileAttributesA(m_szPersistCacheDir);
  1186. if (dirAttribs != INVALID_FILE_ATTRIBUTES) {
  1187. // recreate the same dir name with a leading 0 for the pid...
  1188. CHAR szNewDirName[ sizeof(m_szPersistCacheDir) ];
  1189. iLen = _snprintf(szNewDirName,
  1190. sizeof(szNewDirName),
  1191. "%s\\PID0%d.TMP",
  1192. Glob(pszPersistTemplateDir),
  1193. GetCurrentProcessId());
  1194. if ((iLen <= 0) || (iLen >= sizeof(szNewDirName))) {
  1195. uiEventSubId = IDS_CACHE_DIR_NAME_TOO_LONG;
  1196. goto LExit;
  1197. }
  1198. if (!MoveFileA(m_szPersistCacheDir, szNewDirName)) {
  1199. uiEventSubId = IDS_CACHE_SUBDIR_CREATION_FAILED;
  1200. dwError = GetLastError();
  1201. goto LExit;
  1202. }
  1203. }
  1204. // Now create it
  1205. if (CreateDirectoryA(m_szPersistCacheDir, NULL)) {
  1206. dirAttribs = GetFileAttributesA(m_szPersistCacheDir);
  1207. } else {
  1208. uiEventSubId = IDS_CACHE_SUBDIR_CREATION_FAILED;
  1209. dwError = GetLastError();
  1210. goto LExit;
  1211. }
  1212. if ((dirAttribs == INVALID_FILE_ATTRIBUTES)
  1213. || !(dirAttribs & FILE_ATTRIBUTE_DIRECTORY)) {
  1214. uiEventSubId = IDS_CACHE_SUBDIR_MISSING;
  1215. goto LExit;
  1216. }
  1217. // start the scavenger thread
  1218. m_hOnInitCleanupThread = CreateThread(NULL,
  1219. 0,
  1220. CTemplateCacheManager::OnInitCleanup,
  1221. NULL,
  1222. 0,
  1223. NULL);
  1224. LExit:
  1225. if (uiEventSubId != 0) {
  1226. //
  1227. // an initialization error has occured. log it and indicate initialization fail.
  1228. //
  1229. MSG_Error(IDS_CACHE_INIT_FAILED, pIReq->QueryPszAppPoolIdA(), uiEventSubId, dwError);
  1230. m_fFailedToInitPersistCache = TRUE;
  1231. }
  1232. if (fRevertedToSelf) {
  1233. SetThreadToken(NULL, hImpersonationToken);
  1234. CloseHandle(hImpersonationToken);
  1235. }
  1236. return(!m_fFailedToInitPersistCache);
  1237. }
  1238. /*===================================================================
  1239. CTemplateCacheManager::CTemplateHashTable::CanPersistTemplate
  1240. Parameters:
  1241. pTemplate - The template to test for persistability
  1242. Returns:
  1243. BOOL to indicate if template can be persisted.
  1244. ===================================================================*/
  1245. BOOL CTemplateCacheManager::CTemplateHashTable::CanPersistTemplate(CTemplate *pTemplate)
  1246. {
  1247. // if MaxFiles is zero, then the persist cache is disabled
  1248. if (Glob(dwPersistTemplateMaxFiles) == 0)
  1249. {
  1250. return(FALSE);
  1251. }
  1252. // can't persist if the persist cache failed to init
  1253. if (m_fFailedToInitPersistCache == TRUE)
  1254. {
  1255. return(FALSE);
  1256. }
  1257. // can't persist templates that are marked as debuggable. The
  1258. // script engines need access to the memory.
  1259. if (pTemplate->FDebuggable())
  1260. {
  1261. return(FALSE);
  1262. }
  1263. //
  1264. // Cannot write an Encrypted file to disk as we would be writing it in plaintext.
  1265. //
  1266. if (pTemplate->FIsEncrypted())
  1267. {
  1268. return(FALSE);
  1269. }
  1270. // at this point, we're going to return true. The next part of the code
  1271. // trims the cache as necessary.
  1272. if (m_dwPersistedTemplates >= Glob(dwPersistTemplateMaxFiles))
  1273. {
  1274. //
  1275. // Ignore the value returned from TrimPersistCache as we are not concerned if we met the
  1276. // trim count or not. In anycase, there should be 1 available location to add the new template.
  1277. // Optimization may be to free up couple of places (configurable through the metabase) to achieve performance.
  1278. //
  1279. TrimPersistCache(m_dwPersistedTemplates - Glob(dwPersistTemplateMaxFiles) + 1);
  1280. }
  1281. return(TRUE);
  1282. }
  1283. /*===================================================================
  1284. CTemplateCacheManager::CTemplateHashTable::InsertTemplate
  1285. Parameters:
  1286. pTemplate - Template to insert into the memory cache
  1287. Returns:
  1288. LK_RETCODE indicating the success of the insertion
  1289. ===================================================================*/
  1290. LK_RETCODE CTemplateCacheManager::CTemplateHashTable::InsertTemplate(CTemplate *pTemplate)
  1291. {
  1292. LK_RETCODE rcode = InsertRecord(pTemplate, true);
  1293. if (rcode == LK_SUCCESS) {
  1294. #ifndef PERF_DISABLE
  1295. g_PerfData.Incr_MEMORYTEMPLCACHE();
  1296. g_PerfData.Incr_TEMPLCACHE();
  1297. #endif
  1298. m_dwInMemoryTemplates++;
  1299. pTemplate->PrependTo(m_listMemoryTemplates);
  1300. pTemplate->SetHashTablePtr(this);
  1301. }
  1302. ScavengePersistCache();
  1303. return rcode;
  1304. }
  1305. /*===================================================================
  1306. CTemplateCacheManager::CTemplateHashTable::RemoveTemplate
  1307. Parameters:
  1308. pTemplate - Template to remove from cache
  1309. fPersist - indicate if memory template is a candidate for persist
  1310. Returns:
  1311. LK_RETCODE indicating the success of the removal
  1312. ===================================================================*/
  1313. LK_RETCODE CTemplateCacheManager::CTemplateHashTable::RemoveTemplate(CTemplate *pTemplate,
  1314. BOOL fPersist,
  1315. BOOL fScavengePersistCache)
  1316. {
  1317. LK_RETCODE rcode = LK_SUCCESS;
  1318. #if DBG_PERSTEMPL
  1319. DBGPRINTF((DBG_CONTEXT,
  1320. "RemoveTemplate entered.\n\tTemplate = %s.\n\tfPersist = %d.\n\tFIsPersisted = %d\n",
  1321. pTemplate->GetSourceFileName(),
  1322. fPersist,
  1323. pTemplate->FIsPersisted()));
  1324. #endif
  1325. // if the template isn't in the cache, or if the template isn't on this
  1326. // particular hash table, then just bail. Nothing to
  1327. // do here. It may not be on this particular hash table because the entire
  1328. // table may have been torn off the global cache manager and scheduled for
  1329. // cleanup on the flush thread. In this case, we're checking the wrong
  1330. // table. The flush thread will eventually clean this one up.
  1331. if (pTemplate->FIsEmpty() || (pTemplate->GetHashTablePtr() != this)) {
  1332. return LK_NO_SUCH_KEY;
  1333. }
  1334. // no matter what, this template is going to be unlinked from it's
  1335. // current CDblLink
  1336. pTemplate->UnLink();
  1337. // update the appropriate counter
  1338. if (pTemplate->FIsPersisted() == FALSE) {
  1339. // decrement the number of InMemoryTemplates...
  1340. #ifndef PERF_DISABLE
  1341. g_PerfData.Decr_MEMORYTEMPLCACHE();
  1342. #endif
  1343. m_dwInMemoryTemplates--;
  1344. }
  1345. else {
  1346. m_dwPersistedTemplates--;
  1347. }
  1348. // if asked to be persisted, see if it's a candidate to be persisted.
  1349. if (fPersist && CanPersistTemplate(pTemplate)) {
  1350. pTemplate->m_fIsPersisted = TRUE;
  1351. // if successfully persisted, then add to the list of
  1352. // persisted templates
  1353. pTemplate->PrependTo(m_listPersistTemplates);
  1354. m_dwPersistedTemplates++;
  1355. }
  1356. else {
  1357. #ifndef PERF_DISABLE
  1358. g_PerfData.Decr_TEMPLCACHE();
  1359. #endif
  1360. // if not asked to persist, then delete the record.
  1361. rcode = DeleteRecord(pTemplate);
  1362. }
  1363. if (fScavengePersistCache)
  1364. ScavengePersistCache();
  1365. return rcode;
  1366. }
  1367. /*===================================================================
  1368. CTemplateCacheManager::CTemplateHashTable::FindTemplate
  1369. Parameters:
  1370. rTemplate - the key for the template being looked up
  1371. Returns:
  1372. LK_RETCODE indicating the success of the look up
  1373. ===================================================================*/
  1374. LK_RETCODE CTemplateCacheManager::CTemplateHashTable::FindTemplate(const CTemplateKey &rTemplateKey, CTemplate **ppTemplate, BOOL *pfNeedsCheck)
  1375. {
  1376. #if DBG_PERSTEMPL
  1377. DBGPRINTF((DBG_CONTEXT,
  1378. "FindTemplate entered\n\tLooking for %s\n",
  1379. rTemplateKey.szPathTranslated));
  1380. #endif
  1381. #ifndef PERF_DISABLE
  1382. g_PerfData.Incr_MEMORYTEMPLCACHETRYS();
  1383. g_PerfData.Incr_TEMPLCACHETRYS();
  1384. #endif
  1385. LK_RETCODE rcode = FindKey(&rTemplateKey, ppTemplate);
  1386. // see if we found it.
  1387. if (rcode == LK_SUCCESS) {
  1388. #if DBG_PERSTEMPL
  1389. DBGPRINTF((DBG_CONTEXT,
  1390. "Template found\n\tfPersisted = %d\n",
  1391. (*ppTemplate)->FIsPersisted()));
  1392. #endif
  1393. #ifndef PERF_DISABLE
  1394. g_PerfData.Incr_TEMPLCACHEHITS();
  1395. #endif
  1396. // found it. Is it persisted?
  1397. if ((*ppTemplate)->FIsPersisted()) {
  1398. // It is persisted. Unlink it from the persisted list.
  1399. (*ppTemplate)->UnLink();
  1400. m_dwPersistedTemplates--;
  1401. // unpersist it
  1402. if ((*ppTemplate)->UnPersistData() != S_OK) {
  1403. // error occurred
  1404. // get the template out of the cache
  1405. DeleteRecord(*ppTemplate);
  1406. // release the reference that the cache had on the template
  1407. (*ppTemplate)->Release();
  1408. // NULL out *ppTemplate so that the caller doesn't think they
  1409. // got a valid template
  1410. *ppTemplate = NULL;
  1411. #ifndef PERF_DISABLE
  1412. g_PerfData.Decr_TEMPLCACHE();
  1413. #endif
  1414. // return NO_SUCH_KEY so that a new template will be built
  1415. return(LK_NO_SUCH_KEY);
  1416. }
  1417. // bump the number of in memory templates
  1418. #ifndef PERF_DISABLE
  1419. g_PerfData.Incr_MEMORYTEMPLCACHE();
  1420. #endif
  1421. m_dwInMemoryTemplates++;
  1422. }
  1423. else {
  1424. #ifndef PERF_DISABLE
  1425. g_PerfData.Incr_MEMORYTEMPLCACHEHITS();
  1426. #endif
  1427. }
  1428. // add it to, or move it to the top of, the memory templates
  1429. (*ppTemplate)->PrependTo(m_listMemoryTemplates);
  1430. }
  1431. if (pfNeedsCheck && *ppTemplate && (*ppTemplate)->FNeedsValidation() && (*ppTemplate)->FIsValid())
  1432. *pfNeedsCheck = TRUE;
  1433. ScavengePersistCache();
  1434. return rcode;
  1435. }