Source code of Windows XP (NT5)
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.

3040 lines
82 KiB

  1. // This is a part of the Active Template Library.
  2. // Copyright (C) 1996-2001 Microsoft Corporation
  3. // All rights reserved.
  4. //
  5. // This source code is only intended as a supplement to the
  6. // Active Template Library Reference and related
  7. // electronic documentation provided with the library.
  8. // See these sources for detailed information regarding the
  9. // Active Template Library product.
  10. #ifndef __ATLCACHE_H__
  11. #define __ATLCACHE_H__
  12. #pragma once
  13. #include <atltime.h>
  14. #include <atlutil.h>
  15. #include <atlcoll.h>
  16. #include <atlperf.h>
  17. #include <atlcom.h>
  18. #include <atlstr.h>
  19. #include <atlsrvres.h>
  20. #include <atldbcli.h>
  21. #pragma warning (push)
  22. #ifndef _ATL_NO_PRAGMA_WARNINGS
  23. #pragma warning(disable: 4511) // copy constructor could not be generated
  24. #pragma warning(disable: 4512) // assignment operator could not be generated
  25. #endif //!_ATL_NO_PRAGMA_WARNINGS
  26. #ifndef _CPPUNWIND
  27. #pragma warning(disable: 4702) // unreachable code
  28. #endif
  29. namespace ATL {
  30. //forward declarations;
  31. class CStdStatClass;
  32. class CPerfStatClass;
  33. typedef struct __CACHEITEM
  34. {
  35. } *HCACHEITEM;
  36. //Implementation of a cache that stores pointers to void
  37. extern "C" __declspec(selectany) IID IID_IMemoryCacheClient = {0xb721b49d, 0xbb57, 0x47bc, { 0xac, 0x43, 0xa8, 0xd4, 0xc0, 0x7d, 0x18, 0x3d } };
  38. extern "C" __declspec(selectany) IID IID_IMemoryCache = { 0x9c6cfb46, 0xfbde, 0x4f8b, { 0xb9, 0x44, 0x2a, 0xa0, 0x5d, 0x96, 0xeb, 0x5c } };
  39. extern "C" __declspec(selectany) IID IID_IMemoryCacheControl = { 0x7634b28b, 0xd819, 0x409d, { 0xb9, 0x6e, 0xfc, 0x9f, 0x3a, 0xba, 0x32, 0x9f } };
  40. extern "C" __declspec(selectany) IID IID_IMemoryCacheStats = { 0xd4b6df2d, 0x4bc0, 0x4734, { 0x8a, 0xce, 0xb7, 0x3a, 0xb, 0x97, 0x59, 0x56 } };
  41. __interface ATL_NO_VTABLE __declspec(uuid("b721b49d-bb57-47bc-ac43-a8d4c07d183d"))
  42. IMemoryCacheClient : public IUnknown
  43. {
  44. // IMemoryCacheClient methods
  45. STDMETHOD( Free )(const void *pvData);
  46. };
  47. __interface ATL_NO_VTABLE __declspec(uuid("9c6cfb46-fbde-4f8b-b944-2aa05d96eb5c"))
  48. IMemoryCache : public IUnknown
  49. {
  50. // IMemoryCache Methods
  51. STDMETHOD(Add)(LPCSTR szKey, void *pvData, DWORD dwSize,
  52. FILETIME *pftExpireTime,
  53. HINSTANCE hInstClient, HCACHEITEM *phEntry,
  54. IMemoryCacheClient *pClient);
  55. STDMETHOD(LookupEntry)(LPCSTR szKey, HCACHEITEM * phEntry);
  56. STDMETHOD(GetData)(const HCACHEITEM hEntry, void **ppvData, DWORD *pdwSize) const;
  57. STDMETHOD(ReleaseEntry)(const HCACHEITEM hEntry);
  58. STDMETHOD(RemoveEntry)(const HCACHEITEM hEntry);
  59. STDMETHOD(RemoveEntryByKey)(LPCSTR szKey);
  60. STDMETHOD(Flush)();
  61. };
  62. __interface ATL_NO_VTABLE __declspec(uuid("7634b28b-d819-409d-b96e-fc9f3aba329f"))
  63. IMemoryCacheControl : public IUnknown
  64. {
  65. // IMemoryCacheControl Methods
  66. STDMETHOD(SetMaxAllowedSize)(DWORD dwSize);
  67. STDMETHOD(GetMaxAllowedSize)(DWORD *pdwSize);
  68. STDMETHOD(SetMaxAllowedEntries)(DWORD dwSize);
  69. STDMETHOD(GetMaxAllowedEntries)(DWORD *pdwSize);
  70. STDMETHOD(ResetCache)();
  71. };
  72. __interface ATL_NO_VTABLE __declspec(uuid("d4b6df2d-4bc0-4734-8ace-b73a0b975956"))
  73. IMemoryCacheStats : public IUnknown
  74. {
  75. // IMemoryCacheStats Methods
  76. STDMETHOD(ClearStats)();
  77. STDMETHOD(GetHitCount)(DWORD *pdwSize);
  78. STDMETHOD(GetMissCount)(DWORD *pdwSize);
  79. STDMETHOD(GetCurrentAllocSize)(DWORD *pdwSize);
  80. STDMETHOD(GetMaxAllocSize)(DWORD *pdwSize);
  81. STDMETHOD(GetCurrentEntryCount)(DWORD *pdwSize);
  82. STDMETHOD(GetMaxEntryCount)(DWORD *pdwSize);
  83. };
  84. struct DLL_CACHE_ENTRY
  85. {
  86. HINSTANCE hInstDll;
  87. DWORD dwRefs;
  88. BOOL bAlive;
  89. CHAR szDllName[_MAX_PATH];
  90. };
  91. inline bool operator==(const DLL_CACHE_ENTRY& entry1, const DLL_CACHE_ENTRY& entry2)
  92. {
  93. return (entry1.hInstDll == entry2.hInstDll);
  94. }
  95. //
  96. // IDllCache
  97. // An interface that is used to load and unload Dlls.
  98. //
  99. __interface ATL_NO_VTABLE __declspec(uuid("A12478AB-D261-42f9-B525-7589143C1C97"))
  100. IDllCache : public IUnknown
  101. {
  102. // IDllCache methods
  103. virtual HINSTANCE Load(LPCSTR szFileName, void *pPeerInfo);
  104. virtual BOOL Free(HINSTANCE hInstance);
  105. virtual BOOL AddRefModule(HINSTANCE hInstance);
  106. virtual BOOL ReleaseModule(HINSTANCE hInstance);
  107. virtual HRESULT GetEntries(DWORD dwCount, DLL_CACHE_ENTRY *pEntries, DWORD *pdwCopied);
  108. virtual HRESULT Flush();
  109. };
  110. #ifndef ATL_CACHE_KEY_LENGTH
  111. #define ATL_CACHE_KEY_LENGTH 128
  112. #endif
  113. typedef CFixedStringT<CStringA, ATL_CACHE_KEY_LENGTH> CFixedStringKey;
  114. // No flusher -- only expired entries will be removed from the cache
  115. // Also gives the skeleton for all of the flushers
  116. class CNoFlusher
  117. {
  118. public:
  119. struct CCacheData
  120. {
  121. };
  122. void Add(CCacheData * /*pItem*/) { }
  123. void Remove(CCacheData * /*pItem*/) { }
  124. void Access(CCacheData * /*pItem*/) { }
  125. CCacheData * GetStart() const { return NULL; }
  126. CCacheData * GetNext(CCacheData * /*pCur*/) const { return NULL; }
  127. };
  128. // Old flusher -- oldest items are flushed first
  129. class COldFlusher
  130. {
  131. public:
  132. struct CCacheData
  133. {
  134. CCacheData * pNext;
  135. CCacheData * pPrev;
  136. };
  137. CCacheData * pHead;
  138. CCacheData * pTail;
  139. COldFlusher() : pHead(NULL), pTail(NULL)
  140. {
  141. }
  142. // Add it to the tail of the list
  143. void Add(CCacheData * pItem)
  144. {
  145. ATLASSERT(pItem);
  146. pItem->pNext = NULL;
  147. pItem->pPrev = pTail;
  148. if (pHead)
  149. {
  150. pTail->pNext = pItem;
  151. pTail = pItem;
  152. }
  153. else
  154. {
  155. pHead = pItem;
  156. pTail = pItem;
  157. }
  158. }
  159. void Remove(CCacheData * pItem)
  160. {
  161. ATLASSERT(pItem);
  162. CCacheData * pPrev = pItem->pPrev;
  163. CCacheData * pNext = pItem->pNext;
  164. if (pPrev)
  165. pPrev->pNext = pNext;
  166. else
  167. pHead = pNext;
  168. if (pNext)
  169. pNext->pPrev = pPrev;
  170. else
  171. pTail = pPrev;
  172. }
  173. void Access(CCacheData * /*pItem*/)
  174. {
  175. }
  176. void Release(CCacheData * /*pItem*/)
  177. {
  178. }
  179. CCacheData * GetStart() const
  180. {
  181. return pHead;
  182. }
  183. CCacheData * GetNext(CCacheData * pCur) const
  184. {
  185. if (pCur != NULL)
  186. return pCur->pNext;
  187. else
  188. return NULL;
  189. }
  190. };
  191. // Least recently used flusher -- the item that was accessed the longest time ago is flushed
  192. class CLRUFlusher : public COldFlusher
  193. {
  194. public:
  195. // Move it to the tail of the list
  196. void Access(CCacheData * pItem)
  197. {
  198. ATLASSERT(pItem);
  199. Remove(pItem);
  200. Add(pItem);
  201. }
  202. };
  203. // Least often used flusher
  204. class CLOUFlusher : public COldFlusher
  205. {
  206. public:
  207. struct CCacheData : public COldFlusher::CCacheData
  208. {
  209. DWORD dwAccessed;
  210. };
  211. CCacheData * pHead;
  212. CCacheData * pTail;
  213. // Adds to the tail of the list
  214. void Add(CCacheData * pItem)
  215. {
  216. ATLASSERT(pItem);
  217. pItem->dwAccessed = 1;
  218. COldFlusher::Add(pItem);
  219. }
  220. void Access(CCacheData * pItem)
  221. {
  222. ATLASSERT(pItem);
  223. pItem->dwAccessed++;
  224. CCacheData * pMark = static_cast<CCacheData *>(pItem->pPrev);
  225. if (!pMark) // The item is already at the head
  226. return;
  227. if (pMark->dwAccessed >= pItem->dwAccessed) // The element before it has
  228. return; // been accessed more times
  229. Remove(pItem);
  230. while (pMark && (pItem->dwAccessed < pMark->dwAccessed))
  231. pMark = static_cast<CCacheData *>(pMark->pPrev);
  232. // pMark points to the first element that has been accessed more times,
  233. // so add pItem after pMark
  234. if (pMark)
  235. {
  236. CCacheData *pNext = static_cast<CCacheData *>(pMark->pNext);
  237. pMark->pNext = pItem;
  238. pItem->pPrev = pMark;
  239. pItem->pNext = pNext;
  240. pNext->pPrev = pItem;
  241. }
  242. else // Ran out of items -- put it on the head
  243. {
  244. pItem->pNext = pHead;
  245. pItem->pPrev = NULL;
  246. if (pHead)
  247. pHead->pPrev = pItem;
  248. else // the list was empty
  249. pTail = pItem;
  250. pHead = pItem;
  251. }
  252. }
  253. // We start at the tail and move forward for this flusher
  254. CCacheData * GetStart() const
  255. {
  256. return pTail;
  257. }
  258. CCacheData * GetNext(CCacheData * pCur) const
  259. {
  260. if (pCur != NULL)
  261. return static_cast<CCacheData *>(pCur->pPrev);
  262. else
  263. return NULL;
  264. }
  265. };
  266. template <class CFirst, class CSecond>
  267. class COrFlushers : public CFirst, public CSecond
  268. {
  269. struct CCacheData : public CFirst::CCacheData, CSecond::CCacheData
  270. {
  271. };
  272. BOOL m_bWhich;
  273. COrFlushers() : CFirst(), CSecond()
  274. {
  275. m_bWhich = FALSE;
  276. }
  277. BOOL Switch()
  278. {
  279. m_bWhich = !m_bWhich;
  280. return m_bWhich;
  281. }
  282. void Add(CCacheData * pItem)
  283. {
  284. ATLASSERT(pItem);
  285. CFirst::Add(pItem);
  286. CSecond::Add(pItem);
  287. }
  288. void Remove(CCacheData * pItem)
  289. {
  290. ATLASSERT(pItem);
  291. CFirst::Remove(pItem);
  292. CSecond::Remove(pItem);
  293. }
  294. void Access(CCacheData * pItem)
  295. {
  296. ATLASSERT(pItem);
  297. CFirst::Access(pItem);
  298. CSecond::Access(pItem);
  299. }
  300. void Release(CCacheData * pItem)
  301. {
  302. ATLASSERT(pItem);
  303. CFirst::Release(pItem);
  304. CSecond::Release(pItem);
  305. }
  306. CCacheData * GetStart() const
  307. {
  308. if (bWhich)
  309. return static_cast<CCacheData *>(CFirst::GetStart());
  310. else
  311. return static_cast<CCacheData *>(CSecond::GetStart());
  312. }
  313. CCacheData * GetNext(CCacheData * pCur) const
  314. {
  315. if (bWhich)
  316. return static_cast<CCacheData *>(CFirst::GetNext(pCur));
  317. else
  318. return static_cast<CCacheData *>(CSecond::GetNext(pCur));
  319. }
  320. };
  321. class CNoExpire
  322. {
  323. public:
  324. struct CCacheData
  325. {
  326. };
  327. void Add(CCacheData * /*pItem*/) { }
  328. void Commit(CCacheData * /*pItem*/) { }
  329. void Access(CCacheData * /*pItem*/) { }
  330. void Remove(CCacheData * /*pItem*/) { }
  331. void Start() { }
  332. BOOL IsExpired(CCacheData * /*pItem*/) { return FALSE; }
  333. CCacheData * GetExpired() { return NULL; }
  334. };
  335. class CExpireCuller
  336. {
  337. public:
  338. struct CCacheData
  339. {
  340. CFileTime cftExpireTime;
  341. CCacheData * pNext;
  342. CCacheData * pPrev;
  343. };
  344. CFileTime m_cftCurrent;
  345. CCacheData *pHead;
  346. CCacheData *pTail;
  347. CExpireCuller()
  348. {
  349. pHead = NULL;
  350. pTail = NULL;
  351. }
  352. // Element is being added -- perform necessary initialization
  353. void Add(CCacheData * pItem)
  354. {
  355. pItem;
  356. ATLASSERT(pItem);
  357. }
  358. // Expiration data has been set -- add to main list
  359. // Head is the first item to expire
  360. // a FILETIME of 0 indicates that the item should never expire
  361. void Commit(CCacheData * pItem)
  362. {
  363. ATLASSERT(pItem);
  364. if (!pHead)
  365. {
  366. pHead = pItem;
  367. pTail = pItem;
  368. pItem->pNext = NULL;
  369. pItem->pPrev = NULL;
  370. return;
  371. }
  372. if (CFileTime(pItem->cftExpireTime) == 0)
  373. {
  374. pTail->pNext = pItem;
  375. pItem->pPrev = pTail;
  376. pItem->pNext = NULL;
  377. pTail = pItem;
  378. return;
  379. }
  380. CCacheData * pMark = pHead;
  381. while (pMark && (pMark->cftExpireTime < pItem->cftExpireTime))
  382. pMark = pMark->pNext;
  383. if (pMark) // An entry was found that expires after the added entry
  384. {
  385. CCacheData *pPrev = pMark->pPrev;
  386. if (pPrev)
  387. pPrev->pNext = pItem;
  388. else
  389. pHead = pItem;
  390. pItem->pNext = pMark;
  391. pItem->pPrev = pPrev;
  392. pMark->pPrev = pItem;
  393. }
  394. else // Ran out of items -- put it on the tail
  395. {
  396. if (pTail)
  397. pTail->pNext = pItem;
  398. pItem->pPrev = pTail;
  399. pItem->pNext = NULL;
  400. pTail = pItem;
  401. }
  402. }
  403. void Access(CCacheData * /*pItem*/)
  404. {
  405. }
  406. void Release(CCacheData * /*pItem*/)
  407. {
  408. }
  409. void Remove(CCacheData * pItem)
  410. {
  411. ATLASSERT(pItem);
  412. CCacheData *pPrev = pItem->pPrev;
  413. CCacheData *pNext = pItem->pNext;
  414. if (pPrev)
  415. pPrev->pNext = pNext;
  416. else
  417. pHead = pNext;
  418. if (pNext)
  419. pNext->pPrev = pPrev;
  420. else
  421. pTail = pPrev;
  422. }
  423. // About to start culling
  424. void Start()
  425. {
  426. m_cftCurrent = CFileTime::GetCurrentTime();
  427. }
  428. BOOL IsExpired(CCacheData *pItem)
  429. {
  430. if ((pItem->cftExpireTime != 0) &&
  431. m_cftCurrent > pItem->cftExpireTime)
  432. return TRUE;
  433. return FALSE;
  434. }
  435. // Get the next expired entry
  436. CCacheData * GetExpired()
  437. {
  438. if (!pHead)
  439. return NULL;
  440. if (IsExpired(pHead))
  441. return pHead;
  442. return NULL;
  443. }
  444. };
  445. class CLifetimeCuller : public CExpireCuller
  446. {
  447. public:
  448. struct CCacheData : public CExpireCuller::CCacheData
  449. {
  450. ULONGLONG nLifespan;
  451. };
  452. void Add(CCacheData * pItem)
  453. {
  454. ATLASSERT(pItem);
  455. pItem->nLifespan = 0;
  456. CExpireCuller::Add(pItem);
  457. }
  458. void Commit(CCacheData * pItem)
  459. {
  460. ATLASSERT(pItem);
  461. if (pItem->nLifespan == 0)
  462. pItem->cftExpireTime = 0;
  463. else
  464. pItem->cftExpireTime = CFileTime(CFileTime::GetCurrentTime().GetTime() + pItem->nLifespan);
  465. CExpireCuller::Commit(pItem);
  466. }
  467. void Access(CCacheData * pItem)
  468. {
  469. ATLASSERT(pItem);
  470. CExpireCuller::Remove(pItem);
  471. Commit(pItem);
  472. }
  473. CCacheData * GetExpired()
  474. {
  475. return static_cast<CCacheData *>(CExpireCuller::GetExpired());
  476. }
  477. };
  478. template <__int64 ftLifespan>
  479. class CFixedLifetimeCuller : public CExpireCuller
  480. {
  481. public:
  482. void Commit(CCacheData * pItem)
  483. {
  484. ATLASSERT(pItem);
  485. if (ftLifespan == 0)
  486. pItem->cftExpireTime = 0;
  487. else
  488. pItem->cftExpireTime = CFileTime::GetCurrentTime() + CFileTimeSpan(ftLifespan);
  489. CExpireCuller::Commit(pItem);
  490. }
  491. void Access(CCacheData * pItem)
  492. {
  493. ATLASSERT(pItem);
  494. CExpireCuller::Remove(pItem);
  495. Commit(pItem);
  496. }
  497. CCacheData * GetExpired()
  498. {
  499. return static_cast<CCacheData *>(CExpireCuller::GetExpired());
  500. }
  501. };
  502. template <class CFirst, class CSecond>
  503. class OrCullers : public CFirst, public CSecond
  504. {
  505. struct CCacheData : public CFirst::CCacheData, public CSecond::CCacheData
  506. {
  507. };
  508. void Add(CCacheData * pItem)
  509. {
  510. CFirst::Add(pItem);
  511. CSecond::Add(pItem);
  512. }
  513. void Access(CCacheData * pItem)
  514. {
  515. CFirst::Access(pItem);
  516. CSecond::Access(pItem);
  517. }
  518. void Remove(CCacheData * pItem)
  519. {
  520. CFirst::Remove(pItem);
  521. CSecond::Remove(pItem);
  522. }
  523. void Start(CCacheData * pItem)
  524. {
  525. CFirst::Start(pItem);
  526. CSecond::Start(pItem);
  527. }
  528. CCacheData * GetExpired()
  529. {
  530. CCacheData *pItem = static_cast<CCacheData *>(CFirst::GetExpired());
  531. if (!pItem)
  532. pItem = static_cast<CCacheData *>(CSecond::GetExpired());
  533. return pItem;
  534. }
  535. BOOL IsExpired(CCacheData * pItem)
  536. {
  537. return (CFirst::IsExpired(pItem) || CSecond::IsExpired(pItem))
  538. }
  539. };
  540. //
  541. //CMemoryCacheBase
  542. // Description:
  543. // This class provides the implementation of a generic cache that stores
  544. // elements in memory. CMemoryCacheBase uses the CCacheDataBase generic
  545. // cache element structure to hold items in the cache. The cache is
  546. // implemented using the CAtlMap map class. CMemoryCache uses a wide
  547. // character string as it's Key type to identify entries. Entries must
  548. // have unique key values. If you try to add an entry with a key that
  549. // is exactly the same as an existing key, the existing entry will be
  550. // overwritten.
  551. //
  552. // Template Parameters:
  553. // T: The class that inherits from this class. This class must implement
  554. // void OnDestroyEntry(NodeType *pEntry);
  555. // DataType: Specifies the type of the element to be stored in the memory
  556. // cache such as CString or void*
  557. // NodeInfo: Specifies any additional data that should be stored in each item
  558. // in the cache
  559. // keyType, keyTrait : specifies the key type and traits (see CAtlMap)
  560. // Flusher : the class responsible for determining which data should be flushed
  561. // when the cache is at a configuration limit
  562. // Culler : the class responsible for determining which data should be removed
  563. // from the cache due to expiration
  564. // SyncClass:Specifies the class that will be used for thread synchronization
  565. // when accessing the cache. The class interface for SyncClass must
  566. // be identical to that of CComCriticalSection (see atlbase.h)
  567. // StatClass: Class used to contain statistics about this cache.
  568. // REVIEW: modify interface to Flusher system
  569. template <class T,
  570. class DataType,
  571. class NodeInfo=CCacheDataBase,
  572. class keyType=CFixedStringKey,
  573. class KeyTrait=CStringElementTraits<CFixedStringKey >,
  574. class Flusher=COldFlusher,
  575. class Culler=CExpireCuller,
  576. class SyncClass=CComCriticalSection,
  577. class StatClass=CStdStatClass >
  578. class CMemoryCacheBase
  579. {
  580. protected:
  581. typedef keyType keytype;
  582. struct NodeType : public __CACHEITEM, public NodeInfo,
  583. public Flusher::CCacheData, public Culler::CCacheData
  584. {
  585. NodeType()
  586. {
  587. pos = NULL;
  588. dwSize = 0;
  589. dwRef = 0;
  590. }
  591. DataType Data;
  592. POSITION pos;
  593. DWORD dwSize;
  594. DWORD dwRef;
  595. };
  596. typedef CAtlMap<keyType, NodeType *, KeyTrait> mapType;
  597. SyncClass m_syncObj;
  598. StatClass m_statObj;
  599. Flusher m_flusher;
  600. Culler m_culler;
  601. //memory cache configuration parameters
  602. DWORD m_dwMaxAllocationSize;
  603. DWORD m_dwMaxEntries;
  604. BOOL m_bInitialized;
  605. public:
  606. mapType m_hashTable;
  607. CMemoryCacheBase() throw() :
  608. m_dwMaxAllocationSize(0xFFFFFFFF),
  609. m_dwMaxEntries(0xFFFFFFFF),
  610. m_bInitialized(FALSE)
  611. {
  612. }
  613. //Initializes the cache and the cache synchronization object
  614. //Also the performance monitoring
  615. HRESULT Initialize() throw()
  616. {
  617. if (m_bInitialized)
  618. return HRESULT_FROM_WIN32(ERROR_ALREADY_INITIALIZED);
  619. HRESULT hr;
  620. hr = m_syncObj.Init();
  621. if (hr == S_OK)
  622. hr = m_statObj.Initialize();
  623. m_bInitialized = TRUE;
  624. return hr;
  625. }
  626. //removes all entries whether or not they are initialized.
  627. HRESULT Uninitialize() throw()
  628. {
  629. if (!m_bInitialized)
  630. return S_OK;
  631. //clear out the hash table
  632. m_syncObj.Lock();
  633. RemoveAllEntries();
  634. m_statObj.Uninitialize();
  635. m_syncObj.Unlock();
  636. m_syncObj.Term();
  637. m_bInitialized = FALSE;
  638. return S_OK;
  639. }
  640. //Adds an entry to the cache.
  641. //Also, adds an initial reference on the entry if phEntry is not NULL
  642. HRESULT AddEntry(
  643. const keyType &Key, //key for entry
  644. const DataType &data, //See the DataType template parameter
  645. DWORD dwSize, //Size of memory to be stored in the cache
  646. HCACHEITEM *phEntry = NULL //out pointer that will contain a handle to the new
  647. //cache entry on success.
  648. ) throw()
  649. {
  650. _ATLTRY
  651. {
  652. ATLASSERT(m_bInitialized);
  653. NodeType * pEntry = new NodeType;
  654. if (!pEntry)
  655. return E_OUTOFMEMORY;
  656. //fill entry
  657. if (phEntry)
  658. {
  659. *phEntry = static_cast<HCACHEITEM>(pEntry);
  660. pEntry->dwRef++;
  661. }
  662. pEntry->Data = data;
  663. pEntry->dwSize = dwSize;
  664. m_syncObj.Lock();
  665. POSITION pos = (POSITION)m_hashTable.Lookup(Key);
  666. if (pos != NULL)
  667. {
  668. RemoveAt(pos, FALSE);
  669. m_hashTable.GetValueAt(pos) = pEntry;
  670. }
  671. else
  672. pos = m_hashTable.SetAt(Key, pEntry);
  673. pEntry->pos = pos;
  674. m_statObj.AddElement(dwSize);
  675. m_flusher.Add(pEntry);
  676. m_culler.Add(pEntry);
  677. m_syncObj.Unlock();
  678. if (!phEntry)
  679. return CommitEntry(static_cast<HCACHEITEM>(pEntry));
  680. return S_OK;
  681. }
  682. _ATLCATCHALL()
  683. {
  684. return E_FAIL;
  685. }
  686. }
  687. // Commits the entry to the cache
  688. HRESULT CommitEntry(const HCACHEITEM hEntry)
  689. {
  690. ATLASSERT(m_bInitialized);
  691. if (!hEntry || hEntry == INVALID_HANDLE_VALUE)
  692. return E_INVALIDARG;
  693. m_syncObj.Lock();
  694. NodeType *pEntry = static_cast<NodeType *>(hEntry);
  695. m_culler.Commit(pEntry);
  696. m_syncObj.Unlock();
  697. return S_OK;
  698. }
  699. // Looks up an entry and returns a handle to it,
  700. // also updates access count and reference count
  701. HRESULT LookupEntry(const keyType &Key, HCACHEITEM * phEntry) throw()
  702. {
  703. ATLASSERT(m_bInitialized);
  704. HRESULT hr = E_FAIL;
  705. m_syncObj.Lock();
  706. POSITION pos = (POSITION)m_hashTable.Lookup(Key);
  707. if (pos != NULL)
  708. {
  709. NodeType * pEntry = m_hashTable.GetValueAt(pos);
  710. m_flusher.Access(pEntry);
  711. m_culler.Access(pEntry);
  712. if (phEntry)
  713. {
  714. pEntry->dwRef++;
  715. *phEntry = static_cast<HCACHEITEM>(pEntry);
  716. }
  717. m_statObj.Hit();
  718. hr = S_OK;
  719. }
  720. else
  721. {
  722. *phEntry = NULL;
  723. m_statObj.Miss();
  724. }
  725. m_syncObj.Unlock();
  726. return hr;
  727. }
  728. // Gets the data based on the handle. Is thread-safe as long as there is a
  729. // reference on the data
  730. HRESULT GetEntryData(const HCACHEITEM hEntry, DataType *pData, DWORD *pdwSize) const throw()
  731. {
  732. ATLASSERT(m_bInitialized);
  733. ATLASSERT(pData != NULL || pdwSize != NULL); // At least one should not be NULL
  734. if (!hEntry || hEntry == INVALID_HANDLE_VALUE)
  735. return E_INVALIDARG;
  736. NodeType * pEntry = static_cast<NodeType *>(hEntry);
  737. if (pData)
  738. *pData = pEntry->Data;
  739. if (pdwSize)
  740. *pdwSize = pEntry->dwSize;
  741. return S_OK;
  742. }
  743. // Unreferences the entry based on the handle
  744. DWORD ReleaseEntry(const HCACHEITEM hEntry) throw()
  745. {
  746. ATLASSERT(m_bInitialized);
  747. if (!hEntry || hEntry == INVALID_HANDLE_VALUE)
  748. return (DWORD)-1;
  749. m_syncObj.Lock();
  750. NodeType * pEntry = static_cast<NodeType *>(hEntry);
  751. m_flusher.Release(pEntry);
  752. m_culler.Release(pEntry);
  753. ATLASSERT(pEntry->dwRef > 0);
  754. DWORD dwRef = --pEntry->dwRef;
  755. if ((pEntry->pos == NULL) && (pEntry->dwRef == 0))
  756. InternalRemoveEntry(pEntry);
  757. m_syncObj.Unlock();
  758. return dwRef;
  759. }
  760. // Increments the entry's reference count
  761. DWORD AddRefEntry(const HCACHEITEM hEntry) throw()
  762. {
  763. ATLASSERT(m_bInitialized);
  764. if (!hEntry || hEntry == INVALID_HANDLE_VALUE)
  765. return (DWORD)-1;
  766. m_syncObj.Lock();
  767. NodeType * pEntry = static_cast<NodeType *>(hEntry);
  768. m_flusher.Access(pEntry);
  769. m_culler.Access(pEntry);
  770. DWORD dwRef = ++pEntry->dwRef;
  771. m_syncObj.Unlock();
  772. return dwRef;
  773. }
  774. // Removes an entry from the cache regardless of whether or
  775. // not it has expired. If there are references, it detaches
  776. // the entry so that future lookups will fail, and when
  777. // the ref count drops to zero, it will be deleted
  778. HRESULT RemoveEntryByKey(const keyType &Key) throw()
  779. {
  780. ATLASSERT(m_bInitialized);
  781. HCACHEITEM hEntry;
  782. HRESULT hr = LookupEntry(Key, &hEntry);
  783. if (hr == S_OK)
  784. hr = RemoveEntry(hEntry);
  785. return hr;
  786. }
  787. // Removes the element from the cache. If there are still
  788. // references, then the entry is detached.
  789. HRESULT RemoveEntry(const HCACHEITEM hEntry) throw()
  790. {
  791. ATLASSERT(m_bInitialized);
  792. if (!hEntry || hEntry == INVALID_HANDLE_VALUE)
  793. return E_INVALIDARG;
  794. m_syncObj.Lock();
  795. NodeType * pEntry = static_cast<NodeType *>(hEntry);
  796. m_flusher.Release(pEntry);
  797. m_culler.Release(pEntry);
  798. ATLASSERT(pEntry->dwRef > 0);
  799. pEntry->dwRef--;
  800. RemoveAt(pEntry->pos, TRUE);
  801. m_syncObj.Unlock();
  802. return S_OK;
  803. }
  804. // CullEntries removes all expired items
  805. HRESULT CullEntries() throw()
  806. {
  807. ATLASSERT(m_bInitialized);
  808. m_syncObj.Lock();
  809. m_culler.Start();
  810. while (NodeType *pNode = static_cast<NodeType *>(m_culler.GetExpired()))
  811. RemoveAt(pNode->pos, TRUE);
  812. m_syncObj.Unlock();
  813. return S_OK;
  814. }
  815. // FlushEntries reduces the cache to meet the configuration requirements
  816. HRESULT FlushEntries() throw()
  817. {
  818. ATLASSERT(m_bInitialized);
  819. CullEntries();
  820. m_syncObj.Lock();
  821. NodeType * pNode = static_cast<NodeType *>(m_flusher.GetStart());
  822. while (pNode &&
  823. (((m_statObj.GetCurrentEntryCount() > m_dwMaxEntries)) ||
  824. ((m_statObj.GetCurrentAllocSize() > m_dwMaxAllocationSize))))
  825. {
  826. NodeType *pNext = static_cast<NodeType *>(m_flusher.GetNext(pNode));
  827. if (pNode->dwRef == 0)
  828. RemoveAt(pNode->pos, TRUE);
  829. pNode = pNext;
  830. }
  831. m_syncObj.Unlock();
  832. return S_OK;
  833. }
  834. HRESULT STDMETHODCALLTYPE SetMaxAllowedSize(DWORD dwSize) throw()
  835. {
  836. m_dwMaxAllocationSize = dwSize;
  837. return S_OK;
  838. }
  839. HRESULT STDMETHODCALLTYPE GetMaxAllowedSize(DWORD *pdwSize) throw()
  840. {
  841. if (!pdwSize)
  842. return E_POINTER;
  843. *pdwSize = m_dwMaxAllocationSize;
  844. return S_OK;
  845. }
  846. HRESULT STDMETHODCALLTYPE SetMaxAllowedEntries(DWORD dwSize) throw()
  847. {
  848. m_dwMaxEntries = dwSize;
  849. return S_OK;
  850. }
  851. HRESULT STDMETHODCALLTYPE GetMaxAllowedEntries(DWORD *pdwSize) throw()
  852. {
  853. if (!pdwSize)
  854. return E_POINTER;
  855. *pdwSize = m_dwMaxEntries;
  856. return S_OK;
  857. }
  858. HRESULT ResetCache() throw()
  859. {
  860. ATLASSERT(m_bInitialized);
  861. m_syncObj.Lock();
  862. ClearStats();
  863. m_hashTable.RemoveAll();
  864. m_syncObj.Unlock();
  865. return S_OK;
  866. }
  867. HRESULT ClearStats() throw()
  868. {
  869. m_statObj.ResetCounters();
  870. return S_OK;
  871. }
  872. HRESULT RemoveAllEntries() throw()
  873. {
  874. ATLASSERT(m_bInitialized);
  875. m_syncObj.Lock();
  876. m_hashTable.DisableAutoRehash();
  877. POSITION pos = m_hashTable.GetStartPosition();
  878. POSITION oldpos;
  879. while (pos != NULL)
  880. {
  881. oldpos = pos;
  882. m_hashTable.GetNext(pos);
  883. RemoveAt(oldpos, TRUE);
  884. }
  885. m_hashTable.EnableAutoRehash();
  886. m_syncObj.Unlock();
  887. return S_OK;
  888. }
  889. protected:
  890. // Checks to see if the cache can accommodate any new entries within
  891. // its allocation and entry count limits.
  892. bool CanAddEntry(DWORD dwSizeToAdd) throw()
  893. {
  894. return CheckAlloc(dwSizeToAdd) && CheckEntryCount(1);
  895. }
  896. // Checks to see if the cache can accommodate dwSizeToAdd additional
  897. // allocation within its allocation limit.
  898. bool CheckAlloc(DWORD dwSizeToAdd) throw()
  899. {
  900. if (m_dwMaxAllocationSize == 0xFFFFFFFF)
  901. return true; //max allocation size setting hasn't been set
  902. DWORD dwNew = m_statObj.GetCurrentAllocSize() + dwSizeToAdd;
  903. return dwNew < m_dwMaxAllocationSize;
  904. }
  905. // Checks to see if the cache can accommodate dwNumEntriesToAdd
  906. // additional entries within its limits.
  907. bool CheckEntryCount(DWORD dwNumEntriesToAdd) throw()
  908. {
  909. if (m_dwMaxEntries == 0xFFFFFFFF)
  910. return true; //max entry size hasn't been set
  911. DWORD dwNew = m_statObj.GetCurrentEntryCount() + dwNumEntriesToAdd;
  912. return dwNew < m_dwMaxEntries;
  913. }
  914. protected:
  915. // Takes the element at pos in the hash table and removes it from
  916. // the cache. If there are no references, then the entry is
  917. // deleted, otherwise it is deleted by ReleaseEntry when the
  918. // refcount goes to zero.
  919. HRESULT RemoveAt(POSITION pos, BOOL bDelete) throw()
  920. {
  921. HRESULT hr = S_OK;
  922. ATLASSERT(pos != NULL);
  923. NodeType * pEntry = m_hashTable.GetValueAt(pos);
  924. m_flusher.Remove(pEntry);
  925. m_culler.Remove(pEntry);
  926. if (bDelete)
  927. m_hashTable.RemoveAtPos(pos);
  928. if ((long)pEntry->dwRef == 0)
  929. hr = InternalRemoveEntry(pEntry);
  930. else
  931. pEntry->pos = NULL;
  932. return S_OK;
  933. }
  934. // Does the actual destruction of the node. Deletes the
  935. // NodeType struct and calls the inherited class's
  936. // OnDestroyEntry function, where other necessary destruction
  937. // can take place. Also updates the cache statistics.
  938. // Inherited classes should call RemoveAt unless the element's
  939. // refcount is zero and it has been removed from the
  940. // culler and flusher lists.
  941. HRESULT InternalRemoveEntry(NodeType * pEntry) throw()
  942. {
  943. ATLASSERT(pEntry != NULL);
  944. T* pT = static_cast<T*>(this);
  945. ATLASSERT((long)pEntry->dwRef == 0);
  946. pT->OnDestroyEntry(pEntry);
  947. m_statObj.ReleaseElement(pEntry->dwSize);
  948. free(pEntry);
  949. return S_OK;
  950. }
  951. }; // CMemoryCacheBase
  952. class CCacheDataBase
  953. {
  954. };
  955. struct CCacheDataEx : public CCacheDataBase
  956. {
  957. HINSTANCE hInstance;
  958. IMemoryCacheClient * pClient;
  959. };
  960. template <typename DataType,
  961. class StatClass=CStdStatClass,
  962. class FlushClass=COldFlusher,
  963. class keyType=CFixedStringKey, class KeyTrait=CStringElementTraits<CFixedStringKey >,
  964. class SyncClass=CComCriticalSection,
  965. class CullClass=CExpireCuller >
  966. class CMemoryCache:
  967. public CMemoryCacheBase<CMemoryCache, DataType, CCacheDataEx,
  968. keyType, KeyTrait, FlushClass, CullClass, SyncClass, StatClass>
  969. {
  970. protected:
  971. CComPtr<IServiceProvider> m_spServiceProv;
  972. CComPtr<IDllCache> m_spDllCache;
  973. typedef CMemoryCacheBase<CMemoryCache, DataType, CCacheDataEx,
  974. keyType, KeyTrait, FlushClass, CullClass, SyncClass, StatClass> baseClass;
  975. public:
  976. HRESULT Initialize(IServiceProvider * pProvider)
  977. {
  978. baseClass::Initialize();
  979. m_spServiceProv = pProvider;
  980. if (pProvider)
  981. return m_spServiceProv->QueryService(__uuidof(IDllCache), __uuidof(IDllCache), (void**)&m_spDllCache);
  982. else
  983. return S_OK;
  984. }
  985. HRESULT AddEntry(
  986. const keyType &Key,
  987. const DataType &data,
  988. DWORD dwSize,
  989. FILETIME * pftExpireTime = NULL,
  990. HINSTANCE hInstance = NULL,
  991. IMemoryCacheClient * pClient = NULL,
  992. HCACHEITEM *phEntry = NULL
  993. ) throw()
  994. {
  995. _ATLTRY
  996. {
  997. HRESULT hr;
  998. NodeType * pEntry = NULL;
  999. hr = baseClass::AddEntry(Key, data, dwSize, (HCACHEITEM *)&pEntry);
  1000. if (hr != S_OK)
  1001. return hr;
  1002. pEntry->hInstance = hInstance;
  1003. pEntry->pClient = pClient;
  1004. if (pftExpireTime)
  1005. pEntry->cftExpireTime = *pftExpireTime;
  1006. if (hInstance && m_spDllCache)
  1007. m_spDllCache->AddRefModule(hInstance);
  1008. baseClass::CommitEntry(static_cast<HCACHEITEM>(pEntry));
  1009. if (phEntry)
  1010. *phEntry = static_cast<HCACHEITEM>(pEntry);
  1011. else
  1012. baseClass::ReleaseEntry(static_cast<HCACHEITEM>(pEntry));
  1013. return S_OK;
  1014. }
  1015. _ATLCATCHALL()
  1016. {
  1017. return E_FAIL;
  1018. }
  1019. }
  1020. virtual void OnDestroyEntry(const NodeType * pEntry) throw()
  1021. {
  1022. ATLASSERT(pEntry);
  1023. if (!pEntry)
  1024. return;
  1025. if (pEntry->pClient)
  1026. pEntry->pClient->Free((void *)&pEntry->Data);
  1027. if (pEntry->hInstance && m_spDllCache)
  1028. m_spDllCache->ReleaseModule(pEntry->hInstance);
  1029. }
  1030. }; // CMemoryCache
  1031. // CStdStatData - contains the data that CStdStatClass keeps track of
  1032. #define ATL_PERF_CACHE_OBJECT 100
  1033. struct CPerfStatObject : public CPerfObject
  1034. {
  1035. DECLARE_PERF_OBJECT(CPerfStatObject, ATL_PERF_CACHE_OBJECT, IDS_PERFMON_CACHE, IDS_PERFMON_CACHE_HELP, -1);
  1036. BEGIN_COUNTER_MAP(CPerfStatObject)
  1037. DEFINE_COUNTER(m_nHitCount, IDS_PERFMON_HITCOUNT, IDS_PERFMON_HITCOUNT_HELP, PERF_COUNTER_RAWCOUNT, -1)
  1038. DEFINE_COUNTER(m_nMissCount, IDS_PERFMON_MISSCOUNT, IDS_PERFMON_MISSCOUNT_HELP, PERF_COUNTER_RAWCOUNT, -1)
  1039. DEFINE_COUNTER(m_nCurrentAllocations, IDS_PERFMON_CURRENTALLOCATIONS, IDS_PERFMON_CURRENTALLOCATIONS_HELP, PERF_COUNTER_RAWCOUNT, -3)
  1040. DEFINE_COUNTER(m_nMaxAllocations, IDS_PERFMON_MAXALLOCATIONS, IDS_PERFMON_MAXALLOCATIONS_HELP, PERF_COUNTER_RAWCOUNT, -3)
  1041. DEFINE_COUNTER(m_nCurrentEntries, IDS_PERFMON_CURRENTENTRIES, IDS_PERFMON_CURRENTENTRIES_HELP, PERF_COUNTER_RAWCOUNT, -1)
  1042. DEFINE_COUNTER(m_nMaxEntries, IDS_PERFMON_MAXENTRIES, IDS_PERFMON_MAXENTRIES_HELP, PERF_COUNTER_RAWCOUNT, -1)
  1043. END_COUNTER_MAP()
  1044. DWORD m_nHitCount;
  1045. DWORD m_nMissCount;
  1046. DWORD m_nCurrentAllocations;
  1047. DWORD m_nMaxAllocations;
  1048. DWORD m_nCurrentEntries;
  1049. DWORD m_nMaxEntries;
  1050. };
  1051. // CCachePerfMon - the interface to CPerfMon, with associated definitions
  1052. class CCachePerfMon : public CPerfMon
  1053. {
  1054. public:
  1055. BEGIN_PERF_MAP(_T("ATL Server:Cache"))
  1056. CHAIN_PERF_OBJECT(CPerfStatObject)
  1057. END_PERF_MAP()
  1058. };
  1059. //
  1060. //CStdStatClass
  1061. // Description
  1062. // This class provides the implementation of a standard cache statistics accounting class
  1063. class CStdStatClass
  1064. {
  1065. protected:
  1066. CPerfStatObject* m_pStats;
  1067. CPerfStatObject m_stats;
  1068. public:
  1069. CStdStatClass() throw()
  1070. {
  1071. m_pStats = &m_stats;
  1072. }
  1073. // This function is not thread safe by design
  1074. HRESULT Initialize(CPerfStatObject* pStats = NULL) throw()
  1075. {
  1076. if (pStats)
  1077. m_pStats = pStats;
  1078. else
  1079. m_pStats = &m_stats;
  1080. ResetCounters();
  1081. return S_OK;
  1082. }
  1083. // This function is not thread safe by design
  1084. HRESULT Uninitialize() throw()
  1085. {
  1086. m_pStats = &m_stats;
  1087. return S_OK;
  1088. }
  1089. void Hit() throw()
  1090. {
  1091. InterlockedIncrement(reinterpret_cast<long*>(&m_pStats->m_nHitCount));
  1092. }
  1093. void Miss() throw()
  1094. {
  1095. InterlockedIncrement(reinterpret_cast<long*>(&m_pStats->m_nMissCount));
  1096. }
  1097. void AddElement(DWORD dwBytes) throw()
  1098. {
  1099. InterlockedIncrement(reinterpret_cast<long*>(&m_pStats->m_nCurrentEntries));
  1100. InterlockedExchangeAdd(reinterpret_cast<long*>(&m_pStats->m_nCurrentAllocations), dwBytes);
  1101. if (m_pStats->m_nCurrentEntries > m_pStats->m_nMaxEntries)
  1102. InterlockedExchange(reinterpret_cast<long*>(&m_pStats->m_nMaxEntries), m_pStats->m_nCurrentEntries);
  1103. if (m_pStats->m_nCurrentAllocations > m_pStats->m_nMaxAllocations)
  1104. InterlockedExchange(reinterpret_cast<long*>(&m_pStats->m_nMaxAllocations), m_pStats->m_nCurrentAllocations);
  1105. }
  1106. void ReleaseElement(DWORD dwBytes) throw()
  1107. {
  1108. InterlockedDecrement(reinterpret_cast<long*>(&m_pStats->m_nCurrentEntries));
  1109. InterlockedExchangeAdd(reinterpret_cast<long*>(&m_pStats->m_nCurrentAllocations), -((long)dwBytes));
  1110. }
  1111. DWORD GetHitCount() throw()
  1112. {
  1113. return m_pStats->m_nHitCount;
  1114. }
  1115. DWORD GetMissCount() throw()
  1116. {
  1117. return m_pStats->m_nMissCount;
  1118. }
  1119. DWORD GetCurrentAllocSize() throw()
  1120. {
  1121. return m_pStats->m_nCurrentAllocations;
  1122. }
  1123. DWORD GetMaxAllocSize() throw()
  1124. {
  1125. return m_pStats->m_nMaxAllocations;
  1126. }
  1127. DWORD GetCurrentEntryCount() throw()
  1128. {
  1129. return m_pStats->m_nCurrentEntries;
  1130. }
  1131. DWORD GetMaxEntryCount() throw()
  1132. {
  1133. return m_pStats->m_nMaxEntries;
  1134. }
  1135. void ResetCounters() throw()
  1136. {
  1137. m_pStats->m_nHitCount = 0;
  1138. m_pStats->m_nMissCount = 0;
  1139. m_pStats->m_nCurrentAllocations = 0;
  1140. m_pStats->m_nMaxAllocations = 0;
  1141. m_pStats->m_nCurrentEntries = 0;
  1142. m_pStats->m_nMaxEntries = 0;
  1143. }
  1144. }; // CStdStatClass
  1145. //
  1146. // CNoStatClass
  1147. // This is a noop stat class
  1148. class CNoStatClass
  1149. {
  1150. public:
  1151. HRESULT Initialize() throw(){ return S_OK; }
  1152. HRESULT Uninitialize() throw(){ return S_OK; }
  1153. void Hit() throw(){ }
  1154. void Miss() throw(){ }
  1155. void AddElement(DWORD) throw(){ }
  1156. void ReleaseElement(DWORD) throw(){ }
  1157. DWORD GetHitCount() throw(){ return 0; }
  1158. DWORD GetMissCount() throw(){ return 0; }
  1159. DWORD GetCurrentAllocSize() throw(){ return 0; }
  1160. DWORD GetMaxAllocSize() throw(){ return 0; }
  1161. DWORD GetCurrentEntryCount() throw(){ return 0; }
  1162. DWORD GetMaxEntryCount() throw(){ return 0; }
  1163. void ResetCounters() throw(){ }
  1164. }; // CNoStatClass
  1165. //
  1166. //CPerfStatClass
  1167. // Description
  1168. // This class provides the implementation of a cache statistics gathering class
  1169. // with PerfMon support
  1170. class CPerfStatClass : public CStdStatClass
  1171. {
  1172. CPerfStatObject * m_pPerfObject;
  1173. CCachePerfMon m_PerfMon;
  1174. public:
  1175. HRESULT Initialize(LPWSTR szName=NULL) throw()
  1176. {
  1177. HRESULT hr;
  1178. if (!szName)
  1179. szName = L"Object 1";
  1180. m_pPerfObject = NULL;
  1181. ATLTRACE(atlTraceCache, 2, _T("Initializing m_PerfMon\n"));
  1182. hr = m_PerfMon.Initialize();
  1183. if (SUCCEEDED(hr))
  1184. {
  1185. CPerfLock lock(&m_PerfMon);
  1186. if (FAILED(hr = lock.GetStatus()))
  1187. {
  1188. return hr;
  1189. }
  1190. hr = m_PerfMon.CreateInstance(ATL_PERF_CACHE_OBJECT, 0, szName, reinterpret_cast<CPerfObject**>(&m_pPerfObject));
  1191. if (FAILED(hr))
  1192. {
  1193. return hr;
  1194. }
  1195. CStdStatClass::Initialize(m_pPerfObject);
  1196. }
  1197. else
  1198. ATLASSERT(m_pPerfObject == NULL);
  1199. return hr;
  1200. }
  1201. HRESULT Uninitialize() throw()
  1202. {
  1203. CStdStatClass::Uninitialize();
  1204. if (m_pPerfObject != NULL) // Initialized m_pPerfObject successfully above
  1205. {
  1206. HRESULT hr = m_PerfMon.ReleaseInstance(m_pPerfObject);
  1207. if (hr != S_OK)
  1208. return hr;
  1209. m_PerfMon.UnInitialize();
  1210. }
  1211. return S_OK;
  1212. }
  1213. }; // CPerfStatClass
  1214. #ifndef ATL_BLOB_CACHE_TIMEOUT
  1215. #define ATL_BLOB_CACHE_TIMEOUT 1000
  1216. #endif
  1217. //
  1218. //CBlobCache
  1219. // Description:
  1220. // Implements a cache that stores pointers to void. Uses the generic CMemoryCacheBase class
  1221. // as the implementation.
  1222. template <class MonitorClass,
  1223. class StatClass=CStdStatClass,
  1224. class SyncObj=CComCriticalSection,
  1225. class FlushClass=COldFlusher,
  1226. class CullClass=CExpireCuller >
  1227. class CBlobCache : public CMemoryCache<void*, StatClass, FlushClass, CFixedStringKey,
  1228. CStringElementTraits<CFixedStringKey >, SyncObj, CullClass>,
  1229. public IMemoryCache,
  1230. public IMemoryCacheControl,
  1231. public IMemoryCacheStats,
  1232. public IWorkerThreadClient
  1233. {
  1234. typedef CMemoryCache<void*, StatClass, FlushClass, CFixedStringKey,
  1235. CStringElementTraits<CFixedStringKey>, SyncObj, CullClass> cacheBase;
  1236. MonitorClass m_Monitor;
  1237. protected:
  1238. HANDLE m_hTimer;
  1239. public:
  1240. CBlobCache() : m_hTimer(NULL)
  1241. {
  1242. }
  1243. HRESULT Initialize(IServiceProvider *pProv) throw()
  1244. {
  1245. HRESULT hr = cacheBase::Initialize(pProv);
  1246. if (FAILED(hr))
  1247. return hr;
  1248. hr = m_Monitor.Initialize();
  1249. if (FAILED(hr))
  1250. return hr;
  1251. return m_Monitor.AddTimer(ATL_BLOB_CACHE_TIMEOUT,
  1252. static_cast<IWorkerThreadClient*>(this), (DWORD_PTR) this, &m_hTimer);
  1253. }
  1254. template <class ThreadTraits>
  1255. HRESULT Initialize(IServiceProvider *pProv, CWorkerThread<ThreadTraits> *pWorkerThread) throw()
  1256. {
  1257. ATLASSERT(pWorkerThread);
  1258. HRESULT hr = S_OK;
  1259. if (S_OK == cacheBase::Initialize(pProv))
  1260. {
  1261. hr = m_Monitor.Initialize(pWorkerThread);
  1262. if (FAILED(hr))
  1263. return hr;
  1264. return m_Monitor.AddTimer(ATL_BLOB_CACHE_TIMEOUT,
  1265. static_cast<IWorkerThreadClient*>(this), (DWORD_PTR) this, &m_hTimer);
  1266. }
  1267. return S_OK;
  1268. }
  1269. HRESULT Execute(DWORD_PTR dwParam, HANDLE /*hObject*/) throw()
  1270. {
  1271. CBlobCache* pCache = (CBlobCache*)dwParam;
  1272. if (pCache)
  1273. pCache->Flush();
  1274. return S_OK;
  1275. }
  1276. HRESULT CloseHandle(HANDLE hObject) throw()
  1277. {
  1278. ATLASSERT(m_hTimer == hObject);
  1279. m_hTimer = NULL;
  1280. ::CloseHandle(hObject);
  1281. return S_OK;
  1282. }
  1283. ~CBlobCache() throw()
  1284. {
  1285. if (m_hTimer)
  1286. m_Monitor.RemoveHandle(m_hTimer);
  1287. }
  1288. HRESULT Uninitialize() throw()
  1289. {
  1290. if (m_hTimer)
  1291. {
  1292. m_Monitor.RemoveHandle(m_hTimer);
  1293. m_hTimer = NULL;
  1294. }
  1295. m_Monitor.Shutdown();
  1296. return cacheBase::Uninitialize();
  1297. }
  1298. // IUnknown methods
  1299. HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, void **ppv) throw()
  1300. {
  1301. HRESULT hr = E_NOINTERFACE;
  1302. if (!ppv)
  1303. hr = E_POINTER;
  1304. else
  1305. {
  1306. if (InlineIsEqualGUID(riid, __uuidof(IUnknown)) ||
  1307. InlineIsEqualGUID(riid, __uuidof(IMemoryCache)))
  1308. {
  1309. *ppv = (IUnknown *) (IMemoryCache *) this;
  1310. AddRef();
  1311. hr = S_OK;
  1312. }
  1313. if (InlineIsEqualGUID(riid, __uuidof(IMemoryCacheStats)))
  1314. {
  1315. *ppv = (IUnknown *) (IMemoryCacheStats*)this;
  1316. AddRef();
  1317. hr = S_OK;
  1318. }
  1319. if (InlineIsEqualGUID(riid, __uuidof(IMemoryCacheControl)))
  1320. {
  1321. *ppv = (IUnknown *) (IMemoryCacheControl*)this;
  1322. AddRef();
  1323. hr = S_OK;
  1324. }
  1325. }
  1326. return hr;
  1327. }
  1328. ULONG STDMETHODCALLTYPE AddRef() throw()
  1329. {
  1330. return 1;
  1331. }
  1332. ULONG STDMETHODCALLTYPE Release() throw()
  1333. {
  1334. return 1;
  1335. }
  1336. // IMemoryCache Methods
  1337. HRESULT STDMETHODCALLTYPE Add(LPCSTR szKey, void *pvData, DWORD dwSize,
  1338. FILETIME *pftExpireTime,
  1339. HINSTANCE hInstClient,
  1340. HCACHEITEM *phEntry,
  1341. IMemoryCacheClient *pClient) throw()
  1342. {
  1343. HRESULT hr = E_FAIL;
  1344. //if it's a multithreaded cache monitor we'll let the monitor take care of
  1345. //cleaning up the cache so we don't overflow our configuration settings.
  1346. //if it's not a threaded cache monitor, we need to make sure we don't
  1347. //overflow the configuration settings by adding a new element
  1348. if (m_Monitor.GetThreadHandle()==NULL)
  1349. {
  1350. if (!cacheBase::CanAddEntry(dwSize))
  1351. {
  1352. //flush the entries and check again to see if we can add
  1353. cacheBase::FlushEntries();
  1354. if (!cacheBase::CanAddEntry(dwSize))
  1355. return E_OUTOFMEMORY;
  1356. }
  1357. }
  1358. _ATLTRY
  1359. {
  1360. hr = cacheBase::AddEntry(szKey, pvData, dwSize,
  1361. pftExpireTime, hInstClient, pClient, phEntry);
  1362. return hr;
  1363. }
  1364. _ATLCATCHALL()
  1365. {
  1366. return E_FAIL;
  1367. }
  1368. }
  1369. HRESULT STDMETHODCALLTYPE LookupEntry(LPCSTR szKey, HCACHEITEM * phEntry) throw()
  1370. {
  1371. return cacheBase::LookupEntry(szKey, phEntry);
  1372. }
  1373. HRESULT STDMETHODCALLTYPE GetData(const HCACHEITEM hKey, void **ppvData, DWORD *pdwSize) const throw()
  1374. {
  1375. return cacheBase::GetEntryData(hKey, ppvData, pdwSize);
  1376. }
  1377. HRESULT STDMETHODCALLTYPE ReleaseEntry(const HCACHEITEM hKey) throw()
  1378. {
  1379. return cacheBase::ReleaseEntry(hKey);
  1380. }
  1381. HRESULT STDMETHODCALLTYPE RemoveEntry(const HCACHEITEM hKey) throw()
  1382. {
  1383. return cacheBase::RemoveEntry(hKey);
  1384. }
  1385. HRESULT STDMETHODCALLTYPE RemoveEntryByKey(LPCSTR szKey) throw()
  1386. {
  1387. return cacheBase::RemoveEntryByKey(szKey);
  1388. }
  1389. HRESULT STDMETHODCALLTYPE Flush() throw()
  1390. {
  1391. return cacheBase::FlushEntries();
  1392. }
  1393. HRESULT STDMETHODCALLTYPE SetMaxAllowedSize(DWORD dwSize) throw()
  1394. {
  1395. return cacheBase::SetMaxAllowedSize(dwSize);
  1396. }
  1397. HRESULT STDMETHODCALLTYPE GetMaxAllowedSize(DWORD *pdwSize) throw()
  1398. {
  1399. return cacheBase::GetMaxAllowedSize(pdwSize);
  1400. }
  1401. HRESULT STDMETHODCALLTYPE SetMaxAllowedEntries(DWORD dwSize) throw()
  1402. {
  1403. return cacheBase::SetMaxAllowedEntries(dwSize);
  1404. }
  1405. HRESULT STDMETHODCALLTYPE GetMaxAllowedEntries(DWORD *pdwSize) throw()
  1406. {
  1407. return cacheBase::GetMaxAllowedEntries(pdwSize);
  1408. }
  1409. HRESULT STDMETHODCALLTYPE ResetCache() throw()
  1410. {
  1411. return cacheBase::ResetCache();
  1412. }
  1413. // IMemoryCacheStats methods
  1414. HRESULT STDMETHODCALLTYPE ClearStats() throw()
  1415. {
  1416. m_statObj.ResetCounters();
  1417. return S_OK;
  1418. }
  1419. HRESULT STDMETHODCALLTYPE GetHitCount(DWORD *pdwSize) throw()
  1420. {
  1421. if (!pdwSize)
  1422. return E_POINTER;
  1423. *pdwSize = m_statObj.GetHitCount();
  1424. return S_OK;
  1425. }
  1426. HRESULT STDMETHODCALLTYPE GetMissCount(DWORD *pdwSize) throw()
  1427. {
  1428. if (!pdwSize)
  1429. return E_POINTER;
  1430. *pdwSize = m_statObj.GetMissCount();
  1431. return S_OK;
  1432. }
  1433. HRESULT STDMETHODCALLTYPE GetMaxAllocSize(DWORD *pdwSize) throw()
  1434. {
  1435. if (!pdwSize)
  1436. return E_POINTER;
  1437. *pdwSize = m_statObj.GetMaxAllocSize();
  1438. return S_OK;
  1439. }
  1440. HRESULT STDMETHODCALLTYPE GetCurrentAllocSize(DWORD *pdwSize) throw()
  1441. {
  1442. if (!pdwSize)
  1443. return E_POINTER;
  1444. *pdwSize = m_statObj.GetCurrentAllocSize();
  1445. return S_OK;
  1446. }
  1447. HRESULT STDMETHODCALLTYPE GetMaxEntryCount(DWORD *pdwSize) throw()
  1448. {
  1449. if (!pdwSize)
  1450. return E_POINTER;
  1451. *pdwSize = m_statObj.GetMaxEntryCount();
  1452. return S_OK;
  1453. }
  1454. HRESULT STDMETHODCALLTYPE GetCurrentEntryCount(DWORD *pdwSize) throw()
  1455. {
  1456. if (!pdwSize)
  1457. return E_POINTER;
  1458. *pdwSize = m_statObj.GetCurrentEntryCount();
  1459. return S_OK;
  1460. }
  1461. }; // CBlobCache
  1462. //
  1463. // CDllCache
  1464. // This class manages a cache to handle calls to LoadLibrary
  1465. // and FreeLibrary.
  1466. // It keeps dlls loaded even after the last call to free library
  1467. // a worker thread then calls FreeLibrary on unused dlls
  1468. //
  1469. #ifndef ATL_DLL_CACHE_TIMEOUT
  1470. #ifdef _DEBUG
  1471. #define ATL_DLL_CACHE_TIMEOUT 1000 // 1 sec default for debug builds
  1472. #else
  1473. #define ATL_DLL_CACHE_TIMEOUT 10*60000 // 10 minute default for retail builds
  1474. #endif
  1475. #endif
  1476. class CNoDllCachePeer
  1477. {
  1478. public:
  1479. struct DllInfo
  1480. {
  1481. };
  1482. BOOL Add(HINSTANCE /*hInst*/, DllInfo * /*pInfo*/)
  1483. {
  1484. return TRUE;
  1485. }
  1486. void Remove(HINSTANCE /*hInst*/, DllInfo * /*pInfo*/)
  1487. {
  1488. }
  1489. };
  1490. // CDllCache
  1491. // Implements IDllCache, an interface that is used to load and unload Dlls.
  1492. // To use it, construct an instance of a CDllCache and call Initialize.
  1493. // The Initialize call has to match with the type of monitor class you
  1494. // templatize on. The monitor thread will call IWorkerThreadClient::Execute
  1495. // after its timeout expires. Make sure to Uninitialize the object before
  1496. // it is destroyed by calling Uninitialize
  1497. //
  1498. template <class CMonitorClass, class Peer=CNoDllCachePeer>
  1499. class CDllCache : public IDllCache,
  1500. public IWorkerThreadClient
  1501. {
  1502. protected:
  1503. CComCriticalSection m_critSec;
  1504. CSimpleArray<DLL_CACHE_ENTRY> m_Dlls;
  1505. CSimpleArray<Peer::DllInfo> m_DllInfos;
  1506. CMonitorClass m_Monitor;
  1507. HANDLE m_hTimer;
  1508. void RemoveDllEntry(DLL_CACHE_ENTRY& entry)
  1509. {
  1510. ::FreeLibrary(entry.hInstDll);
  1511. entry.hInstDll = NULL;
  1512. m_Dlls.RemoveAt(m_Dlls.GetSize()-1);
  1513. }
  1514. public:
  1515. Peer m_Peer;
  1516. CDllCache() :
  1517. m_hTimer(INVALID_HANDLE_VALUE)
  1518. {
  1519. }
  1520. HRESULT Initialize(DWORD dwTimeout=ATL_DLL_CACHE_TIMEOUT) throw()
  1521. {
  1522. HRESULT hr = m_critSec.Init();
  1523. if (FAILED(hr))
  1524. return hr;
  1525. hr = m_Monitor.Initialize();
  1526. if (FAILED(hr))
  1527. return hr;
  1528. return m_Monitor.AddTimer(dwTimeout, this, 0, &m_hTimer);
  1529. }
  1530. template <class ThreadTraits>
  1531. HRESULT Initialize(CWorkerThread<ThreadTraits> *pWorkerThread,
  1532. DWORD dwTimeout=ATL_DLL_CACHE_TIMEOUT) throw()
  1533. {
  1534. HRESULT hr = m_critSec.Init();
  1535. if (FAILED(hr))
  1536. return hr;
  1537. hr = m_Monitor.Initialize(pWorkerThread);
  1538. if (FAILED(hr))
  1539. return hr;
  1540. return m_Monitor.AddTimer(dwTimeout, this, 0, &m_hTimer);
  1541. }
  1542. HRESULT Uninitialize() throw()
  1543. {
  1544. HRESULT hr = S_OK;
  1545. if (m_hTimer)
  1546. {
  1547. m_Monitor.RemoveHandle(m_hTimer);
  1548. m_hTimer = NULL;
  1549. }
  1550. m_Monitor.Shutdown();
  1551. // free all the libraries we've cached
  1552. int nLen = m_Dlls.GetSize();
  1553. for (int i=0; i<nLen; i++)
  1554. {
  1555. DLL_CACHE_ENTRY& entry = m_Dlls[i];
  1556. ATLASSERT(entry.dwRefs == 0);
  1557. BOOL bRet = ::FreeLibrary(entry.hInstDll);
  1558. if (!bRet)
  1559. {
  1560. hr = AtlHresultFromLastError();
  1561. ATLTRACE(atlTraceCache, 0, _T("Free library failed on shutdown of dll cache : hr = 0x%08x)"), hr);
  1562. }
  1563. nLen--;
  1564. }
  1565. m_Dlls.RemoveAll();
  1566. m_critSec.Term();
  1567. return hr;
  1568. }
  1569. // IUnknown methods
  1570. HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, void **ppv) throw()
  1571. {
  1572. if (!ppv)
  1573. return E_POINTER;
  1574. if (InlineIsEqualGUID(riid, __uuidof(IUnknown)) ||
  1575. InlineIsEqualGUID(riid, __uuidof(IDllCache)))
  1576. {
  1577. *ppv = (IUnknown *) this;
  1578. AddRef();
  1579. return S_OK;
  1580. }
  1581. return E_NOINTERFACE;
  1582. }
  1583. ULONG STDMETHODCALLTYPE AddRef() throw()
  1584. {
  1585. return 1;
  1586. }
  1587. ULONG STDMETHODCALLTYPE Release() throw()
  1588. {
  1589. return 1;
  1590. }
  1591. // IDllCache methods
  1592. HINSTANCE Load(LPCSTR szDllName, void *pPeerInfo) throw(...)
  1593. {
  1594. m_critSec.Lock();
  1595. int nLen = m_Dlls.GetSize();
  1596. for (int i=0; i<nLen; i++)
  1597. {
  1598. DLL_CACHE_ENTRY& entry = m_Dlls[i];
  1599. if (!_stricmp(entry.szDllName, szDllName))
  1600. {
  1601. entry.dwRefs++;
  1602. m_critSec.Unlock();
  1603. if (pPeerInfo)
  1604. {
  1605. Peer::DllInfo *pl = (Peer::DllInfo*)pPeerInfo;
  1606. *pl = m_DllInfos[i];
  1607. }
  1608. return entry.hInstDll;
  1609. }
  1610. }
  1611. DLL_CACHE_ENTRY entry;
  1612. entry.hInstDll = ::LoadLibraryA(szDllName);
  1613. if (!entry.hInstDll)
  1614. {
  1615. m_critSec.Unlock();
  1616. return NULL;
  1617. }
  1618. strcpy(entry.szDllName, szDllName);
  1619. entry.dwRefs = 1;
  1620. entry.bAlive = TRUE;
  1621. m_Dlls.Add(entry);
  1622. Peer::DllInfo *pdllInfo = (Peer::DllInfo*)pPeerInfo;
  1623. // m_Peer could throw an exception from user code. We
  1624. // pass that exception from here to a higher context (we
  1625. // won't deal with user exception here).
  1626. if (!m_Peer.Add(entry.hInstDll, pdllInfo))
  1627. {
  1628. RemoveDllEntry(entry);
  1629. }
  1630. if ((entry.hInstDll != NULL) && (!m_DllInfos.Add(*pdllInfo)))
  1631. {
  1632. RemoveDllEntry(entry);
  1633. }
  1634. m_critSec.Unlock();
  1635. return entry.hInstDll;
  1636. }
  1637. BOOL Free(HINSTANCE hInstDll) throw()
  1638. {
  1639. m_critSec.Lock();
  1640. int nLen = m_Dlls.GetSize();
  1641. for (int i=0; i<nLen; i++)
  1642. {
  1643. DLL_CACHE_ENTRY &entry = m_Dlls[i];
  1644. if (entry.hInstDll == hInstDll)
  1645. {
  1646. ATLASSERT(entry.dwRefs > 0);
  1647. entry.bAlive = TRUE;
  1648. entry.dwRefs--;
  1649. m_critSec.Unlock();
  1650. return TRUE;
  1651. }
  1652. }
  1653. m_critSec.Unlock();
  1654. // the dll wasn't found
  1655. // in the cache, so just
  1656. // pass along to ::FreeLibrary
  1657. return ::FreeLibrary(hInstDll);
  1658. }
  1659. BOOL AddRefModule(HINSTANCE hInstDll) throw()
  1660. {
  1661. m_critSec.Lock();
  1662. int nLen = m_Dlls.GetSize();
  1663. for (int i=0; i<nLen; i++)
  1664. {
  1665. DLL_CACHE_ENTRY &entry = m_Dlls[i];
  1666. if (entry.hInstDll == hInstDll)
  1667. {
  1668. ATLASSERT(entry.dwRefs > 0);
  1669. entry.dwRefs++;
  1670. m_critSec.Unlock();
  1671. return TRUE;
  1672. }
  1673. }
  1674. m_critSec.Unlock();
  1675. return FALSE;
  1676. }
  1677. BOOL ReleaseModule(HINSTANCE hInstDll) throw()
  1678. {
  1679. m_critSec.Lock();
  1680. int nLen = m_Dlls.GetSize();
  1681. for (int i=0; i<nLen; i++)
  1682. {
  1683. DLL_CACHE_ENTRY &entry = m_Dlls[i];
  1684. if (entry.hInstDll == hInstDll)
  1685. {
  1686. ATLASSERT(entry.dwRefs > 0);
  1687. entry.bAlive = TRUE;
  1688. entry.dwRefs--;
  1689. m_critSec.Unlock();
  1690. return TRUE;
  1691. }
  1692. }
  1693. m_critSec.Unlock();
  1694. return FALSE;
  1695. }
  1696. HRESULT GetEntries(DWORD dwCount, DLL_CACHE_ENTRY *pEntries, DWORD *pdwCopied) throw()
  1697. {
  1698. if (!pdwCopied)
  1699. return E_POINTER;
  1700. m_critSec.Lock();
  1701. if (dwCount==0 || pEntries==NULL)
  1702. {
  1703. // just return the required size
  1704. *pdwCopied = m_Dlls.GetSize();
  1705. m_critSec.Unlock();
  1706. return S_OK;
  1707. }
  1708. if (dwCount > (DWORD) m_Dlls.GetSize())
  1709. dwCount = m_Dlls.GetSize();
  1710. memcpy(pEntries, m_Dlls.GetData(), dwCount*sizeof(DLL_CACHE_ENTRY));
  1711. *pdwCopied = dwCount;
  1712. m_critSec.Unlock();
  1713. return S_OK;
  1714. }
  1715. HRESULT Flush() throw()
  1716. {
  1717. m_critSec.Lock();
  1718. int nLen = m_Dlls.GetSize();
  1719. for (int i=0; i<nLen; i++)
  1720. {
  1721. DLL_CACHE_ENTRY &entry = m_Dlls[i];
  1722. if (entry.dwRefs == 0 && !entry.bAlive)
  1723. {
  1724. _ATLTRY
  1725. {
  1726. m_Peer.Remove(entry.hInstDll, &m_DllInfos[i]);
  1727. }
  1728. _ATLCATCHALL()
  1729. {
  1730. ATLTRACE(atlTraceCache, 2, _T("Exception thrown from user code in CDllCache::Flush\n"));
  1731. }
  1732. ::FreeLibrary(entry.hInstDll);
  1733. m_Dlls.RemoveAt(i);
  1734. m_DllInfos.RemoveAt(i);
  1735. i--;
  1736. nLen--;
  1737. }
  1738. entry.bAlive = FALSE;
  1739. }
  1740. m_critSec.Unlock();
  1741. return S_OK;
  1742. }
  1743. HRESULT Execute(DWORD_PTR /*dwParam*/, HANDLE /*hObject*/) throw()
  1744. {
  1745. Flush();
  1746. return S_OK;
  1747. }
  1748. HRESULT CloseHandle(HANDLE hObject) throw()
  1749. {
  1750. ATLASSERT(m_hTimer == hObject);
  1751. m_hTimer = NULL;
  1752. ::CloseHandle(hObject);
  1753. return S_OK;
  1754. }
  1755. }; // CDllCache
  1756. //
  1757. //IStencilCache
  1758. //IStencilCache is used by a stencil processor to cache pointers to CStencil
  1759. //derived objects.
  1760. // {8702269B-707D-49cc-AEF8-5FFCB3D6891B}
  1761. extern "C" __declspec(selectany) IID IID_IStencilCache = { 0x8702269b, 0x707d, 0x49cc, { 0xae, 0xf8, 0x5f, 0xfc, 0xb3, 0xd6, 0x89, 0x1b } };
  1762. __interface ATL_NO_VTABLE __declspec(uuid("8702269B-707D-49cc-AEF8-5FFCB3D6891B"))
  1763. IStencilCache : public IUnknown
  1764. {
  1765. // IStencilCache methods
  1766. STDMETHOD(CacheStencil)(LPCSTR szName, //a name for this cache entry
  1767. void *pStencil, //a pointer to a CStencil derived object
  1768. DWORD dwSize, //sizeof pStencil
  1769. HCACHEITEM *pHandle, //out pointer to a handle to the this cache entry
  1770. HINSTANCE hInst, //HINSTANCE of the module putting this entry
  1771. //in the cache.
  1772. IMemoryCacheClient *pClient //Interface used to free this instance
  1773. );
  1774. STDMETHOD(LookupStencil)(LPCSTR szName, HCACHEITEM * phStencil);
  1775. STDMETHOD(GetStencil)(const HCACHEITEM hStencil, void ** ppStencil) const;
  1776. STDMETHOD(AddRefStencil)(const HCACHEITEM hStencil);
  1777. STDMETHOD(ReleaseStencil)(const HCACHEITEM hStencil);
  1778. };
  1779. // {55DEF119-D7A7-4eb7-A876-33365E1C5E1A}
  1780. extern "C" __declspec(selectany) IID IID_IStencilCacheControl = { 0x55def119, 0xd7a7, 0x4eb7, { 0xa8, 0x76, 0x33, 0x36, 0x5e, 0x1c, 0x5e, 0x1a } };
  1781. __interface ATL_NO_VTABLE __declspec(uuid("55DEF119-D7A7-4eb7-A876-33365E1C5E1A"))
  1782. IStencilCacheControl : public IUnknown
  1783. {
  1784. //IStencilCacheControl
  1785. STDMETHOD(RemoveStencil)(const HCACHEITEM hStencil); // Removes the stencil if there are no references,
  1786. // otherwise detaches it
  1787. STDMETHOD(RemoveStencilByName)(LPCSTR szStencil); //removes a stencil if there are no
  1788. //references to it
  1789. STDMETHOD(RemoveAllStencils)(); //removes all stencils that don't have references on them
  1790. STDMETHOD(SetDefaultLifespan)(unsigned __int64 dwdwLifespan); //sets the lifespan for all stencils
  1791. //in the cache (in 100 nanosecond units (10,000,000=1 second)).
  1792. STDMETHOD(GetDefaultLifespan)(unsigned __int64 *pdwdwLifespan);
  1793. };
  1794. #ifndef ATL_STENCIL_CACHE_TIMEOUT
  1795. #define ATL_STENCIL_CACHE_TIMEOUT 1000
  1796. #endif
  1797. #ifndef ATL_STENCIL_LIFESPAN
  1798. #ifdef _DEBUG
  1799. #define ATL_STENCIL_LIFESPAN CFileTime::Second
  1800. #else
  1801. #define ATL_STENCIL_LIFESPAN CFileTime::Hour
  1802. #endif
  1803. #endif
  1804. // timeout before we check if the file
  1805. // has changed in m.s.
  1806. #ifndef ATL_STENCIL_CHECK_TIMEOUT
  1807. #define ATL_STENCIL_CHECK_TIMEOUT 1000
  1808. #endif
  1809. template <class MonitorClass,
  1810. class StatClass=CStdStatClass,
  1811. class SyncClass=CComCriticalSection,
  1812. class FlushClass=COldFlusher,
  1813. class CullClass=CLifetimeCuller >
  1814. class CStencilCache :
  1815. public CMemoryCacheBase<CStencilCache, void *, CCacheDataEx,
  1816. CFixedStringKey, CStringElementTraitsI<CFixedStringKey >,
  1817. FlushClass, CullClass, SyncClass, StatClass>,
  1818. public IStencilCache,
  1819. public IStencilCacheControl,
  1820. public IWorkerThreadClient,
  1821. public IMemoryCacheStats,
  1822. public CComObjectRootEx<CComGlobalsThreadModel>
  1823. {
  1824. protected:
  1825. typedef CMemoryCacheBase<CStencilCache, void *, CCacheDataEx,
  1826. CFixedStringKey, CStringElementTraitsI<CFixedStringKey >,
  1827. FlushClass, CullClass, SyncClass, StatClass> cacheBase;
  1828. unsigned __int64 m_dwdwStencilLifespan;
  1829. MonitorClass m_Monitor;
  1830. HANDLE m_hTimer;
  1831. CComPtr<IDllCache> m_spDllCache;
  1832. public:
  1833. CStencilCache() throw() :
  1834. m_dwdwStencilLifespan(ATL_STENCIL_LIFESPAN)
  1835. {
  1836. }
  1837. ~CStencilCache() throw()
  1838. {
  1839. if (m_hTimer)
  1840. m_Monitor.RemoveHandle(m_hTimer);
  1841. }
  1842. HRESULT Execute(DWORD_PTR dwParam, HANDLE /*hObject*/) throw()
  1843. {
  1844. CStencilCache* pCache = (CStencilCache*)dwParam;
  1845. if (pCache)
  1846. pCache->FlushEntries();
  1847. return S_OK;
  1848. }
  1849. HRESULT CloseHandle(HANDLE hObject) throw()
  1850. {
  1851. ATLASSERT(m_hTimer == hObject);
  1852. m_hTimer = NULL;
  1853. ::CloseHandle(hObject);
  1854. return S_OK;
  1855. }
  1856. HRESULT Initialize(IServiceProvider *pProv, DWORD dwStencilCacheTimeout=ATL_STENCIL_CACHE_TIMEOUT,
  1857. __int64 dwdwStencilLifespan=ATL_STENCIL_LIFESPAN) throw()
  1858. {
  1859. m_dwdwStencilLifespan = dwdwStencilLifespan;
  1860. HRESULT hr = cacheBase::Initialize();
  1861. if (FAILED(hr))
  1862. return hr;
  1863. hr = E_FAIL;
  1864. if (pProv)
  1865. hr = pProv->QueryService(__uuidof(IDllCache), __uuidof(IDllCache), (void**)&m_spDllCache);
  1866. if (FAILED(hr))
  1867. return hr;
  1868. hr = m_Monitor.Initialize();
  1869. if (FAILED(hr))
  1870. return hr;
  1871. return m_Monitor.AddTimer(dwStencilCacheTimeout, this, (DWORD_PTR) this, &m_hTimer);
  1872. }
  1873. template <class ThreadTraits>
  1874. HRESULT Initialize(IServiceProvider *pProv, CWorkerThread<ThreadTraits> *pWorkerThread,
  1875. DWORD dwStencilCacheTimeout=ATL_STENCIL_CACHE_TIMEOUT, __int64 dwdwStencilLifespan=ATL_STENCIL_LIFESPAN) throw()
  1876. {
  1877. m_dwdwStencilLifespan = dwdwStencilLifespan;
  1878. HRESULT hr = cacheBase::Initialize();
  1879. if (FAILED(hr))
  1880. return hr;
  1881. hr = E_FAIL;
  1882. if (pProv)
  1883. hr = pProv->QueryService(__uuidof(IDllCache), __uuidof(IDllCache), (void**)&m_spDllCache);
  1884. if (FAILED(hr))
  1885. return hr;
  1886. hr = m_Monitor.Initialize(pWorkerThread);
  1887. if (FAILED(hr))
  1888. return hr;
  1889. return m_Monitor.AddTimer(dwStencilCacheTimeout, this, (DWORD_PTR) this, &m_hTimer);
  1890. }
  1891. BEGIN_COM_MAP(CStencilCache)
  1892. COM_INTERFACE_ENTRY(IMemoryCacheStats)
  1893. COM_INTERFACE_ENTRY(IStencilCache)
  1894. COM_INTERFACE_ENTRY(IStencilCacheControl)
  1895. END_COM_MAP()
  1896. //IStencilCache methods
  1897. STDMETHOD(CacheStencil)(LPCSTR szName, void *pStencil, DWORD dwSize, HCACHEITEM *phEntry,
  1898. HINSTANCE hInstance, IMemoryCacheClient *pClient) throw()
  1899. {
  1900. NodeType * pEntry = NULL;
  1901. m_syncObj.Lock();
  1902. HRESULT hr;
  1903. _ATLTRY
  1904. {
  1905. hr = cacheBase::AddEntry(szName, pStencil, dwSize, (HCACHEITEM *)&pEntry);
  1906. }
  1907. _ATLCATCHALL()
  1908. {
  1909. hr = E_FAIL;
  1910. }
  1911. if (hr != S_OK)
  1912. {
  1913. m_syncObj.Unlock();
  1914. return hr;
  1915. }
  1916. pEntry->hInstance = hInstance;
  1917. pEntry->pClient = pClient;
  1918. pEntry->nLifespan = m_dwdwStencilLifespan;
  1919. if (hInstance && m_spDllCache)
  1920. m_spDllCache->AddRefModule(hInstance);
  1921. cacheBase::CommitEntry(static_cast<HCACHEITEM>(pEntry));
  1922. if (phEntry)
  1923. *phEntry = static_cast<HCACHEITEM>(pEntry);
  1924. else
  1925. cacheBase::ReleaseEntry(static_cast<HCACHEITEM>(pEntry));
  1926. m_syncObj.Unlock();
  1927. return hr;
  1928. }
  1929. STDMETHOD(LookupStencil)(LPCSTR szName, HCACHEITEM * phStencil) throw()
  1930. {
  1931. return cacheBase::LookupEntry(szName, phStencil);
  1932. }
  1933. STDMETHOD(GetStencil)(const HCACHEITEM hStencil, void ** pStencil) const throw()
  1934. {
  1935. return cacheBase::GetEntryData(hStencil, pStencil, NULL);
  1936. }
  1937. STDMETHOD(AddRefStencil)(const HCACHEITEM hStencil) throw()
  1938. {
  1939. return cacheBase::AddRefEntry(hStencil);
  1940. }
  1941. STDMETHOD(ReleaseStencil)(const HCACHEITEM hStencil) throw()
  1942. {
  1943. return cacheBase::ReleaseEntry(hStencil);
  1944. }
  1945. //IStencilCacheControl
  1946. STDMETHOD(RemoveStencil)(const HCACHEITEM hStencil) throw()
  1947. {
  1948. return cacheBase::RemoveEntry(hStencil);
  1949. }
  1950. STDMETHOD(RemoveStencilByName)(LPCSTR szStencil)
  1951. {
  1952. return cacheBase::RemoveEntryByKey(szStencil);
  1953. }
  1954. STDMETHOD(RemoveAllStencils)() throw()
  1955. {
  1956. return cacheBase::RemoveAllEntries();
  1957. }
  1958. STDMETHOD(SetDefaultLifespan)(unsigned __int64 dwdwLifespan) throw()
  1959. {
  1960. m_dwdwStencilLifespan = dwdwLifespan;
  1961. return S_OK;
  1962. }
  1963. STDMETHOD(GetDefaultLifespan)(unsigned __int64 *pdwdwLifepsan) throw()
  1964. {
  1965. HRESULT hr = E_POINTER;
  1966. if (pdwdwLifepsan)
  1967. {
  1968. *pdwdwLifepsan = m_dwdwStencilLifespan;
  1969. hr = S_OK;
  1970. }
  1971. return hr;
  1972. }
  1973. virtual void OnDestroyEntry(const NodeType * pEntry) throw()
  1974. {
  1975. ATLASSERT(pEntry);
  1976. if (!pEntry)
  1977. return;
  1978. if (pEntry->pClient)
  1979. pEntry->pClient->Free((void *)&pEntry->Data);
  1980. if (pEntry->hInstance && m_spDllCache)
  1981. m_spDllCache->ReleaseModule(pEntry->hInstance);
  1982. }
  1983. HRESULT Uninitialize() throw()
  1984. {
  1985. if (m_hTimer)
  1986. {
  1987. m_Monitor.RemoveHandle(m_hTimer);
  1988. m_hTimer = NULL;
  1989. }
  1990. m_Monitor.Shutdown();
  1991. return cacheBase::Uninitialize();
  1992. }
  1993. // IMemoryCacheStats methods
  1994. HRESULT STDMETHODCALLTYPE ClearStats() throw()
  1995. {
  1996. m_statObj.ResetCounters();
  1997. return S_OK;
  1998. }
  1999. HRESULT STDMETHODCALLTYPE GetHitCount(DWORD *pdwSize) throw()
  2000. {
  2001. if (!pdwSize)
  2002. return E_POINTER;
  2003. *pdwSize = m_statObj.GetHitCount();
  2004. return S_OK;
  2005. }
  2006. HRESULT STDMETHODCALLTYPE GetMissCount(DWORD *pdwSize) throw()
  2007. {
  2008. if (!pdwSize)
  2009. return E_POINTER;
  2010. *pdwSize = m_statObj.GetMissCount();
  2011. return S_OK;
  2012. }
  2013. HRESULT STDMETHODCALLTYPE GetMaxAllocSize(DWORD *pdwSize) throw()
  2014. {
  2015. if (!pdwSize)
  2016. return E_POINTER;
  2017. *pdwSize = m_statObj.GetMaxAllocSize();
  2018. return S_OK;
  2019. }
  2020. HRESULT STDMETHODCALLTYPE GetCurrentAllocSize(DWORD *pdwSize) throw()
  2021. {
  2022. if (!pdwSize)
  2023. return E_POINTER;
  2024. *pdwSize = m_statObj.GetCurrentAllocSize();
  2025. return S_OK;
  2026. }
  2027. HRESULT STDMETHODCALLTYPE GetMaxEntryCount(DWORD *pdwSize) throw()
  2028. {
  2029. if (!pdwSize)
  2030. return E_POINTER;
  2031. *pdwSize = m_statObj.GetMaxEntryCount();
  2032. return S_OK;
  2033. }
  2034. HRESULT STDMETHODCALLTYPE GetCurrentEntryCount(DWORD *pdwSize) throw()
  2035. {
  2036. if (!pdwSize)
  2037. return E_POINTER;
  2038. *pdwSize = m_statObj.GetCurrentEntryCount();
  2039. return S_OK;
  2040. }
  2041. }; // CStencilCache
  2042. // {105A8866-4059-45fe-86AE-FA0EABBFBBB4}
  2043. extern "C" __declspec(selectany) IID IID_IFileCache = { 0x105a8866, 0x4059, 0x45fe, { 0x86, 0xae, 0xfa, 0xe, 0xab, 0xbf, 0xbb, 0xb4 } };
  2044. __interface ATL_NO_VTABLE __declspec(uuid("105A8866-4059-45fe-86AE-FA0EABBFBBB4"))
  2045. IFileCache : public IUnknown
  2046. {
  2047. // IFileCache Methods
  2048. STDMETHOD(AddFile)(
  2049. LPCSTR szFileName,
  2050. LPCSTR szTempFileName,
  2051. FILETIME *pftExpireTime,
  2052. void *pPeerInfo,
  2053. HCACHEITEM * phKey);
  2054. STDMETHOD(LookupFile)(LPCSTR szFileName, HCACHEITEM * phKey);
  2055. STDMETHOD(GetFile)(const HCACHEITEM hKey, LPSTR * pszFileName, void **ppPeerInfo);
  2056. STDMETHOD(ReleaseFile)(const HCACHEITEM hKey);
  2057. STDMETHOD(RemoveFile)(const HCACHEITEM hKey);
  2058. STDMETHOD(RemoveFileByName)(LPCSTR szFileName);
  2059. STDMETHOD(Flush)();
  2060. };
  2061. #ifndef ATL_FILE_CACHE_TIMEOUT
  2062. #define ATL_FILE_CACHE_TIMEOUT 1000
  2063. #endif
  2064. class CNoFileCachePeer
  2065. {
  2066. public:
  2067. struct PeerInfo
  2068. {
  2069. };
  2070. static BOOL Add(PeerInfo* /*pDest*/, void * /*pSrc*/)
  2071. {
  2072. return TRUE;
  2073. }
  2074. static BOOL Remove(const PeerInfo* /*pFileInfo*/)
  2075. {
  2076. return TRUE;
  2077. }
  2078. };
  2079. template <class Peer>
  2080. struct CCacheDataPeer : public CCacheDataBase
  2081. {
  2082. Peer::PeerInfo PeerData;
  2083. };
  2084. // A class to keep track of files, with maintenance -- maximum size of cache,
  2085. // maximum number of entries, expiration of entries, etc. -- inherits from
  2086. // CMemoryCacheBase
  2087. template <
  2088. class MonitorClass,
  2089. class StatClass=CStdStatClass,
  2090. class FileCachePeer=CNoFileCachePeer,
  2091. class FlushClass=COldFlusher,
  2092. class SyncClass=CComCriticalSection,
  2093. class CullClass=CExpireCuller >
  2094. class CFileCache:
  2095. public CMemoryCacheBase<CFileCache, LPSTR, CCacheDataPeer<FileCachePeer>,
  2096. CFixedStringKey, CStringElementTraits<CFixedStringKey >,
  2097. FlushClass, CullClass, SyncClass, StatClass>,
  2098. public IWorkerThreadClient,
  2099. public IFileCache,
  2100. public IMemoryCacheControl,
  2101. public IMemoryCacheStats
  2102. {
  2103. typedef CMemoryCacheBase<CFileCache, LPSTR, CCacheDataPeer<FileCachePeer>,
  2104. CFixedStringKey, CStringElementTraits<CFixedStringKey >,
  2105. FlushClass, CullClass, SyncClass, StatClass> cacheBase;
  2106. MonitorClass m_Monitor;
  2107. protected:
  2108. HANDLE m_hTimer;
  2109. public:
  2110. HRESULT Initialize() throw()
  2111. {
  2112. HRESULT hr = cacheBase::Initialize();
  2113. if (FAILED(hr))
  2114. return hr;
  2115. hr = m_Monitor.Initialize();
  2116. if (FAILED(hr))
  2117. return hr;
  2118. return m_Monitor.AddTimer(ATL_FILE_CACHE_TIMEOUT,
  2119. static_cast<IWorkerThreadClient*>(this), (DWORD_PTR) this, &m_hTimer);
  2120. }
  2121. template <class ThreadTraits>
  2122. HRESULT Initialize(CWorkerThread<ThreadTraits> *pWorkerThread) throw()
  2123. {
  2124. ATLASSERT(pWorkerThread);
  2125. HRESULT hr = S_OK;
  2126. if (S_OK == cacheBase::Initialize())
  2127. {
  2128. hr = m_Monitor.Initialize(pWorkerThread);
  2129. if (FAILED(hr))
  2130. return hr;
  2131. return m_Monitor.AddTimer(ATL_FILE_CACHE_TIMEOUT,
  2132. static_cast<IWorkerThreadClient*>(this), (DWORD_PTR) this, &m_hTimer);
  2133. }
  2134. return S_OK;
  2135. }
  2136. // Callback for CWorkerThread
  2137. HRESULT Execute(DWORD_PTR dwParam, HANDLE /*hObject*/) throw()
  2138. {
  2139. CFileCache* pCache = (CFileCache*)dwParam;
  2140. if (pCache)
  2141. pCache->Flush();
  2142. return S_OK;
  2143. }
  2144. HRESULT CloseHandle(HANDLE hObject) throw()
  2145. {
  2146. ATLASSERT(m_hTimer == hObject);
  2147. m_hTimer = NULL;
  2148. ::CloseHandle(hObject);
  2149. return S_OK;
  2150. }
  2151. ~CFileCache() throw()
  2152. {
  2153. if (m_hTimer)
  2154. {
  2155. m_Monitor.RemoveHandle(m_hTimer);
  2156. m_hTimer = NULL;
  2157. }
  2158. }
  2159. HRESULT Uninitialize() throw()
  2160. {
  2161. if (m_hTimer)
  2162. {
  2163. m_Monitor.RemoveHandle(m_hTimer);
  2164. m_hTimer = NULL;
  2165. }
  2166. m_Monitor.Shutdown();
  2167. return cacheBase::Uninitialize();
  2168. }
  2169. // IUnknown methods
  2170. HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, void **ppv) throw()
  2171. {
  2172. HRESULT hr = E_NOINTERFACE;
  2173. if (!ppv)
  2174. hr = E_POINTER;
  2175. else
  2176. {
  2177. if (InlineIsEqualGUID(riid, __uuidof(IUnknown)) ||
  2178. InlineIsEqualGUID(riid, __uuidof(IFileCache)))
  2179. {
  2180. *ppv = (IUnknown *) (IFileCache *) this;
  2181. AddRef();
  2182. hr = S_OK;
  2183. }
  2184. if (InlineIsEqualGUID(riid, __uuidof(IMemoryCacheStats)))
  2185. {
  2186. *ppv = (IMemoryCacheStats*)this;
  2187. AddRef();
  2188. hr = S_OK;
  2189. }
  2190. if (InlineIsEqualGUID(riid, __uuidof(IMemoryCacheControl)))
  2191. {
  2192. *ppv = (IMemoryCacheControl*)this;
  2193. AddRef();
  2194. hr = S_OK;
  2195. }
  2196. }
  2197. return hr;
  2198. }
  2199. ULONG STDMETHODCALLTYPE AddRef() throw()
  2200. {
  2201. return 1;
  2202. }
  2203. ULONG STDMETHODCALLTYPE Release() throw()
  2204. {
  2205. return 1;
  2206. }
  2207. // Adds a file to the cache. A file is created with a
  2208. // temporary name, and then Add is called with the temp
  2209. // file name and the final file name, along with expiration data,
  2210. // etc. A search on the file name will return the name of
  2211. // the file on disk (i.e. the temporary file)
  2212. HRESULT STDMETHODCALLTYPE AddFile(
  2213. LPCSTR szFileName,
  2214. LPCSTR szTempFileName,
  2215. FILETIME *pftExpireTime,
  2216. void *pPeerInfo,
  2217. HCACHEITEM * phKey = NULL) throw()
  2218. {
  2219. WIN32_FILE_ATTRIBUTE_DATA fadData;
  2220. BOOL bRet = GetFileAttributesExA(szTempFileName, GetFileExInfoStandard, &fadData);
  2221. if (!bRet)
  2222. return AtlHresultFromLastError();
  2223. __int64 ddwFileSize = (fadData.nFileSizeHigh << 32) + fadData.nFileSizeLow;
  2224. DWORD dwRecordedFileSize = (DWORD) (ddwFileSize >> 10);
  2225. // Round the file size up to 1K if it is < 1K
  2226. if (dwRecordedFileSize == 0)
  2227. dwRecordedFileSize = 1;
  2228. if (m_Monitor.GetThreadHandle()==NULL)
  2229. {
  2230. if (!cacheBase::CanAddEntry(dwRecordedFileSize))
  2231. {
  2232. cacheBase::FlushEntries();
  2233. if (!cacheBase::CanAddEntry(dwRecordedFileSize))
  2234. return E_OUTOFMEMORY;
  2235. }
  2236. }
  2237. HRESULT hr = E_FAIL;
  2238. NodeType *pEntry = NULL;
  2239. m_syncObj.Lock();
  2240. // Make a private copy of the file name
  2241. CHeapPtr<char> szTempFileCopy;
  2242. if (szTempFileCopy.Allocate(MAX_PATH))
  2243. {
  2244. strcpy(szTempFileCopy, szTempFileName);
  2245. _ATLTRY
  2246. {
  2247. hr = cacheBase::AddEntry(szFileName, szTempFileCopy, dwRecordedFileSize, (HCACHEITEM*)&pEntry);
  2248. szTempFileCopy.Detach();
  2249. }
  2250. _ATLCATCHALL()
  2251. {
  2252. hr = E_FAIL;
  2253. }
  2254. }
  2255. if (hr != S_OK)
  2256. {
  2257. m_syncObj.Unlock();
  2258. return hr;
  2259. }
  2260. if (pftExpireTime)
  2261. pEntry->cftExpireTime = *pftExpireTime;
  2262. FileCachePeer::Add(&pEntry->PeerData, pPeerInfo);
  2263. cacheBase::CommitEntry(static_cast<HCACHEITEM>(pEntry));
  2264. if (phKey)
  2265. *phKey = static_cast<HCACHEITEM>(pEntry);
  2266. else
  2267. cacheBase::ReleaseEntry(pEntry);
  2268. m_syncObj.Unlock();
  2269. return hr;
  2270. }
  2271. // Action to take when the entry is removed from the cache
  2272. virtual void OnDestroyEntry(const NodeType * pEntry) throw()
  2273. {
  2274. ATLASSERT(pEntry);
  2275. if (!pEntry)
  2276. return;
  2277. FileCachePeer::Remove(&pEntry->PeerData);
  2278. DeleteFileA(pEntry->Data);
  2279. free(pEntry->Data);
  2280. }
  2281. // Looks up a file by name. Must be released after use
  2282. HRESULT STDMETHODCALLTYPE LookupFile(LPCSTR szFileName, HCACHEITEM * phKey) throw()
  2283. {
  2284. return cacheBase::LookupEntry(szFileName, phKey);
  2285. }
  2286. // Gets the name of the file on disk
  2287. HRESULT STDMETHODCALLTYPE GetFile(const HCACHEITEM hKey, LPSTR * pszFileName, void **ppPeerInfo) throw()
  2288. {
  2289. NodeType *pEntry = (NodeType *)hKey;
  2290. if (ppPeerInfo)
  2291. *ppPeerInfo = &pEntry->PeerData;
  2292. return cacheBase::GetEntryData(hKey, pszFileName, NULL);
  2293. }
  2294. // Releases a file
  2295. HRESULT STDMETHODCALLTYPE ReleaseFile(const HCACHEITEM hKey) throw()
  2296. {
  2297. return cacheBase::ReleaseEntry(hKey);
  2298. }
  2299. // Releases a file and marks it for deletion
  2300. HRESULT STDMETHODCALLTYPE RemoveFile(const HCACHEITEM hKey) throw()
  2301. {
  2302. return cacheBase::RemoveEntry(hKey);
  2303. }
  2304. // Removes a file by name -- this calls IMemoryCacheClient->Free
  2305. // on the file name, which by default (for CFileCache) deletes the
  2306. // file.
  2307. HRESULT STDMETHODCALLTYPE RemoveFileByName(LPCSTR szFileName) throw()
  2308. {
  2309. return cacheBase::RemoveEntryByKey(szFileName);
  2310. }
  2311. // Flushes the entries in the cache, eliminates expired entries,
  2312. // or if the cache exceeds the parameters (alloc size, num entries),
  2313. // culls items based on the sweep mode
  2314. HRESULT STDMETHODCALLTYPE Flush() throw()
  2315. {
  2316. return cacheBase::FlushEntries();
  2317. }
  2318. // IMemoryCacheControl methods
  2319. HRESULT STDMETHODCALLTYPE SetMaxAllowedSize(DWORD dwSize) throw()
  2320. {
  2321. return cacheBase::SetMaxAllowedSize(dwSize);
  2322. }
  2323. HRESULT STDMETHODCALLTYPE GetMaxAllowedSize(DWORD *pdwSize) throw()
  2324. {
  2325. return cacheBase::GetMaxAllowedSize(pdwSize);
  2326. }
  2327. HRESULT STDMETHODCALLTYPE SetMaxAllowedEntries(DWORD dwSize) throw()
  2328. {
  2329. return cacheBase::SetMaxAllowedEntries(dwSize);
  2330. }
  2331. HRESULT STDMETHODCALLTYPE GetMaxAllowedEntries(DWORD *pdwSize) throw()
  2332. {
  2333. return cacheBase::GetMaxAllowedEntries(pdwSize);
  2334. }
  2335. HRESULT STDMETHODCALLTYPE ResetCache() throw()
  2336. {
  2337. return cacheBase::ResetCache();
  2338. }
  2339. // IMemoryCacheStats methods
  2340. HRESULT STDMETHODCALLTYPE ClearStats() throw()
  2341. {
  2342. m_statObj.ResetCounters();
  2343. return S_OK;
  2344. }
  2345. HRESULT STDMETHODCALLTYPE GetHitCount(DWORD *pdwSize) throw()
  2346. {
  2347. if (!pdwSize)
  2348. return E_POINTER;
  2349. *pdwSize = m_statObj.GetHitCount();
  2350. return S_OK;
  2351. }
  2352. HRESULT STDMETHODCALLTYPE GetMissCount(DWORD *pdwSize) throw()
  2353. {
  2354. if (!pdwSize)
  2355. return E_POINTER;
  2356. *pdwSize = m_statObj.GetMissCount();
  2357. return S_OK;
  2358. }
  2359. HRESULT STDMETHODCALLTYPE GetMaxAllocSize(DWORD *pdwSize) throw()
  2360. {
  2361. if (!pdwSize)
  2362. return E_POINTER;
  2363. *pdwSize = m_statObj.GetMaxAllocSize();
  2364. return S_OK;
  2365. }
  2366. HRESULT STDMETHODCALLTYPE GetCurrentAllocSize(DWORD *pdwSize) throw()
  2367. {
  2368. if (!pdwSize)
  2369. return E_POINTER;
  2370. *pdwSize = m_statObj.GetCurrentAllocSize();
  2371. return S_OK;
  2372. }
  2373. HRESULT STDMETHODCALLTYPE GetMaxEntryCount(DWORD *pdwSize) throw()
  2374. {
  2375. if (!pdwSize)
  2376. return E_POINTER;
  2377. *pdwSize = m_statObj.GetMaxEntryCount();
  2378. return S_OK;
  2379. }
  2380. HRESULT STDMETHODCALLTYPE GetCurrentEntryCount(DWORD *pdwSize) throw()
  2381. {
  2382. if (!pdwSize)
  2383. return E_POINTER;
  2384. *pdwSize = m_statObj.GetCurrentEntryCount();
  2385. return S_OK;
  2386. }
  2387. }; // CFileCache
  2388. class CDataConnection; // see atldbcli.h
  2389. __interface __declspec(uuid("52E7759B-D6CC-4a03-BDF3-80A6BDCA1F94"))
  2390. IDataSourceCache : public IUnknown
  2391. {
  2392. // Method: Add
  2393. // Params:
  2394. // szConn: Connection string of data source to connect to
  2395. // ppDS: Out pointer to the newly added data source
  2396. // Comments:
  2397. // Attempts to open a connection to the specified data source
  2398. // using a CDataSource object. Once the connection is open, the
  2399. // CDatasource is cached.
  2400. STDMETHOD(Add)(LPCTSTR szID, LPCOLESTR szConn, CDataConnection *pDS);
  2401. // Method: Remove
  2402. // Params:
  2403. // szConn: Specifies the connection string of the connection to close
  2404. // Comments:
  2405. // Closes the specified connection and removes it's entry from the cache
  2406. STDMETHOD(Remove)(LPCTSTR szID);
  2407. // Method: Lookup
  2408. // Params:
  2409. // szConn: Specifies the connection string of the connection to look up
  2410. // ppDS: Out pointer to CDataSource object that is connected to the specified
  2411. // data source.
  2412. STDMETHOD(Lookup)(LPCTSTR szID, CDataConnection *pDS);
  2413. // Method: Uninitialize
  2414. // Params:
  2415. // None
  2416. // Comments:
  2417. // Closes removes all connections from the cache.
  2418. STDMETHOD(Uninitialize)();
  2419. };
  2420. #ifndef ATL_DS_CONN_STRING_LEN
  2421. #define ATL_DS_CONN_STRING_LEN 512
  2422. #endif
  2423. template <>
  2424. class CElementTraits< CDataConnection > :
  2425. public CElementTraitsBase< CDataConnection >
  2426. {
  2427. public:
  2428. static ULONG Hash( INARGTYPE t )
  2429. {
  2430. return( ULONG( ULONG_PTR( &t ) ) );
  2431. }
  2432. static bool CompareElements( INARGTYPE element1, INARGTYPE element2 )
  2433. {
  2434. return( element1.m_session.m_spOpenRowset == element2.m_session.m_spOpenRowset);
  2435. }
  2436. static int CompareElementsOrdered( INARGTYPE /*element1*/, INARGTYPE /*element2*/ )
  2437. {
  2438. ATLASSERT(FALSE);
  2439. return -1;
  2440. }
  2441. };
  2442. typedef CFixedStringT<CString, ATL_DS_CONN_STRING_LEN> atlDataSourceKey;
  2443. typedef CAtlMap<atlDataSourceKey, CDataConnection,
  2444. CStringElementTraits<atlDataSourceKey>, CElementTraits<CDataConnection> > atlDataSourceCacheMap;
  2445. template <class TCritSec=CComFakeCriticalSection>
  2446. class CDataSourceCache :
  2447. public IDataSourceCache,
  2448. public CComObjectRootEx<CComGlobalsThreadModel>
  2449. {
  2450. public:
  2451. BEGIN_COM_MAP(CDataSourceCache)
  2452. COM_INTERFACE_ENTRY(IDataSourceCache)
  2453. END_COM_MAP()
  2454. CDataSourceCache() throw()
  2455. {
  2456. m_cs.Init();
  2457. }
  2458. virtual ~CDataSourceCache () throw()
  2459. {
  2460. Uninitialize();
  2461. }
  2462. STDMETHOD(Uninitialize)() throw()
  2463. {
  2464. m_cs.Lock();
  2465. m_ConnectionMap.RemoveAll();
  2466. m_cs.Unlock();
  2467. return S_OK;
  2468. }
  2469. STDMETHOD(Add)(LPCTSTR szID, LPCOLESTR szConn, CDataConnection *pSession) throw()
  2470. {
  2471. HRESULT hr = E_FAIL;
  2472. if (pSession)
  2473. *pSession = NULL;
  2474. if (!szID)
  2475. return E_INVALIDARG; // must have session name
  2476. // Do a lookup to make sure we don't add multiple entries
  2477. // with the same name. Adding multiple entries with the same name
  2478. // could cause some entries to get orphaned.
  2479. m_cs.Lock();
  2480. const atlDataSourceCacheMap::CPair *pPair =
  2481. m_ConnectionMap.Lookup(szID);
  2482. if (!pPair)
  2483. {
  2484. // try to open connection
  2485. CDataConnection DS;
  2486. hr = DS.Open(szConn);
  2487. if (hr == S_OK)
  2488. {
  2489. _ATLTRY
  2490. {
  2491. if (m_ConnectionMap.SetAt(szID, DS))
  2492. {
  2493. if (pSession)
  2494. *pSession = DS; // copy connection to output.
  2495. hr = S_OK;
  2496. }
  2497. else
  2498. hr = E_FAIL; // map add failed
  2499. }
  2500. _ATLCATCHALL()
  2501. {
  2502. hr = E_FAIL;
  2503. }
  2504. }
  2505. }
  2506. else // lookup succeeded, entry is already in cache
  2507. {
  2508. // Instead of opening a new connection, just copy
  2509. // the one we already have in the cache.
  2510. if (pSession)
  2511. *pSession = pPair->m_value;
  2512. hr = S_OK;
  2513. }
  2514. m_cs.Unlock();
  2515. return hr;
  2516. }
  2517. STDMETHOD(Remove)(LPCTSTR szID) throw()
  2518. {
  2519. HRESULT hr = E_INVALIDARG;
  2520. if (!szID)
  2521. return hr; // must have session name
  2522. m_cs.Lock();
  2523. hr = m_ConnectionMap.RemoveKey(szID) ? S_OK : E_FAIL;
  2524. m_cs.Unlock();
  2525. return hr;
  2526. }
  2527. STDMETHOD(Lookup)(LPCTSTR szID, CDataConnection *pSession) throw()
  2528. {
  2529. if (!szID||!pSession)
  2530. return E_POINTER;
  2531. m_cs.Lock();
  2532. bool bRet = m_ConnectionMap.Lookup(szID, *pSession);
  2533. m_cs.Unlock();
  2534. return (bRet && (bool)*pSession)? S_OK : E_FAIL;
  2535. }
  2536. protected:
  2537. atlDataSourceCacheMap m_ConnectionMap;
  2538. TCritSec m_cs;
  2539. };
  2540. // Some helpers for using the datasource cache.
  2541. //
  2542. // Function: GetDataSource
  2543. // Params:
  2544. // pProvider: Pointer to IServiceProvider that provides the
  2545. // data source cache service
  2546. // szID: The name of the connection (can be same as szDS)
  2547. // szDS: OLEDB connection string for data source
  2548. // ppDS: Out pointer to CDataSource. The CDataSource will be connected
  2549. // to the OLEDB provider specified by szDS on successful return.
  2550. // RetVal:
  2551. // Returns S_OK on success.
  2552. static HRESULT ATL_NOINLINE GetDataSource(IServiceProvider *pProvider,
  2553. LPCTSTR szID, LPCOLESTR szConn,
  2554. CDataConnection *pSession) throw()
  2555. {
  2556. if (!pProvider || !szID || !szConn || !pSession)
  2557. return E_POINTER;
  2558. CComPtr<IDataSourceCache> spDSCache;
  2559. HRESULT hr;
  2560. hr = pProvider->QueryService(__uuidof(IDataSourceCache), __uuidof(IDataSourceCache), (void**)&spDSCache);
  2561. if (hr == S_OK && spDSCache)
  2562. {
  2563. hr = spDSCache->Add(szID, szConn, pSession);
  2564. }
  2565. return hr;
  2566. }
  2567. //
  2568. // Function: RemoveDataSource
  2569. // Params:
  2570. // pProvider: Pointer to IServiceProvider that provides the
  2571. // data source cache service
  2572. // szID: Name of the datasource connection to remove from the cache
  2573. // RetVal:
  2574. // none
  2575. // Comments:
  2576. // Removes the datasource entry from the datasource cache. Since entries are
  2577. // copied to the client on calls to lookup and add, removing an entry will not
  2578. // release the connections of existing clients.
  2579. static HRESULT ATL_NOINLINE RemoveDataSource(IServiceProvider *pProvider, LPCTSTR szID) throw()
  2580. {
  2581. if (!pProvider || !szID)
  2582. return E_POINTER;
  2583. CComPtr<IDataSourceCache> spDSCache;
  2584. HRESULT hr = pProvider->QueryService(__uuidof(IDataSourceCache), __uuidof(IDataSourceCache), (void**)&spDSCache);
  2585. if (spDSCache)
  2586. hr = spDSCache->Remove(szID);
  2587. return hr;
  2588. }
  2589. } // namespace ATL
  2590. #pragma warning (pop)
  2591. #endif // __ATLCACHE_H__