Support unrar64.dll
[xy_vsfilter.git] / include / atl / atlcache.h
blob7eacf9aa853b49a21af0d9cfcd050cb63db563c4
1 // This is a part of the Active Template Library.
2 // Copyright (C) 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.
11 #ifndef __ATLCACHE_H__
12 #define __ATLCACHE_H__
14 #pragma once
16 #include <atltime.h>
17 #include <atlutil.h>
18 #include <atlcoll.h>
19 #include <atlperf.h>
20 #include <atlcom.h>
21 #include <atlstr.h>
22 #include <atlsrvres.h>
23 #include <atldbcli.h>
24 #include <atlspriv.h>
25 #include <atlutil.h>
27 #pragma warning (push)
28 #ifndef _ATL_NO_PRAGMA_WARNINGS
29 #pragma warning(disable: 4511) // copy constructor could not be generated
30 #pragma warning(disable: 4512) // assignment operator could not be generated
31 #endif //!_ATL_NO_PRAGMA_WARNINGS
33 #pragma warning(disable: 4625) // copy constructor could not be generated because a base class copy constructor is inaccessible
34 #pragma warning(disable: 4626) // assignment operator could not be generated because a base class assignment operator is inaccessible
36 #ifndef _CPPUNWIND
37 #pragma warning(disable: 4702) // unreachable code
38 #endif
39 #pragma pack(push,_ATL_PACKING)
40 namespace ATL {
42 //forward declarations;
43 class CStdStatClass;
44 class CPerfStatClass;
46 typedef struct __CACHEITEM
48 } *HCACHEITEM;
50 //Implementation of a cache that stores pointers to void
51 extern "C" __declspec(selectany) const IID IID_IMemoryCacheClient = {0xb721b49d, 0xbb57, 0x47bc, { 0xac, 0x43, 0xa8, 0xd4, 0xc0, 0x7d, 0x18, 0x3d } };
52 extern "C" __declspec(selectany) const IID IID_IMemoryCache = { 0x9c6cfb46, 0xfbde, 0x4f8b, { 0xb9, 0x44, 0x2a, 0xa0, 0x5d, 0x96, 0xeb, 0x5c } };
53 extern "C" __declspec(selectany) const IID IID_IMemoryCacheControl = { 0x7634b28b, 0xd819, 0x409d, { 0xb9, 0x6e, 0xfc, 0x9f, 0x3a, 0xba, 0x32, 0x9f } };
54 extern "C" __declspec(selectany) const IID IID_IMemoryCacheStats = { 0xd4b6df2d, 0x4bc0, 0x4734, { 0x8a, 0xce, 0xb7, 0x3a, 0xb, 0x97, 0x59, 0x56 } };
56 __interface ATL_NO_VTABLE __declspec(uuid("b721b49d-bb57-47bc-ac43-a8d4c07d183d"))
57 IMemoryCacheClient : public IUnknown
59 // IMemoryCacheClient methods
60 STDMETHOD( Free )(const void *pvData);
63 __interface ATL_NO_VTABLE __declspec(uuid("9c6cfb46-fbde-4f8b-b944-2aa05d96eb5c"))
64 IMemoryCache : public IUnknown
66 // IMemoryCache Methods
67 STDMETHOD(Add)(LPCSTR szKey, void *pvData, DWORD dwSize,
68 FILETIME *pftExpireTime,
69 HINSTANCE hInstClient, HCACHEITEM *phEntry,
70 IMemoryCacheClient *pClient);
72 STDMETHOD(LookupEntry)(LPCSTR szKey, HCACHEITEM * phEntry);
73 STDMETHOD(GetData)(const HCACHEITEM hEntry, void **ppvData, DWORD *pdwSize) const;
74 STDMETHOD(ReleaseEntry)(const HCACHEITEM hEntry);
75 STDMETHOD(RemoveEntry)(const HCACHEITEM hEntry);
76 STDMETHOD(RemoveEntryByKey)(LPCSTR szKey);
78 STDMETHOD(Flush)();
81 __interface ATL_NO_VTABLE __declspec(uuid("7634b28b-d819-409d-b96e-fc9f3aba329f"))
82 IMemoryCacheControl : public IUnknown
84 // IMemoryCacheControl Methods
85 STDMETHOD(SetMaxAllowedSize)(DWORD dwSize);
86 STDMETHOD(GetMaxAllowedSize)(DWORD *pdwSize);
87 STDMETHOD(SetMaxAllowedEntries)(DWORD dwSize);
88 STDMETHOD(GetMaxAllowedEntries)(DWORD *pdwSize);
89 STDMETHOD(ResetCache)();
92 __interface ATL_NO_VTABLE __declspec(uuid("d4b6df2d-4bc0-4734-8ace-b73a0b975956"))
93 IMemoryCacheStats : public IUnknown
95 // IMemoryCacheStats Methods
96 STDMETHOD(ClearStats)();
97 STDMETHOD(GetHitCount)(DWORD *pdwSize);
98 STDMETHOD(GetMissCount)(DWORD *pdwSize);
99 STDMETHOD(GetCurrentAllocSize)(DWORD *pdwSize);
100 STDMETHOD(GetMaxAllocSize)(DWORD *pdwSize);
101 STDMETHOD(GetCurrentEntryCount)(DWORD *pdwSize);
102 STDMETHOD(GetMaxEntryCount)(DWORD *pdwSize);
106 struct DLL_CACHE_ENTRY
108 HINSTANCE hInstDll;
109 DWORD dwRefs;
110 BOOL bAlive;
111 CHAR szDllName[MAX_PATH];
114 inline bool operator==(const DLL_CACHE_ENTRY& entry1, const DLL_CACHE_ENTRY& entry2)
116 return (entry1.hInstDll == entry2.hInstDll);
120 // IDllCache
121 // An interface that is used to load and unload Dlls.
123 __interface ATL_NO_VTABLE __declspec(uuid("A12478AB-D261-42f9-B525-7589143C1C97"))
124 IDllCache : public IUnknown
126 // IDllCache methods
127 virtual HINSTANCE Load(LPCSTR szFileName, void *pPeerInfo);
128 virtual BOOL Free(HINSTANCE hInstance);
129 virtual BOOL AddRefModule(HINSTANCE hInstance);
130 virtual BOOL ReleaseModule(HINSTANCE hInstance);
131 virtual HRESULT GetEntries(DWORD dwCount, DLL_CACHE_ENTRY *pEntries, DWORD *pdwCopied);
132 virtual HRESULT Flush();
135 #ifndef ATL_CACHE_KEY_LENGTH
136 #define ATL_CACHE_KEY_LENGTH 128
137 #endif
139 typedef CFixedStringT<CStringA, ATL_CACHE_KEY_LENGTH> CFixedStringKey;
141 struct CFlusherCacheData
143 CFlusherCacheData *pNext;
144 CFlusherCacheData *pPrev;
145 DWORD dwAccessed;
147 CFlusherCacheData()
149 pNext = NULL;
150 pPrev = NULL;
151 dwAccessed = 0;
155 // No flusher -- only expired entries will be removed from the cache
156 // Also gives the skeleton for all of the flushers
157 class CNoFlusher
159 public:
160 void Add(CFlusherCacheData * /*pItem*/) { }
161 void Remove(CFlusherCacheData * /*pItem*/) { }
162 void Access(CFlusherCacheData * /*pItem*/) { }
163 CFlusherCacheData * GetStart() const { return NULL; }
164 CFlusherCacheData * GetNext(CFlusherCacheData * /*pCur*/) const { return NULL; }
165 void Release(CFlusherCacheData * /*pItem*/){ }
168 // Old flusher -- oldest items are flushed first
169 class COldFlusher
171 public:
172 CFlusherCacheData * pHead;
173 CFlusherCacheData * pTail;
175 COldFlusher() : pHead(NULL), pTail(NULL)
179 // Add it to the tail of the list
180 void Add(CFlusherCacheData * pItem)
182 ATLENSURE(pItem);
184 pItem->pNext = NULL;
185 pItem->pPrev = pTail;
186 if (pHead)
188 pTail->pNext = pItem;
189 pTail = pItem;
191 else
193 pHead = pItem;
194 pTail = pItem;
198 void Remove(CFlusherCacheData * pItem)
200 ATLENSURE(pItem);
202 CFlusherCacheData * pPrev = pItem->pPrev;
203 CFlusherCacheData * pNext = pItem->pNext;
205 if (pPrev)
206 pPrev->pNext = pNext;
207 else
208 pHead = pNext;
210 if (pNext)
211 pNext->pPrev = pPrev;
212 else
213 pTail = pPrev;
217 void Access(CFlusherCacheData * /*pItem*/)
221 void Release(CFlusherCacheData * /*pItem*/)
225 CFlusherCacheData * GetStart() const
227 return pHead;
230 CFlusherCacheData * GetNext(CFlusherCacheData * pCur) const
232 if (pCur != NULL)
233 return pCur->pNext;
234 else
235 return NULL;
239 // Least recently used flusher -- the item that was accessed the longest time ago is flushed
240 class CLRUFlusher : public COldFlusher
242 public:
243 // Move it to the tail of the list
244 void Access(CFlusherCacheData * pItem)
246 ATLASSERT(pItem);
248 Remove(pItem);
249 Add(pItem);
253 // Least often used flusher
254 class CLOUFlusher : public COldFlusher
256 public:
257 // Adds to the tail of the list
258 void Add(CFlusherCacheData * pItem)
260 ATLENSURE(pItem);
261 pItem->dwAccessed = 1;
262 COldFlusher::Add(pItem);
265 void Access(CFlusherCacheData * pItem)
267 ATLENSURE(pItem);
268 pItem->dwAccessed++;
270 CFlusherCacheData * pMark = static_cast<CFlusherCacheData *>(pItem->pPrev);
271 if (!pMark) // The item is already at the head
272 return;
274 if (pMark->dwAccessed >= pItem->dwAccessed) // The element before it has
275 return; // been accessed more times
277 Remove(pItem);
279 while (pMark && (pMark->dwAccessed < pItem->dwAccessed))
280 pMark = static_cast<CFlusherCacheData *>(pMark->pPrev);
282 // pMark points to the first element that has been accessed more times,
283 // so add pItem after pMark
284 if (pMark)
286 CFlusherCacheData *pNext = static_cast<CFlusherCacheData *>(pMark->pNext);
287 pMark->pNext = pItem;
288 pItem->pPrev = pMark;
290 pItem->pNext = pNext;
291 pNext->pPrev = pItem;
293 else // Ran out of items -- put it on the head
295 pItem->pNext = pHead;
296 pItem->pPrev = NULL;
297 if (pHead)
298 pHead->pPrev = pItem;
299 else // the list was empty
300 pTail = pItem;
301 pHead = pItem;
305 // We start at the tail and move forward for this flusher
306 CFlusherCacheData * GetStart() const
308 return pTail;
311 CFlusherCacheData * GetNext(CFlusherCacheData * pCur) const
313 if (pCur != NULL)
314 return static_cast<CFlusherCacheData *>(pCur->pPrev);
315 else
316 return NULL;
320 template <class CFirst, class CSecond>
321 class COrFlushers
323 CFirst m_First;
324 CSecond m_Second;
325 BOOL m_bWhich;
326 public:
327 COrFlushers()
329 m_bWhich = FALSE;
332 BOOL Switch()
334 m_bWhich = !m_bWhich;
335 return m_bWhich;
338 void Add(CFlusherCacheData * pItem)
340 ATLASSERT(pItem);
341 m_First.Add(pItem);
342 m_Second.Add(pItem);
345 void Remove(CFlusherCacheData * pItem)
347 ATLASSERT(pItem);
348 m_First.Remove(pItem);
349 m_Second.Remove(pItem);
352 void Access(CFlusherCacheData * pItem)
354 ATLASSERT(pItem);
355 m_First.Access(pItem);
356 m_Second.Access(pItem);
358 void Release(CFlusherCacheData * pItem)
360 ATLASSERT(pItem);
361 m_First.Release(pItem);
362 m_Second.Release(pItem);
365 CFlusherCacheData * GetStart() const
367 if (m_bWhich)
368 return m_First.GetStart();
369 else
370 return m_Second.GetStart();
373 CFlusherCacheData * GetNext(CFlusherCacheData * pCur) const
375 if (m_bWhich)
376 return m_First.GetNext(pCur);
377 else
378 return m_Second.GetNext(pCur);
382 struct CCullerCacheData
384 CCullerCacheData()
386 pNext = NULL;
387 pPrev = NULL;
388 nLifespan = 0;
390 CCullerCacheData *pNext;
391 CCullerCacheData *pPrev;
392 ULONGLONG nLifespan;
393 CFileTime cftExpireTime;
396 class CNoExpireCuller
398 public:
399 void Add(CCullerCacheData * /*pItem*/) { }
400 void Commit(CCullerCacheData * /*pItem*/) { }
401 void Access(CCullerCacheData * /*pItem*/) { }
402 void Remove(CCullerCacheData * /*pItem*/) { }
403 void Start() { }
404 BOOL IsExpired(CCullerCacheData * /*pItem*/) { return FALSE; }
405 CCullerCacheData * GetExpired() { return NULL; }
406 void Release(CCullerCacheData * /*pItem*/){}
410 class CExpireCuller
412 public:
413 CFileTime m_cftCurrent;
414 CCullerCacheData *pHead;
415 CCullerCacheData *pTail;
417 CExpireCuller()
419 pHead = NULL;
420 pTail = NULL;
423 // Element is being added -- perform necessary initialization
424 void Add(CCullerCacheData * pItem)
426 (pItem);
427 ATLASSERT(pItem);
430 // Expiration data has been set -- add to main list
431 // Head is the first item to expire
432 // a FILETIME of 0 indicates that the item should never expire
433 void Commit(CCullerCacheData * pItem)
435 ATLENSURE(pItem);
436 if (!pHead)
438 pHead = pItem;
439 pTail = pItem;
440 pItem->pNext = NULL;
441 pItem->pPrev = NULL;
442 return;
445 if (CFileTime(pItem->cftExpireTime) == 0)
447 pTail->pNext = pItem;
448 pItem->pPrev = pTail;
449 pItem->pNext = NULL;
450 pTail = pItem;
451 return;
454 CCullerCacheData * pMark = pHead;
455 while (pMark && (pMark->cftExpireTime < pItem->cftExpireTime))
456 pMark = pMark->pNext;
458 if (pMark) // An entry was found that expires after the added entry
460 CCullerCacheData *pPrev = pMark->pPrev;
461 if (pPrev)
462 pPrev->pNext = pItem;
463 else
464 pHead = pItem;
466 pItem->pNext = pMark;
467 pItem->pPrev = pPrev;
468 pMark->pPrev = pItem;
470 else // Ran out of items -- put it on the tail
472 if (pTail)
473 pTail->pNext = pItem;
474 pItem->pPrev = pTail;
475 pItem->pNext = NULL;
476 pTail = pItem;
480 void Access(CCullerCacheData * /*pItem*/)
484 void Release(CCullerCacheData * /*pItem*/)
488 void Remove(CCullerCacheData * pItem)
490 ATLENSURE(pItem);
491 CCullerCacheData *pPrev = pItem->pPrev;
492 CCullerCacheData *pNext = pItem->pNext;
494 if (pPrev)
495 pPrev->pNext = pNext;
496 else
497 pHead = pNext;
499 if (pNext)
500 pNext->pPrev = pPrev;
501 else
502 pTail = pPrev;
506 // About to start culling
507 void Start()
509 m_cftCurrent = CFileTime::GetCurrentTime();
512 BOOL IsExpired(CCullerCacheData *pItem)
514 if ((pItem->cftExpireTime != 0) &&
515 m_cftCurrent > pItem->cftExpireTime)
516 return TRUE;
518 return FALSE;
521 // Get the next expired entry
522 CCullerCacheData * GetExpired()
524 if (!pHead)
525 return NULL;
526 if (IsExpired(pHead))
527 return pHead;
529 return NULL;
533 class CLifetimeCuller : public CExpireCuller
535 public:
536 void Add(CCullerCacheData * pItem)
538 ATLENSURE(pItem);
539 pItem->nLifespan = 0;
540 CExpireCuller::Add(pItem);
543 void Commit(CCullerCacheData * pItem)
545 ATLENSURE(pItem);
546 if (pItem->nLifespan == 0)
547 pItem->cftExpireTime = 0;
548 else
549 pItem->cftExpireTime = CFileTime(CFileTime::GetCurrentTime().GetTime() + pItem->nLifespan);
550 CExpireCuller::Commit(pItem);
553 void Access(CCullerCacheData * pItem)
555 ATLASSERT(pItem);
556 CExpireCuller::Remove(pItem);
557 Commit(pItem);
560 CCullerCacheData * GetExpired()
562 return static_cast<CCullerCacheData *>(CExpireCuller::GetExpired());
566 template <__int64 ftLifespan>
567 class CFixedLifetimeCuller : public CExpireCuller
569 public:
570 void Commit(CCullerCacheData * pItem)
572 ATLASSERT(pItem);
573 __int64 nLifeSpan = ftLifespan;
574 if (nLifeSpan == 0)
575 pItem->cftExpireTime = 0;
576 else
577 pItem->cftExpireTime = CFileTime::GetCurrentTime() + CFileTimeSpan(ftLifespan);
579 CExpireCuller::Commit(pItem);
582 void Access(CCullerCacheData * pItem)
584 ATLASSERT(pItem);
585 CExpireCuller::Remove(pItem);
586 Commit(pItem);
589 CCullerCacheData * GetExpired()
591 return static_cast<CCullerCacheData *>(CExpireCuller::GetExpired());
596 template <class CFirst, class CSecond>
597 class COrCullers
599 CFirst m_First;
600 CSecond m_Second;
601 public:
602 void Add(CCullerCacheData * pItem)
604 m_First.Add(pItem);
605 m_Second.Add(pItem);
608 void Access(CCullerCacheData * pItem)
610 m_First.Access(pItem);
611 m_Second.Access(pItem);
614 void Remove(CCullerCacheData * pItem)
616 m_First.Remove(pItem);
617 m_Second.Remove(pItem);
620 void Start()
622 m_First.Start();
623 m_Second.Start();
626 void Release(CCullerCacheData *pItem)
628 m_First.Release(pItem);
629 m_Second.Release(pItem);
632 void Commit(CCullerCacheData * pItem)
634 m_First.Commit(pItem);
635 m_Second.Commit(pItem);
637 CCullerCacheData * GetExpired()
639 CCullerCacheData *pItem = m_First.GetExpired();
640 if (!pItem)
641 pItem = m_Second.GetExpired();
643 return pItem;
646 BOOL IsExpired(CCullerCacheData * pItem)
648 return (m_First.IsExpired(pItem) || m_Second.IsExpired(pItem));
653 //CMemoryCacheBase
654 // Description:
655 // This class provides the implementation of a generic cache that stores
656 // elements in memory. CMemoryCacheBase uses the CCacheDataBase generic
657 // cache element structure to hold items in the cache. The cache is
658 // implemented using the CAtlMap map class. CMemoryCache uses a wide
659 // character string as it's Key type to identify entries. Entries must
660 // have unique key values. If you try to add an entry with a key that
661 // is exactly the same as an existing key, the existing entry will be
662 // overwritten.
664 // Template Parameters:
665 // T: The class that inherits from this class. This class must implement
666 // void OnDestroyEntry(NodeType *pEntry);
667 // DataType: Specifies the type of the element to be stored in the memory
668 // cache such as CString or void*
669 // NodeInfo: Specifies any additional data that should be stored in each item
670 // in the cache
671 // keyType, keyTrait : specifies the key type and traits (see CAtlMap)
672 // Flusher : the class responsible for determining which data should be flushed
673 // when the cache is at a configuration limit
674 // Culler : the class responsible for determining which data should be removed
675 // from the cache due to expiration
676 // SyncClass:Specifies the class that will be used for thread synchronization
677 // when accessing the cache. The class interface for SyncClass must
678 // be identical to that of CComCriticalSection (see atlbase.h)
679 // StatClass: Class used to contain statistics about this cache.
680 template <class T,
681 class DataType,
682 class NodeInfo=CCacheDataBase,
683 class keyType=CFixedStringKey,
684 class KeyTrait=CStringElementTraits<CFixedStringKey >,
685 class Flusher=COldFlusher,
686 class Culler=CExpireCuller,
687 class SyncClass=CComCriticalSection,
688 class StatClass=CStdStatClass >
689 class CMemoryCacheBase
691 protected:
692 typedef keyType keytype;
693 struct NodeType : public __CACHEITEM,
694 public NodeInfo,
695 public CFlusherCacheData,
696 public CCullerCacheData
698 NodeType()
700 pos = NULL;
701 dwSize = 0;
702 dwRef = 0;
705 DataType Data;
706 POSITION pos;
707 DWORD dwSize;
708 DWORD dwRef;
711 typedef CAtlMap<keyType, NodeType *, KeyTrait> mapType;
712 SyncClass m_syncObj;
713 StatClass m_statObj;
714 Flusher m_flusher;
715 Culler m_culler;
717 //memory cache configuration parameters
718 DWORD m_dwMaxAllocationSize;
719 DWORD m_dwMaxEntries;
721 BOOL m_bInitialized;
722 public:
724 mapType m_hashTable;
725 CMemoryCacheBase() :
726 m_dwMaxAllocationSize(0xFFFFFFFF),
727 m_dwMaxEntries(0xFFFFFFFF),
728 m_bInitialized(FALSE)
733 //Initializes the cache and the cache synchronization object
734 //Also the performance monitoring
735 HRESULT Initialize()
737 if (m_bInitialized)
738 return HRESULT_FROM_WIN32(ERROR_ALREADY_INITIALIZED);
739 HRESULT hr;
740 hr = m_syncObj.Init();
742 if (hr == S_OK)
743 hr = m_statObj.Initialize();
745 m_bInitialized = TRUE;
747 return hr;
750 //removes all entries whether or not they are initialized.
751 HRESULT Uninitialize()
753 if (!m_bInitialized)
754 return S_OK;
756 //clear out the hash table
757 HRESULT hr = m_syncObj.Lock();
758 if (FAILED(hr))
759 return hr;
761 RemoveAllEntries();
762 m_statObj.Uninitialize();
764 m_syncObj.Unlock();
765 m_syncObj.Term();
767 m_bInitialized = FALSE;
769 return S_OK;
772 //Adds an entry to the cache.
773 //Also, adds an initial reference on the entry if phEntry is not NULL
774 HRESULT AddEntry(
775 const keyType &Key, //key for entry
776 const DataType &data, //See the DataType template parameter
777 DWORD dwSize, //Size of memory to be stored in the cache
778 HCACHEITEM *phEntry = NULL //out pointer that will contain a handle to the new
779 //cache entry on success.
782 _ATLTRY
784 ATLASSUME(m_bInitialized);
786 CAutoPtr<NodeType> spEntry(new NodeType);
788 if (!spEntry)
789 return E_OUTOFMEMORY;
791 NodeType *pEntry = spEntry;
793 //fill entry
794 if (phEntry)
796 *phEntry = static_cast<HCACHEITEM>(pEntry);
797 pEntry->dwRef++;
799 pEntry->Data = data;
800 pEntry->dwSize = dwSize;
802 CComCritSecLock<SyncClass> lock(m_syncObj, false);
804 HRESULT hr = lock.Lock();
805 if (FAILED(hr))
807 return hr;
810 POSITION pos = (POSITION)m_hashTable.Lookup(Key);
812 if (pos != NULL)
814 RemoveAt(pos, FALSE);
815 m_hashTable.GetValueAt(pos) = pEntry;
817 else
819 pos = m_hashTable.SetAt(Key, pEntry);
821 spEntry.Detach();
823 pEntry->pos = pos;
824 m_statObj.AddElement(dwSize);
825 m_flusher.Add(pEntry);
826 m_culler.Add(pEntry);
828 lock.Unlock();
830 if (!phEntry)
831 return CommitEntry(static_cast<HCACHEITEM>(pEntry));
833 return S_OK;
835 _ATLCATCHALL()
837 return E_FAIL;
841 // Commits the entry to the cache
842 HRESULT CommitEntry(const HCACHEITEM hEntry)
844 ATLASSUME(m_bInitialized);
845 if (!hEntry || hEntry == INVALID_HANDLE_VALUE)
846 return E_INVALIDARG;
848 HRESULT hr = m_syncObj.Lock();
849 if (FAILED(hr))
851 return hr;
854 NodeType *pEntry = static_cast<NodeType *>(hEntry);
855 m_culler.Commit(pEntry);
856 m_syncObj.Unlock();
857 return S_OK;
860 // Looks up an entry and returns a handle to it,
861 // also updates access count and reference count
862 HRESULT LookupEntry(const keyType &Key, HCACHEITEM * phEntry)
864 ATLASSUME(m_bInitialized);
865 HRESULT hr = m_syncObj.Lock();
866 if (FAILED(hr))
868 return hr;
871 hr = E_FAIL;
873 POSITION pos = (POSITION)m_hashTable.Lookup(Key);
874 if (pos != NULL)
876 NodeType * pEntry = m_hashTable.GetValueAt(pos);
877 m_flusher.Access(pEntry);
878 m_culler.Access(pEntry);
879 if (phEntry)
881 pEntry->dwRef++;
882 *phEntry = static_cast<HCACHEITEM>(pEntry);
885 m_statObj.Hit();
887 hr = S_OK;
889 else
891 *phEntry = NULL;
892 m_statObj.Miss();
894 m_syncObj.Unlock();
896 return hr;
899 // Gets the data based on the handle. Is thread-safe as long as there is a
900 // reference on the data
901 HRESULT GetEntryData(const HCACHEITEM hEntry, DataType *pData, DWORD *pdwSize) const
903 ATLASSUME(m_bInitialized);
904 ATLASSERT(pData != NULL || pdwSize != NULL); // At least one should not be NULL
906 if (!hEntry || hEntry == INVALID_HANDLE_VALUE)
907 return E_INVALIDARG;
909 NodeType * pEntry = static_cast<NodeType *>(hEntry);
910 if (pData)
911 *pData = pEntry->Data;
912 if (pdwSize)
913 *pdwSize = pEntry->dwSize;
915 return S_OK;
918 // Unreferences the entry based on the handle
919 DWORD ReleaseEntry(const HCACHEITEM hEntry)
921 ATLASSUME(m_bInitialized);
922 if (!hEntry || hEntry == INVALID_HANDLE_VALUE)
923 return (DWORD)-1;
925 HRESULT hr = m_syncObj.Lock();
926 if (FAILED(hr))
927 return (DWORD)-1;
929 NodeType * pEntry = static_cast<NodeType *>(hEntry);
930 m_flusher.Release(pEntry);
931 m_culler.Release(pEntry);
932 ATLASSERT(pEntry->dwRef > 0);
934 DWORD dwRef = --pEntry->dwRef;
935 if ((pEntry->pos == NULL) && (pEntry->dwRef == 0))
936 InternalRemoveEntry(pEntry);
938 m_syncObj.Unlock();
940 return dwRef;
943 // Increments the entry's reference count
944 DWORD AddRefEntry(const HCACHEITEM hEntry)
946 ATLASSUME(m_bInitialized);
947 if (!hEntry || hEntry == INVALID_HANDLE_VALUE)
948 return (DWORD)-1;
950 HRESULT hr = m_syncObj.Lock();
951 if (FAILED(hr))
952 return (DWORD)-1;
954 NodeType * pEntry = static_cast<NodeType *>(hEntry);
955 m_flusher.Access(pEntry);
956 m_culler.Access(pEntry);
957 DWORD dwRef = ++pEntry->dwRef;
958 m_syncObj.Unlock();
960 return dwRef;
963 // Removes an entry from the cache regardless of whether or
964 // not it has expired. If there are references, it detaches
965 // the entry so that future lookups will fail, and when
966 // the ref count drops to zero, it will be deleted
967 HRESULT RemoveEntryByKey(const keyType &Key)
969 ATLASSUME(m_bInitialized);
970 HCACHEITEM hEntry;
971 HRESULT hr = LookupEntry(Key, &hEntry);
972 if (hr == S_OK)
973 hr = RemoveEntry(hEntry);
975 return hr;
978 // Removes the element from the cache. If there are still
979 // references, then the entry is detached.
980 HRESULT RemoveEntry(const HCACHEITEM hEntry)
982 ATLASSUME(m_bInitialized);
983 if (!hEntry || hEntry == INVALID_HANDLE_VALUE)
984 return E_INVALIDARG;
986 _ATLTRY
988 CComCritSecLock<SyncClass> lock(m_syncObj, false);
990 HRESULT hr = lock.Lock();
991 if (FAILED(hr))
992 return hr;
994 NodeType * pEntry = static_cast<NodeType *>(hEntry);
995 m_flusher.Release(pEntry);
996 m_culler.Release(pEntry);
997 ATLASSERT(pEntry->dwRef > 0);
998 pEntry->dwRef--;
999 if (pEntry->pos)
1000 RemoveAt(pEntry->pos, TRUE);
1001 else if ((long)pEntry->dwRef == 0)
1002 InternalRemoveEntry(pEntry);
1003 lock.Unlock();
1005 _ATLCATCHALL()
1007 return E_OUTOFMEMORY;
1010 return S_OK;
1013 // CullEntries removes all expired items
1014 HRESULT CullEntries()
1016 ATLASSUME(m_bInitialized);
1018 _ATLTRY
1020 CComCritSecLock<SyncClass> lock(m_syncObj, false);
1021 HRESULT hr = lock.Lock();
1022 if (FAILED(hr))
1023 return hr;
1025 m_culler.Start();
1027 while (NodeType *pNode = static_cast<NodeType *>(m_culler.GetExpired()))
1028 RemoveAt(pNode->pos, TRUE);
1030 lock.Unlock();
1032 _ATLCATCHALL()
1034 return E_OUTOFMEMORY;
1037 return S_OK;
1040 // FlushEntries reduces the cache to meet the configuration requirements
1041 HRESULT FlushEntries()
1043 ATLASSUME(m_bInitialized);
1044 HRESULT hr = CullEntries();
1045 if (FAILED(hr))
1046 return hr;
1048 _ATLTRY
1050 CComCritSecLock<SyncClass> lock(m_syncObj, false);
1051 hr = lock.Lock();
1052 if (FAILED(hr))
1053 return hr;
1055 NodeType * pNode = static_cast<NodeType *>(m_flusher.GetStart());
1057 while (pNode &&
1058 (((m_statObj.GetCurrentEntryCount() > m_dwMaxEntries)) ||
1059 ((m_statObj.GetCurrentAllocSize() > m_dwMaxAllocationSize))))
1061 NodeType *pNext = static_cast<NodeType *>(m_flusher.GetNext(pNode));
1063 if (pNode->dwRef == 0)
1064 RemoveAt(pNode->pos, TRUE);
1066 pNode = pNext;
1068 lock.Unlock();
1070 _ATLCATCHALL()
1072 return E_OUTOFMEMORY;
1075 return S_OK;
1078 HRESULT STDMETHODCALLTYPE SetMaxAllowedSize(DWORD dwSize)
1080 m_dwMaxAllocationSize = dwSize;
1081 return S_OK;
1084 HRESULT STDMETHODCALLTYPE GetMaxAllowedSize(DWORD *pdwSize)
1086 if (!pdwSize)
1087 return E_POINTER;
1088 *pdwSize = m_dwMaxAllocationSize;
1089 return S_OK;
1092 HRESULT STDMETHODCALLTYPE SetMaxAllowedEntries(DWORD dwSize)
1094 m_dwMaxEntries = dwSize;
1095 return S_OK;
1098 HRESULT STDMETHODCALLTYPE GetMaxAllowedEntries(DWORD *pdwSize)
1100 if (!pdwSize)
1101 return E_POINTER;
1102 *pdwSize = m_dwMaxEntries;
1103 return S_OK;
1107 HRESULT ResetCache()
1109 ATLASSUME(m_bInitialized);
1110 HRESULT hr = E_UNEXPECTED;
1111 if (SUCCEEDED(ClearStats()))
1112 hr = RemoveAllEntries();
1113 return hr;
1116 HRESULT ClearStats()
1118 m_statObj.ResetCounters();
1119 return S_OK;
1122 HRESULT RemoveAllEntries()
1124 ATLASSUME(m_bInitialized);
1125 HRESULT hr = m_syncObj.Lock();
1126 if (FAILED(hr))
1127 return hr;
1129 m_hashTable.DisableAutoRehash();
1130 POSITION pos = m_hashTable.GetStartPosition();
1131 POSITION oldpos;
1132 while (pos != NULL)
1134 oldpos = pos;
1135 m_hashTable.GetNext(pos);
1136 RemoveAt(oldpos, TRUE);
1138 m_hashTable.EnableAutoRehash();
1139 m_syncObj.Unlock();
1141 return S_OK;
1144 protected:
1146 // Checks to see if the cache can accommodate any new entries within
1147 // its allocation and entry count limits.
1148 bool CanAddEntry(DWORD dwSizeToAdd)
1150 return CheckAlloc(dwSizeToAdd) && CheckEntryCount(1);
1153 // Checks to see if the cache can accommodate dwSizeToAdd additional
1154 // allocation within its allocation limit.
1155 bool CheckAlloc(DWORD dwSizeToAdd)
1157 if (m_dwMaxAllocationSize == 0xFFFFFFFF)
1158 return true; //max allocation size setting hasn't been set
1159 DWORD dwNew = m_statObj.GetCurrentAllocSize() + dwSizeToAdd;
1160 return dwNew < m_dwMaxAllocationSize;
1164 // Checks to see if the cache can accommodate dwNumEntriesToAdd
1165 // additional entries within its limits.
1166 bool CheckEntryCount(DWORD dwNumEntriesToAdd)
1168 if (m_dwMaxEntries == 0xFFFFFFFF)
1169 return true; //max entry size hasn't been set
1170 DWORD dwNew = m_statObj.GetCurrentEntryCount() + dwNumEntriesToAdd;
1171 return dwNew < m_dwMaxEntries;
1175 protected:
1176 // Takes the element at pos in the hash table and removes it from
1177 // the cache. If there are no references, then the entry is
1178 // deleted, otherwise it is deleted by ReleaseEntry when the
1179 // refcount goes to zero.
1180 HRESULT RemoveAt(POSITION pos, BOOL bDelete)
1182 HRESULT hr = S_OK;
1183 ATLASSERT(pos != NULL);
1184 NodeType * pEntry = m_hashTable.GetValueAt(pos);
1185 m_flusher.Remove(pEntry);
1186 m_culler.Remove(pEntry);
1187 if (bDelete)
1188 m_hashTable.RemoveAtPos(pos);
1190 if ((long)pEntry->dwRef == 0)
1191 hr = InternalRemoveEntry(pEntry);
1192 else
1193 pEntry->pos = NULL;
1195 return S_OK;
1198 // Does the actual destruction of the node. Deletes the
1199 // NodeType struct and calls the inherited class's
1200 // OnDestroyEntry function, where other necessary destruction
1201 // can take place. Also updates the cache statistics.
1202 // Inherited classes should call RemoveAt unless the element's
1203 // refcount is zero and it has been removed from the
1204 // culler and flusher lists.
1205 HRESULT InternalRemoveEntry(NodeType * pEntry)
1207 ATLENSURE(pEntry != NULL);
1209 T* pT = static_cast<T*>(this);
1211 ATLASSERT((long)pEntry->dwRef == 0);
1213 pT->OnDestroyEntry(pEntry);
1215 m_statObj.ReleaseElement(pEntry->dwSize);
1217 delete pEntry;
1219 return S_OK;
1221 }; // CMemoryCacheBase
1223 class CCacheDataBase
1227 struct CCacheDataEx : public CCacheDataBase
1229 CCacheDataEx()
1231 hInstance = NULL;
1232 pClient = NULL;
1235 HINSTANCE hInstance;
1236 IMemoryCacheClient * pClient;
1240 template <typename DataType,
1241 class StatClass=CStdStatClass,
1242 class FlushClass=COldFlusher,
1243 class keyType=CFixedStringKey, class KeyTrait=CStringElementTraits<CFixedStringKey >,
1244 class SyncClass=CComCriticalSection,
1245 class CullClass=CExpireCuller >
1246 class CMemoryCache:
1247 public CMemoryCacheBase<CMemoryCache<DataType, StatClass, FlushClass, keyType, KeyTrait, SyncClass, CullClass>, DataType, CCacheDataEx,
1248 keyType, KeyTrait, FlushClass, CullClass, SyncClass, StatClass>
1250 protected:
1251 CComPtr<IServiceProvider> m_spServiceProv;
1252 CComPtr<IDllCache> m_spDllCache;
1253 typedef CMemoryCacheBase<CMemoryCache<DataType, StatClass, FlushClass, keyType, KeyTrait, SyncClass, CullClass>, DataType, CCacheDataEx,
1254 keyType, KeyTrait, FlushClass, CullClass, SyncClass, StatClass> baseClass;
1255 public:
1256 virtual ~CMemoryCache()
1260 HRESULT Initialize(IServiceProvider * pProvider)
1262 baseClass::Initialize();
1263 m_spServiceProv = pProvider;
1264 if (pProvider)
1265 return m_spServiceProv->QueryService(__uuidof(IDllCache), __uuidof(IDllCache), (void**)&m_spDllCache);
1266 else
1267 return S_OK;
1270 HRESULT AddEntry(
1271 const keyType &Key,
1272 const DataType &data,
1273 DWORD dwSize,
1274 FILETIME * pftExpireTime = NULL,
1275 HINSTANCE hInstance = NULL,
1276 IMemoryCacheClient * pClient = NULL,
1277 HCACHEITEM *phEntry = NULL
1280 _ATLTRY
1282 HRESULT hr;
1283 NodeType * pEntry = NULL;
1284 hr = baseClass::AddEntry(Key, data, dwSize, (HCACHEITEM *)&pEntry);
1285 if (hr != S_OK)
1286 return hr;
1288 pEntry->hInstance = hInstance;
1289 pEntry->pClient = pClient;
1290 if (pftExpireTime)
1291 pEntry->cftExpireTime = *pftExpireTime;
1293 if (hInstance && m_spDllCache)
1294 m_spDllCache->AddRefModule(hInstance);
1296 baseClass::CommitEntry(static_cast<HCACHEITEM>(pEntry));
1298 if (phEntry)
1299 *phEntry = static_cast<HCACHEITEM>(pEntry);
1300 else
1301 baseClass::ReleaseEntry(static_cast<HCACHEITEM>(pEntry));
1303 return S_OK;
1305 _ATLCATCHALL()
1307 return E_FAIL;
1311 virtual void OnDestroyEntry(const NodeType * pEntry)
1313 ATLASSERT(pEntry);
1314 if (!pEntry)
1315 return;
1317 if (pEntry->pClient)
1318 pEntry->pClient->Free((void *)&pEntry->Data);
1319 if (pEntry->hInstance && m_spDllCache)
1320 m_spDllCache->ReleaseModule(pEntry->hInstance);
1322 }; // CMemoryCache
1324 // CStdStatData - contains the data that CStdStatClass keeps track of
1325 #define ATL_PERF_CACHE_OBJECT 100
1327 struct CPerfStatObject : public CPerfObject
1329 DECLARE_PERF_CATEGORY(CPerfStatObject, ATL_PERF_CACHE_OBJECT, IDS_PERFMON_CACHE, IDS_PERFMON_CACHE_HELP, -1);
1331 BEGIN_COUNTER_MAP(CPerfStatObject)
1332 DEFINE_COUNTER(m_nHitCount, IDS_PERFMON_HITCOUNT, IDS_PERFMON_HITCOUNT_HELP, PERF_COUNTER_RAWCOUNT, -1)
1333 DEFINE_COUNTER(m_nMissCount, IDS_PERFMON_MISSCOUNT, IDS_PERFMON_MISSCOUNT_HELP, PERF_COUNTER_RAWCOUNT, -1)
1334 DEFINE_COUNTER(m_nCurrentAllocations, IDS_PERFMON_CURRENTALLOCATIONS, IDS_PERFMON_CURRENTALLOCATIONS_HELP, PERF_COUNTER_RAWCOUNT, -3)
1335 DEFINE_COUNTER(m_nMaxAllocations, IDS_PERFMON_MAXALLOCATIONS, IDS_PERFMON_MAXALLOCATIONS_HELP, PERF_COUNTER_RAWCOUNT, -3)
1336 DEFINE_COUNTER(m_nCurrentEntries, IDS_PERFMON_CURRENTENTRIES, IDS_PERFMON_CURRENTENTRIES_HELP, PERF_COUNTER_RAWCOUNT, -1)
1337 DEFINE_COUNTER(m_nMaxEntries, IDS_PERFMON_MAXENTRIES, IDS_PERFMON_MAXENTRIES_HELP, PERF_COUNTER_RAWCOUNT, -1)
1338 END_COUNTER_MAP()
1340 long m_nHitCount;
1341 long m_nMissCount;
1342 long m_nCurrentAllocations;
1343 long m_nMaxAllocations;
1344 long m_nCurrentEntries;
1345 long m_nMaxEntries;
1348 // CCachePerfMon - the interface to CPerfMon, with associated definitions
1349 class CCachePerfMon : public CPerfMon
1351 public:
1352 BEGIN_PERF_MAP(_T("ATL Server:Cache"))
1353 CHAIN_PERF_CATEGORY(CPerfStatObject)
1354 END_PERF_MAP()
1358 //CStdStatClass
1359 // Description
1360 // This class provides the implementation of a standard cache statistics accounting class
1361 class CStdStatClass
1363 protected:
1364 CPerfStatObject* m_pStats;
1365 CPerfStatObject m_stats;
1367 public:
1369 CStdStatClass()
1371 m_pStats = &m_stats;
1374 // This function is not thread safe by design
1375 HRESULT Initialize(CPerfStatObject* pStats = NULL)
1377 if (pStats)
1378 m_pStats = pStats;
1379 else
1380 m_pStats = &m_stats;
1382 ResetCounters();
1383 return S_OK;
1386 // This function is not thread safe by design
1387 HRESULT Uninitialize()
1389 m_pStats = &m_stats;
1390 return S_OK;
1393 void Hit()
1395 InterlockedIncrement(&m_pStats->m_nHitCount);
1398 void Miss()
1400 InterlockedIncrement(&m_pStats->m_nMissCount);
1403 void AddElement(DWORD dwBytes)
1405 DWORD nCurrentEntries = InterlockedIncrement(&m_pStats->m_nCurrentEntries);
1406 AtlInterlockedUpdateMax(nCurrentEntries, &m_pStats->m_nMaxEntries);
1408 DWORD nCurrentAllocations = dwBytes + AtlInterlockedExchangeAdd(&m_pStats->m_nCurrentAllocations, dwBytes);
1409 AtlInterlockedUpdateMax(nCurrentAllocations, &m_pStats->m_nMaxAllocations);
1412 void ReleaseElement(DWORD dwBytes)
1414 InterlockedDecrement(&m_pStats->m_nCurrentEntries);
1415 AtlInterlockedExchangeAdd(&m_pStats->m_nCurrentAllocations, -((long)dwBytes));
1418 DWORD GetHitCount()
1420 return m_pStats->m_nHitCount;
1423 DWORD GetMissCount()
1425 return m_pStats->m_nMissCount;
1428 DWORD GetCurrentAllocSize()
1430 return m_pStats->m_nCurrentAllocations;
1433 DWORD GetMaxAllocSize()
1435 return m_pStats->m_nMaxAllocations;
1438 DWORD GetCurrentEntryCount()
1440 return m_pStats->m_nCurrentEntries;
1443 DWORD GetMaxEntryCount()
1445 return m_pStats->m_nMaxEntries;
1448 void ResetCounters()
1450 m_pStats->m_nHitCount = 0;
1451 m_pStats->m_nMissCount = 0;
1452 m_pStats->m_nCurrentAllocations = 0;
1453 m_pStats->m_nMaxAllocations = 0;
1454 m_pStats->m_nCurrentEntries = 0;
1455 m_pStats->m_nMaxEntries = 0;
1457 }; // CStdStatClass
1460 // CNoStatClass
1461 // This is a noop stat class
1462 class CNoStatClass
1464 public:
1465 HRESULT Initialize(){ return S_OK; }
1466 HRESULT Uninitialize(){ return S_OK; }
1467 void Hit(){ }
1468 void Miss(){ }
1469 void AddElement(DWORD){ }
1470 void ReleaseElement(DWORD){ }
1471 DWORD GetHitCount(){ return 0; }
1472 DWORD GetMissCount(){ return 0; }
1473 DWORD GetCurrentAllocSize(){ return 0; }
1474 DWORD GetMaxAllocSize(){ return 0; }
1475 DWORD GetCurrentEntryCount(){ return 0; }
1476 DWORD GetMaxEntryCount(){ return 0; }
1477 void ResetCounters(){ }
1478 }; // CNoStatClass
1481 //CPerfStatClass
1482 // Description
1483 // This class provides the implementation of a cache statistics gathering class
1484 // with PerfMon support
1485 class CPerfStatClass : public CStdStatClass
1487 CPerfStatObject * m_pPerfObject;
1488 CCachePerfMon m_PerfMon;
1490 public:
1492 HRESULT Initialize(__in_z_opt LPWSTR szName=NULL)
1494 HRESULT hr;
1495 WCHAR szPath[MAX_PATH];
1497 if (!szName)
1499 // default name is the name of the module
1500 // we don't care about possible truncation if longer than max_path
1501 // we just need an identifier
1502 HINSTANCE hInst = _AtlBaseModule.GetModuleInstance();
1503 if (::GetModuleFileNameW(hInst, szPath, MAX_PATH) == 0)
1505 return E_FAIL;
1507 szPath[MAX_PATH-1] = 0;
1508 szName = szPath;
1511 m_pPerfObject = NULL;
1512 ATLTRACE(atlTraceCache, 2, _T("Initializing m_PerfMon\n"));
1513 hr = m_PerfMon.Initialize();
1514 if (SUCCEEDED(hr))
1516 CPerfLock lock(&m_PerfMon);
1517 if (FAILED(hr = lock.GetStatus()))
1519 return hr;
1522 hr = m_PerfMon.CreateInstance(ATL_PERF_CACHE_OBJECT, 0, szName, reinterpret_cast<CPerfObject**>(&m_pPerfObject));
1523 if (FAILED(hr))
1525 return hr;
1528 CStdStatClass::Initialize(m_pPerfObject);
1530 else
1531 ATLASSUME(m_pPerfObject == NULL);
1533 return hr;
1536 HRESULT Uninitialize()
1538 CStdStatClass::Uninitialize();
1540 if (m_pPerfObject != NULL) // Initialized m_pPerfObject successfully above
1542 HRESULT hr = m_PerfMon.ReleaseInstance(m_pPerfObject);
1543 if (hr != S_OK)
1544 return hr;
1546 m_PerfMon.UnInitialize();
1549 return S_OK;
1551 }; // CPerfStatClass
1553 #ifndef ATL_BLOB_CACHE_TIMEOUT
1554 #ifdef _DEBUG
1555 #define ATL_BLOB_CACHE_TIMEOUT 1000
1556 #else
1557 #define ATL_BLOB_CACHE_TIMEOUT 5000
1558 #endif // _DEBUG
1559 #endif // ATL_BLOB_CACHE_TIMEOUT
1562 //CBlobCache
1563 // Description:
1564 // Implements a cache that stores pointers to void. Uses the generic CMemoryCacheBase class
1565 // as the implementation.
1566 template <class MonitorClass,
1567 class StatClass=CStdStatClass,
1568 class SyncObj=CComCriticalSection,
1569 class FlushClass=COldFlusher,
1570 class CullClass=CExpireCuller >
1571 class CBlobCache : public CMemoryCache<void*, StatClass, FlushClass, CFixedStringKey,
1572 CStringElementTraits<CFixedStringKey >, SyncObj, CullClass>,
1573 public IMemoryCache,
1574 public IMemoryCacheControl,
1575 public IMemoryCacheStats,
1576 public IWorkerThreadClient
1578 typedef CMemoryCache<void*, StatClass, FlushClass, CFixedStringKey,
1579 CStringElementTraits<CFixedStringKey>, SyncObj, CullClass> cacheBase;
1581 MonitorClass m_Monitor;
1583 protected:
1584 HANDLE m_hTimer;
1586 public:
1587 CBlobCache() : m_hTimer(NULL)
1591 HRESULT Initialize(IServiceProvider *pProv)
1593 HRESULT hr = cacheBase::Initialize(pProv);
1594 if (FAILED(hr))
1595 return hr;
1596 hr = m_Monitor.Initialize();
1597 if (FAILED(hr))
1598 return hr;
1599 return m_Monitor.AddTimer(ATL_BLOB_CACHE_TIMEOUT,
1600 static_cast<IWorkerThreadClient*>(this), (DWORD_PTR) this, &m_hTimer);
1603 template <class ThreadTraits>
1604 HRESULT Initialize(IServiceProvider *pProv, CWorkerThread<ThreadTraits> *pWorkerThread)
1606 ATLASSERT(pWorkerThread);
1608 HRESULT hr = cacheBase::Initialize(pProv);
1609 if (FAILED(hr))
1610 return hr;
1612 hr = m_Monitor.Initialize(pWorkerThread);
1613 if (FAILED(hr))
1614 return hr;
1616 return m_Monitor.AddTimer(ATL_BLOB_CACHE_TIMEOUT,
1617 static_cast<IWorkerThreadClient*>(this), (DWORD_PTR) this, &m_hTimer);
1620 HRESULT Execute(DWORD_PTR dwParam, HANDLE /*hObject*/)
1622 CBlobCache* pCache = (CBlobCache*)dwParam;
1624 if (pCache)
1625 pCache->Flush();
1626 return S_OK;
1629 HRESULT CloseHandle(HANDLE hObject)
1631 ATLASSUME(m_hTimer == hObject);
1632 m_hTimer = NULL;
1633 ::CloseHandle(hObject);
1634 return S_OK;
1637 virtual ~CBlobCache()
1639 if (m_hTimer)
1641 ATLENSURE(SUCCEEDED(m_Monitor.RemoveHandle(m_hTimer)));
1645 HRESULT Uninitialize()
1647 HRESULT hrMonitor=S_OK;
1648 if (m_hTimer)
1650 hrMonitor=m_Monitor.RemoveHandle(m_hTimer);
1651 m_hTimer = NULL;
1653 HRESULT hrShut=m_Monitor.Shutdown();
1654 HRESULT hrCache=cacheBase::Uninitialize();
1655 if(FAILED(hrMonitor))
1657 return hrMonitor;
1659 if(FAILED(hrShut))
1661 return hrShut;
1663 return hrCache;
1665 // IUnknown methods
1666 HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, void **ppv)
1668 HRESULT hr = E_NOINTERFACE;
1669 if (!ppv)
1670 hr = E_POINTER;
1671 else
1673 if (InlineIsEqualGUID(riid, __uuidof(IUnknown)) ||
1674 InlineIsEqualGUID(riid, __uuidof(IMemoryCache)))
1676 *ppv = (IUnknown *) (IMemoryCache *) this;
1677 AddRef();
1678 hr = S_OK;
1680 if (InlineIsEqualGUID(riid, __uuidof(IMemoryCacheStats)))
1682 *ppv = (IUnknown *) (IMemoryCacheStats*)this;
1683 AddRef();
1684 hr = S_OK;
1686 if (InlineIsEqualGUID(riid, __uuidof(IMemoryCacheControl)))
1688 *ppv = (IUnknown *) (IMemoryCacheControl*)this;
1689 AddRef();
1690 hr = S_OK;
1694 return hr;
1697 ULONG STDMETHODCALLTYPE AddRef()
1699 return 1;
1702 ULONG STDMETHODCALLTYPE Release()
1704 return 1;
1707 // IMemoryCache Methods
1708 HRESULT STDMETHODCALLTYPE Add(LPCSTR szKey, void *pvData, DWORD dwSize,
1709 FILETIME *pftExpireTime,
1710 HINSTANCE hInstClient,
1711 HCACHEITEM *phEntry,
1712 IMemoryCacheClient *pClient)
1714 HRESULT hr = E_FAIL;
1715 //if it's a multithreaded cache monitor we'll let the monitor take care of
1716 //cleaning up the cache so we don't overflow our configuration settings.
1717 //if it's not a threaded cache monitor, we need to make sure we don't
1718 //overflow the configuration settings by adding a new element
1719 if (m_Monitor.GetThreadHandle()==NULL)
1721 if (!cacheBase::CanAddEntry(dwSize))
1723 //flush the entries and check again to see if we can add
1724 cacheBase::FlushEntries();
1725 if (!cacheBase::CanAddEntry(dwSize))
1726 return E_OUTOFMEMORY;
1729 _ATLTRY
1731 hr = cacheBase::AddEntry(szKey, pvData, dwSize,
1732 pftExpireTime, hInstClient, pClient, phEntry);
1733 return hr;
1735 _ATLCATCHALL()
1737 return E_FAIL;
1741 HRESULT STDMETHODCALLTYPE LookupEntry(LPCSTR szKey, HCACHEITEM * phEntry)
1743 return cacheBase::LookupEntry(szKey, phEntry);
1746 HRESULT STDMETHODCALLTYPE GetData(const HCACHEITEM hKey, void **ppvData, DWORD *pdwSize) const
1748 return cacheBase::GetEntryData(hKey, ppvData, pdwSize);
1751 HRESULT STDMETHODCALLTYPE ReleaseEntry(const HCACHEITEM hKey)
1753 return cacheBase::ReleaseEntry(hKey);
1756 HRESULT STDMETHODCALLTYPE RemoveEntry(const HCACHEITEM hKey)
1758 return cacheBase::RemoveEntry(hKey);
1761 HRESULT STDMETHODCALLTYPE RemoveEntryByKey(LPCSTR szKey)
1763 return cacheBase::RemoveEntryByKey(szKey);
1766 HRESULT STDMETHODCALLTYPE Flush()
1768 return cacheBase::FlushEntries();
1772 HRESULT STDMETHODCALLTYPE SetMaxAllowedSize(DWORD dwSize)
1774 return cacheBase::SetMaxAllowedSize(dwSize);
1777 HRESULT STDMETHODCALLTYPE GetMaxAllowedSize(DWORD *pdwSize)
1779 return cacheBase::GetMaxAllowedSize(pdwSize);
1782 HRESULT STDMETHODCALLTYPE SetMaxAllowedEntries(DWORD dwSize)
1784 return cacheBase::SetMaxAllowedEntries(dwSize);
1787 HRESULT STDMETHODCALLTYPE GetMaxAllowedEntries(DWORD *pdwSize)
1789 return cacheBase::GetMaxAllowedEntries(pdwSize);
1792 HRESULT STDMETHODCALLTYPE ResetCache()
1794 return cacheBase::ResetCache();
1797 // IMemoryCacheStats methods
1798 HRESULT STDMETHODCALLTYPE ClearStats()
1800 m_statObj.ResetCounters();
1801 return S_OK;
1804 HRESULT STDMETHODCALLTYPE GetHitCount(DWORD *pdwSize)
1806 if (!pdwSize)
1807 return E_POINTER;
1808 *pdwSize = m_statObj.GetHitCount();
1809 return S_OK;
1812 HRESULT STDMETHODCALLTYPE GetMissCount(DWORD *pdwSize)
1814 if (!pdwSize)
1815 return E_POINTER;
1816 *pdwSize = m_statObj.GetMissCount();
1817 return S_OK;
1820 HRESULT STDMETHODCALLTYPE GetMaxAllocSize(DWORD *pdwSize)
1822 if (!pdwSize)
1823 return E_POINTER;
1824 *pdwSize = m_statObj.GetMaxAllocSize();
1825 return S_OK;
1828 HRESULT STDMETHODCALLTYPE GetCurrentAllocSize(DWORD *pdwSize)
1830 if (!pdwSize)
1831 return E_POINTER;
1832 *pdwSize = m_statObj.GetCurrentAllocSize();
1833 return S_OK;
1836 HRESULT STDMETHODCALLTYPE GetMaxEntryCount(DWORD *pdwSize)
1838 if (!pdwSize)
1839 return E_POINTER;
1840 *pdwSize = m_statObj.GetMaxEntryCount();
1841 return S_OK;
1844 HRESULT STDMETHODCALLTYPE GetCurrentEntryCount(DWORD *pdwSize)
1846 if (!pdwSize)
1847 return E_POINTER;
1848 *pdwSize = m_statObj.GetCurrentEntryCount();
1849 return S_OK;
1852 }; // CBlobCache
1856 // CDllCache
1857 // This class manages a cache to handle calls to LoadLibrary
1858 // and FreeLibrary.
1859 // It keeps dlls loaded even after the last call to free library
1860 // a worker thread then calls FreeLibrary on unused dlls
1862 #ifndef ATL_DLL_CACHE_TIMEOUT
1863 #ifdef _DEBUG
1864 #define ATL_DLL_CACHE_TIMEOUT 1000 // 1 sec default for debug builds
1865 #else
1866 #define ATL_DLL_CACHE_TIMEOUT 10*60000 // 10 minute default for retail builds
1867 #endif
1868 #endif
1870 class CNoDllCachePeer
1872 public:
1873 struct DllInfo
1877 BOOL Add(HINSTANCE /*hInst*/, DllInfo * /*pInfo*/)
1879 return TRUE;
1882 void Remove(HINSTANCE /*hInst*/, DllInfo * /*pInfo*/)
1887 // CDllCache
1888 // Implements IDllCache, an interface that is used to load and unload Dlls.
1889 // To use it, construct an instance of a CDllCache and call Initialize.
1890 // The Initialize call has to match with the type of monitor class you
1891 // templatize on. The monitor thread will call IWorkerThreadClient::Execute
1892 // after its timeout expires. Make sure to Uninitialize the object before
1893 // it is destroyed by calling Uninitialize
1895 template <class MonitorClass, class Peer=CNoDllCachePeer>
1896 class CDllCache : public IDllCache,
1897 public IWorkerThreadClient
1899 protected:
1900 CComCriticalSection m_critSec;
1901 CSimpleArray<DLL_CACHE_ENTRY> m_Dlls;
1902 CSimpleArray<typename Peer::DllInfo> m_DllInfos;
1903 MonitorClass m_Monitor;
1904 HANDLE m_hTimer;
1906 void RemoveDllEntry(DLL_CACHE_ENTRY& entry)
1908 ::FreeLibrary(entry.hInstDll);
1909 entry.hInstDll = NULL;
1910 m_Dlls.RemoveAt(m_Dlls.GetSize()-1);
1913 public:
1914 Peer m_Peer;
1916 CDllCache() :
1917 m_hTimer(NULL)
1922 HRESULT Initialize(DWORD dwTimeout=ATL_DLL_CACHE_TIMEOUT)
1924 HRESULT hr = m_critSec.Init();
1925 if (FAILED(hr))
1926 return hr;
1927 hr = m_Monitor.Initialize();
1928 if (FAILED(hr))
1929 return hr;
1930 return m_Monitor.AddTimer(dwTimeout, this, 0, &m_hTimer);
1933 template <class ThreadTraits>
1934 HRESULT Initialize(CWorkerThread<ThreadTraits> *pWorkerThread,
1935 DWORD dwTimeout=ATL_DLL_CACHE_TIMEOUT)
1937 HRESULT hr = m_critSec.Init();
1938 if (FAILED(hr))
1939 return hr;
1940 hr = m_Monitor.Initialize(pWorkerThread);
1941 if (FAILED(hr))
1942 return hr;
1943 return m_Monitor.AddTimer(dwTimeout, this, 0, &m_hTimer);
1946 HRESULT Uninitialize()
1948 HRESULT hr = S_OK;
1949 HRESULT hrLatest = S_OK;
1950 if (m_hTimer)
1952 hrLatest=m_Monitor.RemoveHandle(m_hTimer);
1953 if(FAILED(hrLatest) && SUCCEEDED(hr))
1955 hr=hrLatest;
1957 m_hTimer = NULL;
1959 m_Monitor.Shutdown();
1961 // free all the libraries we've cached
1962 int nLen = m_Dlls.GetSize();
1963 for (int i=0; i<nLen; i++)
1965 DLL_CACHE_ENTRY& entry = m_Dlls[i];
1966 ATLASSERT(entry.dwRefs == 0);
1967 BOOL bRet = ::FreeLibrary(entry.hInstDll);
1969 if (!bRet)
1971 hrLatest = AtlHresultFromLastError();
1972 if(FAILED(hrLatest) && SUCCEEDED(hr))
1974 hr=hrLatest;
1976 ATLTRACE(atlTraceCache, 0, _T("Free library failed on shutdown of dll cache : hr = 0x%08x)"), hr);
1979 m_Dlls.RemoveAll();
1980 m_critSec.Term();
1981 return hr;
1985 // IUnknown methods
1986 HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, void **ppv)
1988 if (!ppv)
1989 return E_POINTER;
1990 if (InlineIsEqualGUID(riid, __uuidof(IUnknown)) ||
1991 InlineIsEqualGUID(riid, __uuidof(IDllCache)))
1993 *ppv = (IUnknown *) this;
1994 AddRef();
1995 return S_OK;
1997 return E_NOINTERFACE;
2000 ULONG STDMETHODCALLTYPE AddRef()
2002 return 1;
2005 ULONG STDMETHODCALLTYPE Release()
2007 return 1;
2010 // IDllCache methods
2011 HINSTANCE Load(LPCSTR szDllName, void *pPeerInfo) throw(...)
2013 HRESULT hr = m_critSec.Lock();
2014 if (FAILED(hr))
2015 return NULL;
2017 int nLen = m_Dlls.GetSize();
2018 for (int i=0; i<nLen; i++)
2020 DLL_CACHE_ENTRY& entry = m_Dlls[i];
2021 if (!_stricmp(entry.szDllName, szDllName))
2023 entry.dwRefs++;
2024 m_critSec.Unlock();
2025 if (pPeerInfo)
2027 Peer::DllInfo *pl = (Peer::DllInfo*)pPeerInfo;
2028 *pl = m_DllInfos[i];
2030 return entry.hInstDll;
2033 DLL_CACHE_ENTRY entry;
2034 entry.hInstDll = ::LoadLibraryA(szDllName);
2035 if (!entry.hInstDll)
2037 m_critSec.Unlock();
2038 return NULL;
2040 if (!SafeStringCopy(entry.szDllName, szDllName))
2042 ::FreeLibrary(entry.hInstDll);
2043 m_critSec.Unlock();
2044 return NULL;
2046 entry.dwRefs = 1;
2047 entry.bAlive = TRUE;
2048 m_Dlls.Add(entry);
2050 Peer::DllInfo *pdllInfo = (Peer::DllInfo*)pPeerInfo;
2052 // m_Peer could throw an exception from user code. We
2053 // pass that exception from here to a higher context (we
2054 // won't deal with user exception here).
2055 if (!m_Peer.Add(entry.hInstDll, pdllInfo))
2057 RemoveDllEntry(entry);
2061 if ((entry.hInstDll != NULL) && (!m_DllInfos.Add(*pdllInfo)))
2063 RemoveDllEntry(entry);
2066 m_critSec.Unlock();
2067 return entry.hInstDll;
2070 BOOL Free(HINSTANCE hInstDll)
2072 HRESULT hr = m_critSec.Lock();
2073 if (FAILED(hr))
2074 return FALSE;
2076 int nLen = m_Dlls.GetSize();
2077 for (int i=0; i<nLen; i++)
2079 DLL_CACHE_ENTRY &entry = m_Dlls[i];
2080 if (entry.hInstDll == hInstDll)
2082 ATLASSERT(entry.dwRefs > 0);
2083 entry.bAlive = TRUE;
2084 entry.dwRefs--;
2085 m_critSec.Unlock();
2086 return TRUE;
2090 m_critSec.Unlock();
2091 // the dll wasn't found
2092 // in the cache, so just
2093 // pass along to ::FreeLibrary
2094 return ::FreeLibrary(hInstDll);
2097 BOOL AddRefModule(HINSTANCE hInstDll)
2099 HRESULT hr = m_critSec.Lock();
2100 if (FAILED(hr))
2101 return FALSE;
2103 int nLen = m_Dlls.GetSize();
2104 for (int i=0; i<nLen; i++)
2106 DLL_CACHE_ENTRY &entry = m_Dlls[i];
2107 if (entry.hInstDll == hInstDll)
2109 ATLASSERT(entry.dwRefs > 0);
2110 entry.dwRefs++;
2111 m_critSec.Unlock();
2112 return TRUE;
2116 m_critSec.Unlock();
2117 return FALSE;
2120 BOOL ReleaseModule(HINSTANCE hInstDll)
2122 HRESULT hr = m_critSec.Lock();
2123 if (FAILED(hr))
2124 return FALSE;
2126 int nLen = m_Dlls.GetSize();
2127 for (int i=0; i<nLen; i++)
2129 DLL_CACHE_ENTRY &entry = m_Dlls[i];
2130 if (entry.hInstDll == hInstDll)
2132 ATLASSERT(entry.dwRefs > 0);
2133 entry.bAlive = TRUE;
2134 entry.dwRefs--;
2135 m_critSec.Unlock();
2136 return TRUE;
2139 m_critSec.Unlock();
2140 return FALSE;
2143 HRESULT GetEntries(DWORD dwCount, DLL_CACHE_ENTRY *pEntries, DWORD *pdwCopied)
2145 if (!pdwCopied)
2146 return E_POINTER;
2148 HRESULT hr = m_critSec.Lock();
2149 if (FAILED(hr))
2150 return hr;
2152 if (dwCount==0 || pEntries==NULL)
2154 // just return the required size
2155 *pdwCopied = m_Dlls.GetSize();
2156 m_critSec.Unlock();
2157 return S_OK;
2160 if (dwCount > (DWORD) m_Dlls.GetSize())
2161 dwCount = m_Dlls.GetSize();
2162 Checked::memcpy_s(pEntries, dwCount*sizeof(DLL_CACHE_ENTRY), m_Dlls.GetData(), dwCount*sizeof(DLL_CACHE_ENTRY));
2163 *pdwCopied = dwCount;
2164 m_critSec.Unlock();
2165 return S_OK;
2168 HRESULT Flush()
2170 HRESULT hr = m_critSec.Lock();
2171 if (FAILED(hr))
2172 return hr;
2174 int nLen = m_Dlls.GetSize();
2175 for (int i=0; i<nLen; i++)
2177 DLL_CACHE_ENTRY &entry = m_Dlls[i];
2178 if (entry.dwRefs == 0 && !entry.bAlive)
2180 _ATLTRY
2182 m_Peer.Remove(entry.hInstDll, &m_DllInfos[i]);
2184 _ATLCATCHALL()
2186 ATLTRACE(atlTraceCache, 2, _T("Exception thrown from user code in CDllCache::Flush\n"));
2189 ::FreeLibrary(entry.hInstDll);
2190 m_Dlls.RemoveAt(i);
2191 m_DllInfos.RemoveAt(i);
2192 i--;
2193 nLen--;
2195 entry.bAlive = FALSE;
2198 m_critSec.Unlock();
2199 return S_OK;
2202 HRESULT Execute(DWORD_PTR /*dwParam*/, HANDLE /*hObject*/)
2204 Flush();
2205 return S_OK;
2208 HRESULT CloseHandle(HANDLE hObject)
2210 ATLASSUME(m_hTimer == hObject);
2211 m_hTimer = NULL;
2212 ::CloseHandle(hObject);
2213 return S_OK;
2215 }; // CDllCache
2219 //IStencilCache
2220 //IStencilCache is used by a stencil processor to cache pointers to CStencil
2221 //derived objects.
2224 // {8702269B-707D-49cc-AEF8-5FFCB3D6891B}
2225 extern "C" __declspec(selectany) const IID IID_IStencilCache = { 0x8702269b, 0x707d, 0x49cc, { 0xae, 0xf8, 0x5f, 0xfc, 0xb3, 0xd6, 0x89, 0x1b } };
2227 __interface ATL_NO_VTABLE __declspec(uuid("8702269B-707D-49cc-AEF8-5FFCB3D6891B"))
2228 IStencilCache : public IUnknown
2230 // IStencilCache methods
2231 STDMETHOD(CacheStencil)(LPCSTR szName, //a name for this cache entry
2232 void *pStencil, //a pointer to a CStencil derived object
2233 DWORD dwSize, //sizeof pStencil
2234 HCACHEITEM *pHandle, //out pointer to a handle to the this cache entry
2235 HINSTANCE hInst, //HINSTANCE of the module putting this entry
2236 //in the cache.
2237 IMemoryCacheClient *pClient //Interface used to free this instance
2239 STDMETHOD(LookupStencil)(LPCSTR szName, HCACHEITEM * phStencil);
2240 STDMETHOD(GetStencil)(const HCACHEITEM hStencil, void ** ppStencil) const;
2241 STDMETHOD(AddRefStencil)(const HCACHEITEM hStencil);
2242 STDMETHOD(ReleaseStencil)(const HCACHEITEM hStencil);
2245 // {55DEF119-D7A7-4eb7-A876-33365E1C5E1A}
2246 extern "C" __declspec(selectany) const IID IID_IStencilCacheControl = { 0x55def119, 0xd7a7, 0x4eb7, { 0xa8, 0x76, 0x33, 0x36, 0x5e, 0x1c, 0x5e, 0x1a } };
2247 __interface ATL_NO_VTABLE __declspec(uuid("55DEF119-D7A7-4eb7-A876-33365E1C5E1A"))
2248 IStencilCacheControl : public IUnknown
2250 //IStencilCacheControl
2251 STDMETHOD(RemoveStencil)(const HCACHEITEM hStencil); // Removes the stencil if there are no references,
2252 // otherwise detaches it
2253 STDMETHOD(RemoveStencilByName)(LPCSTR szStencil); //removes a stencil if there are no
2254 //references to it
2255 STDMETHOD(RemoveAllStencils)(); //removes all stencils that don't have references on them
2256 STDMETHOD(SetDefaultLifespan)(unsigned __int64 dwdwLifespan); //sets the lifespan for all stencils
2257 //in the cache (in 100 nanosecond units (10,000,000=1 second)).
2258 STDMETHOD(GetDefaultLifespan)(unsigned __int64 *pdwdwLifespan);
2261 #ifndef ATL_STENCIL_CACHE_TIMEOUT
2262 #ifdef _DEBUG
2263 #define ATL_STENCIL_CACHE_TIMEOUT 1000
2264 #else
2265 #define ATL_STENCIL_CACHE_TIMEOUT 5000
2266 #endif // _DEBUG
2267 #endif // ATL_STENCIL_CACHE_TIMEOUT
2269 #ifndef ATL_STENCIL_LIFESPAN
2270 #ifdef _DEBUG
2271 #define ATL_STENCIL_LIFESPAN CFileTime::Second
2272 #else
2273 #define ATL_STENCIL_LIFESPAN CFileTime::Hour
2274 #endif
2275 #endif
2277 // timeout before we check if the file
2278 // has changed in m.s.
2279 #ifndef ATL_STENCIL_CHECK_TIMEOUT
2280 #define ATL_STENCIL_CHECK_TIMEOUT 1000
2281 #endif
2283 template <class MonitorClass,
2284 class StatClass=CStdStatClass,
2285 class SyncClass=CComCriticalSection,
2286 class FlushClass=COldFlusher,
2287 class CullClass=CLifetimeCuller >
2288 class CStencilCache :
2289 public CMemoryCacheBase<CStencilCache<MonitorClass, StatClass, SyncClass, FlushClass, CullClass>, void *, CCacheDataEx,
2290 CFixedStringKey, CStringElementTraitsI<CFixedStringKey >,
2291 FlushClass, CullClass, SyncClass, StatClass>,
2292 public IStencilCache,
2293 public IStencilCacheControl,
2294 public IWorkerThreadClient,
2295 public IMemoryCacheStats,
2296 public CComObjectRootEx<CComGlobalsThreadModel>
2298 protected:
2299 typedef CMemoryCacheBase<CStencilCache<MonitorClass, StatClass, SyncClass, FlushClass, CullClass>, void *, CCacheDataEx,
2300 CFixedStringKey, CStringElementTraitsI<CFixedStringKey >,
2301 FlushClass, CullClass, SyncClass, StatClass> cacheBase;
2302 unsigned __int64 m_dwdwStencilLifespan;
2304 MonitorClass m_Monitor;
2305 HANDLE m_hTimer;
2306 CComPtr<IDllCache> m_spDllCache;
2308 public:
2310 CStencilCache() :
2311 m_dwdwStencilLifespan(ATL_STENCIL_LIFESPAN),
2312 m_hTimer(NULL)
2317 ~CStencilCache()
2319 if (m_hTimer)
2321 ATLENSURE(SUCCEEDED(m_Monitor.RemoveHandle(m_hTimer)));
2325 HRESULT Execute(DWORD_PTR dwParam, HANDLE /*hObject*/)
2327 CStencilCache* pCache = (CStencilCache*)dwParam;
2328 if (pCache)
2329 pCache->FlushEntries();
2330 return S_OK;
2333 HRESULT CloseHandle(HANDLE hObject)
2335 ATLASSUME(m_hTimer == hObject);
2336 m_hTimer = NULL;
2337 ::CloseHandle(hObject);
2338 return S_OK;
2341 HRESULT Initialize(IServiceProvider *pProv, DWORD dwStencilCacheTimeout=ATL_STENCIL_CACHE_TIMEOUT,
2342 __int64 dwdwStencilLifespan=ATL_STENCIL_LIFESPAN)
2344 m_dwdwStencilLifespan = dwdwStencilLifespan;
2345 HRESULT hr = cacheBase::Initialize();
2346 if (FAILED(hr))
2347 return hr;
2348 hr = E_FAIL;
2349 if (pProv)
2350 hr = pProv->QueryService(__uuidof(IDllCache), __uuidof(IDllCache), (void**)&m_spDllCache);
2351 if (FAILED(hr))
2352 return hr;
2353 hr = m_Monitor.Initialize();
2354 if (FAILED(hr))
2355 return hr;
2356 return m_Monitor.AddTimer(dwStencilCacheTimeout, this, (DWORD_PTR) this, &m_hTimer);
2359 template <class ThreadTraits>
2360 HRESULT Initialize(IServiceProvider *pProv, CWorkerThread<ThreadTraits> *pWorkerThread,
2361 DWORD dwStencilCacheTimeout=ATL_STENCIL_CACHE_TIMEOUT, __int64 dwdwStencilLifespan=ATL_STENCIL_LIFESPAN)
2363 m_dwdwStencilLifespan = dwdwStencilLifespan;
2364 HRESULT hr = cacheBase::Initialize();
2365 if (FAILED(hr))
2366 return hr;
2367 hr = E_FAIL;
2368 if (pProv)
2369 hr = pProv->QueryService(__uuidof(IDllCache), __uuidof(IDllCache), (void**)&m_spDllCache);
2370 if (FAILED(hr))
2371 return hr;
2372 hr = m_Monitor.Initialize(pWorkerThread);
2373 if (FAILED(hr))
2374 return hr;
2375 return m_Monitor.AddTimer(dwStencilCacheTimeout, this, (DWORD_PTR) this, &m_hTimer);
2379 BEGIN_COM_MAP(CStencilCache)
2380 COM_INTERFACE_ENTRY(IMemoryCacheStats)
2381 COM_INTERFACE_ENTRY(IStencilCache)
2382 COM_INTERFACE_ENTRY(IStencilCacheControl)
2383 END_COM_MAP()
2384 //IStencilCache methods
2385 STDMETHOD(CacheStencil)(LPCSTR szName, void *pStencil, DWORD dwSize, HCACHEITEM *phEntry,
2386 HINSTANCE hInstance, IMemoryCacheClient *pClient)
2388 NodeType * pEntry = NULL;
2389 HRESULT hr = m_syncObj.Lock();
2390 if (FAILED(hr))
2391 return hr;
2393 _ATLTRY
2395 hr = cacheBase::AddEntry(szName, pStencil, dwSize, (HCACHEITEM *)&pEntry);
2397 _ATLCATCHALL()
2399 hr = E_FAIL;
2401 if (hr != S_OK)
2403 m_syncObj.Unlock();
2404 return hr;
2407 pEntry->hInstance = hInstance;
2408 pEntry->pClient = pClient;
2409 pEntry->nLifespan = m_dwdwStencilLifespan;
2410 if (hInstance && m_spDllCache)
2411 m_spDllCache->AddRefModule(hInstance);
2413 cacheBase::CommitEntry(static_cast<HCACHEITEM>(pEntry));
2415 if (phEntry)
2416 *phEntry = static_cast<HCACHEITEM>(pEntry);
2417 else
2418 cacheBase::ReleaseEntry(static_cast<HCACHEITEM>(pEntry));
2420 m_syncObj.Unlock();
2421 return hr;
2424 STDMETHOD(LookupStencil)(LPCSTR szName, HCACHEITEM * phStencil)
2426 return cacheBase::LookupEntry(szName, phStencil);
2429 STDMETHOD(GetStencil)(const HCACHEITEM hStencil, void ** pStencil) const
2431 return cacheBase::GetEntryData(hStencil, pStencil, NULL);
2434 STDMETHOD(AddRefStencil)(const HCACHEITEM hStencil)
2436 return cacheBase::AddRefEntry(hStencil);
2439 STDMETHOD(ReleaseStencil)(const HCACHEITEM hStencil)
2441 return cacheBase::ReleaseEntry(hStencil);
2444 //IStencilCacheControl
2446 STDMETHOD(RemoveStencil)(const HCACHEITEM hStencil)
2448 return cacheBase::RemoveEntry(hStencil);
2451 STDMETHOD(RemoveStencilByName)(LPCSTR szStencil)
2453 return cacheBase::RemoveEntryByKey(szStencil);
2456 STDMETHOD(RemoveAllStencils)()
2458 return cacheBase::RemoveAllEntries();
2461 STDMETHOD(SetDefaultLifespan)(unsigned __int64 dwdwLifespan)
2463 m_dwdwStencilLifespan = dwdwLifespan;
2464 return S_OK;
2467 STDMETHOD(GetDefaultLifespan)(unsigned __int64 *pdwdwLifepsan)
2469 HRESULT hr = E_POINTER;
2470 if (pdwdwLifepsan)
2472 *pdwdwLifepsan = m_dwdwStencilLifespan;
2473 hr = S_OK;
2475 return hr;
2478 virtual void OnDestroyEntry(const NodeType * pEntry)
2480 ATLASSERT(pEntry);
2481 if (!pEntry)
2482 return;
2484 if (pEntry->pClient)
2485 pEntry->pClient->Free((void *)&pEntry->Data);
2486 if (pEntry->hInstance && m_spDllCache)
2487 m_spDllCache->ReleaseModule(pEntry->hInstance);
2490 HRESULT Uninitialize()
2492 HRESULT hrMonitor=S_OK;
2493 if (m_hTimer)
2495 hrMonitor=m_Monitor.RemoveHandle(m_hTimer);
2496 m_hTimer = NULL;
2498 m_Monitor.Shutdown();
2499 HRESULT hrCache=cacheBase::Uninitialize();
2500 if(FAILED(hrMonitor))
2502 return hrMonitor;
2504 return hrCache;
2506 // IMemoryCacheStats methods
2507 HRESULT STDMETHODCALLTYPE ClearStats()
2509 m_statObj.ResetCounters();
2510 return S_OK;
2513 HRESULT STDMETHODCALLTYPE GetHitCount(DWORD *pdwSize)
2515 if (!pdwSize)
2516 return E_POINTER;
2517 *pdwSize = m_statObj.GetHitCount();
2518 return S_OK;
2521 HRESULT STDMETHODCALLTYPE GetMissCount(DWORD *pdwSize)
2523 if (!pdwSize)
2524 return E_POINTER;
2525 *pdwSize = m_statObj.GetMissCount();
2526 return S_OK;
2529 HRESULT STDMETHODCALLTYPE GetMaxAllocSize(DWORD *pdwSize)
2531 if (!pdwSize)
2532 return E_POINTER;
2533 *pdwSize = m_statObj.GetMaxAllocSize();
2534 return S_OK;
2537 HRESULT STDMETHODCALLTYPE GetCurrentAllocSize(DWORD *pdwSize)
2539 if (!pdwSize)
2540 return E_POINTER;
2541 *pdwSize = m_statObj.GetCurrentAllocSize();
2542 return S_OK;
2545 HRESULT STDMETHODCALLTYPE GetMaxEntryCount(DWORD *pdwSize)
2547 if (!pdwSize)
2548 return E_POINTER;
2549 *pdwSize = m_statObj.GetMaxEntryCount();
2550 return S_OK;
2553 HRESULT STDMETHODCALLTYPE GetCurrentEntryCount(DWORD *pdwSize)
2555 if (!pdwSize)
2556 return E_POINTER;
2557 *pdwSize = m_statObj.GetCurrentEntryCount();
2558 return S_OK;
2560 }; // CStencilCache
2562 // {105A8866-4059-45fe-86AE-FA0EABBFBBB4}
2563 extern "C" __declspec(selectany) const IID IID_IFileCache = { 0x105a8866, 0x4059, 0x45fe, { 0x86, 0xae, 0xfa, 0xe, 0xab, 0xbf, 0xbb, 0xb4 } };
2565 __interface ATL_NO_VTABLE __declspec(uuid("105A8866-4059-45fe-86AE-FA0EABBFBBB4"))
2566 IFileCache : public IUnknown
2568 // IFileCache Methods
2570 STDMETHOD(AddFile)(
2571 LPCSTR szFileName,
2572 LPCSTR szTempFileName,
2573 FILETIME *pftExpireTime,
2574 void *pPeerInfo,
2575 HCACHEITEM * phKey);
2576 STDMETHOD(LookupFile)(LPCSTR szFileName, HCACHEITEM * phKey);
2577 STDMETHOD(GetFile)(const HCACHEITEM hKey, LPSTR * pszFileName, void **ppPeerInfo);
2578 STDMETHOD(ReleaseFile)(const HCACHEITEM hKey);
2579 STDMETHOD(RemoveFile)(const HCACHEITEM hKey);
2580 STDMETHOD(RemoveFileByName)(LPCSTR szFileName);
2581 STDMETHOD(Flush)();
2584 #ifndef ATL_FILE_CACHE_TIMEOUT
2585 #define ATL_FILE_CACHE_TIMEOUT 1000
2586 #endif
2588 class CNoFileCachePeer
2590 public:
2591 struct PeerInfo
2595 static BOOL Add(PeerInfo* /*pDest*/, PeerInfo * /*pSrc*/)
2597 return TRUE;
2600 static BOOL Remove(const PeerInfo* /*pFileInfo*/)
2602 return TRUE;
2606 template <class Peer>
2607 struct CCacheDataPeer : public CCacheDataBase
2609 typename Peer::PeerInfo PeerData;
2612 // A class to keep track of files, with maintenance -- maximum size of cache,
2613 // maximum number of entries, expiration of entries, etc. -- inherits from
2614 // CMemoryCacheBase
2615 template <
2616 class MonitorClass,
2617 class StatClass=CStdStatClass,
2618 class FileCachePeer=CNoFileCachePeer,
2619 class FlushClass=COldFlusher,
2620 class SyncClass=CComCriticalSection,
2621 class CullClass=CExpireCuller >
2622 class CFileCache:
2623 public CMemoryCacheBase<CFileCache<MonitorClass, StatClass, FileCachePeer, FlushClass, SyncClass, CullClass>, LPSTR, CCacheDataPeer<FileCachePeer>,
2624 CFixedStringKey, CStringElementTraits<CFixedStringKey >,
2625 FlushClass, CullClass, SyncClass, StatClass>,
2626 public IWorkerThreadClient,
2627 public IFileCache,
2628 public IMemoryCacheControl,
2629 public IMemoryCacheStats
2631 typedef CMemoryCacheBase<CFileCache<MonitorClass, StatClass, FileCachePeer, FlushClass, SyncClass, CullClass>, LPSTR, CCacheDataPeer<FileCachePeer>,
2632 CFixedStringKey, CStringElementTraits<CFixedStringKey >,
2633 FlushClass, CullClass, SyncClass, StatClass> cacheBase;
2635 MonitorClass m_Monitor;
2637 protected:
2638 HANDLE m_hTimer;
2640 public:
2642 CFileCache() : m_hTimer(NULL)
2646 HRESULT Initialize()
2648 HRESULT hr = cacheBase::Initialize();
2649 if (FAILED(hr))
2650 return hr;
2651 hr = m_Monitor.Initialize();
2652 if (FAILED(hr))
2653 return hr;
2654 return m_Monitor.AddTimer(ATL_FILE_CACHE_TIMEOUT,
2655 static_cast<IWorkerThreadClient*>(this), (DWORD_PTR) this, &m_hTimer);
2658 template <class ThreadTraits>
2659 HRESULT Initialize(CWorkerThread<ThreadTraits> *pWorkerThread)
2661 ATLASSERT(pWorkerThread);
2663 HRESULT hr = cacheBase::Initialize();
2664 if (FAILED(hr))
2665 return hr;
2666 hr = m_Monitor.Initialize(pWorkerThread);
2667 if (FAILED(hr))
2668 return hr;
2669 return m_Monitor.AddTimer(ATL_FILE_CACHE_TIMEOUT,
2670 static_cast<IWorkerThreadClient*>(this), (DWORD_PTR) this, &m_hTimer);
2674 // Callback for CWorkerThread
2675 HRESULT Execute(DWORD_PTR dwParam, HANDLE /*hObject*/)
2677 CFileCache* pCache = (CFileCache*)dwParam;
2679 if (pCache)
2680 pCache->Flush();
2681 return S_OK;
2684 HRESULT CloseHandle(HANDLE hObject)
2686 ATLASSUME(m_hTimer == hObject);
2687 m_hTimer = NULL;
2688 ::CloseHandle(hObject);
2689 return S_OK;
2692 ~CFileCache()
2694 if (m_hTimer)
2696 ATLENSURE(SUCCEEDED(m_Monitor.RemoveHandle(m_hTimer)));
2697 m_hTimer = NULL;
2701 HRESULT Uninitialize()
2703 HRESULT hrMonitor=S_OK;
2704 if (m_hTimer)
2706 hrMonitor=m_Monitor.RemoveHandle(m_hTimer);
2707 m_hTimer = NULL;
2709 m_Monitor.Shutdown();
2710 HRESULT hrCache=cacheBase::Uninitialize();
2711 if(FAILED(hrMonitor))
2713 return hrMonitor;
2715 return hrCache;
2719 // IUnknown methods
2720 HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, void **ppv)
2722 HRESULT hr = E_NOINTERFACE;
2723 if (!ppv)
2724 hr = E_POINTER;
2725 else
2727 if (InlineIsEqualGUID(riid, __uuidof(IUnknown)) ||
2728 InlineIsEqualGUID(riid, __uuidof(IFileCache)))
2730 *ppv = (IUnknown *) (IFileCache *) this;
2731 AddRef();
2732 hr = S_OK;
2734 if (InlineIsEqualGUID(riid, __uuidof(IMemoryCacheStats)))
2736 *ppv = (IMemoryCacheStats*)this;
2737 AddRef();
2738 hr = S_OK;
2740 if (InlineIsEqualGUID(riid, __uuidof(IMemoryCacheControl)))
2742 *ppv = (IMemoryCacheControl*)this;
2743 AddRef();
2744 hr = S_OK;
2748 return hr;
2751 ULONG STDMETHODCALLTYPE AddRef()
2753 return 1;
2756 ULONG STDMETHODCALLTYPE Release()
2758 return 1;
2761 // Adds a file to the cache. A file is created with a
2762 // temporary name, and then Add is called with the temp
2763 // file name and the final file name, along with expiration data,
2764 // etc. A search on the file name will return the name of
2765 // the file on disk (i.e. the temporary file)
2766 HRESULT STDMETHODCALLTYPE AddFile(
2767 LPCSTR szFileName,
2768 LPCSTR szTempFileName,
2769 FILETIME *pftExpireTime,
2770 void* pPeerInfo,
2771 HCACHEITEM * phKey = NULL)
2773 WIN32_FILE_ATTRIBUTE_DATA fadData;
2774 BOOL bRet = GetFileAttributesExA(szTempFileName, GetFileExInfoStandard, &fadData);
2775 if (!bRet)
2776 return AtlHresultFromLastError();
2778 __int64 ddwFileSize = (static_cast<__int64>(fadData.nFileSizeHigh) << 32) + fadData.nFileSizeLow;
2780 DWORD dwRecordedFileSize = (DWORD) (ddwFileSize >> 10);
2781 // Round the file size up to 1K if it is < 1K
2782 if (dwRecordedFileSize == 0)
2783 dwRecordedFileSize = 1;
2785 if (m_Monitor.GetThreadHandle()==NULL)
2787 if (!cacheBase::CanAddEntry(dwRecordedFileSize))
2789 cacheBase::FlushEntries();
2790 if (!cacheBase::CanAddEntry(dwRecordedFileSize))
2791 return E_OUTOFMEMORY;
2795 NodeType *pEntry = NULL;
2796 HRESULT hr = m_syncObj.Lock();
2797 if (FAILED(hr))
2798 return hr;
2800 hr = E_FAIL;
2802 // Make a private copy of the file name
2803 CHeapPtr<char> szTempFileCopy;
2804 if (szTempFileCopy.Allocate(MAX_PATH))
2806 if (strlen(szTempFileName) >= MAX_PATH)
2808 m_syncObj.Unlock();
2809 return E_FAIL;
2811 Checked::strncpy_s(szTempFileCopy, MAX_PATH, szTempFileName, _TRUNCATE);
2813 _ATLTRY
2815 hr = cacheBase::AddEntry(szFileName, szTempFileCopy, dwRecordedFileSize, (HCACHEITEM*)&pEntry);
2816 szTempFileCopy.Detach();
2818 _ATLCATCHALL()
2820 hr = E_FAIL;
2824 if (hr != S_OK)
2826 m_syncObj.Unlock();
2827 return hr;
2831 hr = (TRUE == FileCachePeer::Add(&pEntry->PeerData,
2832 static_cast<FileCachePeer::PeerInfo*>(pPeerInfo)) ? S_OK : E_FAIL);
2833 if (hr == S_OK)
2834 hr = cacheBase::CommitEntry(static_cast<HCACHEITEM>(pEntry));
2836 if (hr != S_OK)
2838 cacheBase::RemoveEntry(static_cast<HCACHEITEM>(pEntry));
2839 m_syncObj.Unlock();
2840 return hr;
2844 if (pftExpireTime)
2845 pEntry->cftExpireTime = *pftExpireTime;
2847 if (phKey)
2848 *phKey = static_cast<HCACHEITEM>(pEntry);
2849 else
2850 cacheBase::ReleaseEntry(pEntry);
2852 m_syncObj.Unlock();
2853 return hr;
2856 // Action to take when the entry is removed from the cache
2857 virtual void OnDestroyEntry(const NodeType * pEntry)
2859 ATLASSERT(pEntry);
2860 if (!pEntry)
2861 return;
2862 FileCachePeer::Remove(&pEntry->PeerData);
2863 if (pEntry->Data)
2865 DeleteFileA(pEntry->Data);
2866 free(pEntry->Data);
2867 const_cast<NodeType*>(pEntry)->Data = NULL;
2871 // Looks up a file by name. Must be released after use
2872 HRESULT STDMETHODCALLTYPE LookupFile(LPCSTR szFileName, HCACHEITEM * phKey)
2874 return cacheBase::LookupEntry(szFileName, phKey);
2877 // Gets the name of the file on disk
2878 HRESULT STDMETHODCALLTYPE GetFile(__in const HCACHEITEM hKey, __deref_out_z_opt LPSTR * pszFileName, __deref_out_opt void **ppPeerInfo)
2880 NodeType *pEntry = (NodeType *)hKey;
2881 if (ppPeerInfo)
2882 *ppPeerInfo = &pEntry->PeerData;
2883 return cacheBase::GetEntryData(hKey, pszFileName, NULL);
2886 // Releases a file
2887 HRESULT STDMETHODCALLTYPE ReleaseFile(const HCACHEITEM hKey)
2889 return cacheBase::ReleaseEntry(hKey);
2892 // Releases a file and marks it for deletion
2893 HRESULT STDMETHODCALLTYPE RemoveFile(const HCACHEITEM hKey)
2895 return cacheBase::RemoveEntry(hKey);
2898 // Removes a file by name -- this calls IMemoryCacheClient->Free
2899 // on the file name, which by default (for CFileCache) deletes the
2900 // file.
2901 HRESULT STDMETHODCALLTYPE RemoveFileByName(LPCSTR szFileName)
2903 return cacheBase::RemoveEntryByKey(szFileName);
2906 // Flushes the entries in the cache, eliminates expired entries,
2907 // or if the cache exceeds the parameters (alloc size, num entries),
2908 // culls items based on the sweep mode
2909 HRESULT STDMETHODCALLTYPE Flush()
2911 return cacheBase::FlushEntries();
2914 // IMemoryCacheControl methods
2915 HRESULT STDMETHODCALLTYPE SetMaxAllowedSize(DWORD dwSize)
2917 return cacheBase::SetMaxAllowedSize(dwSize);
2920 HRESULT STDMETHODCALLTYPE GetMaxAllowedSize(DWORD *pdwSize)
2922 return cacheBase::GetMaxAllowedSize(pdwSize);
2925 HRESULT STDMETHODCALLTYPE SetMaxAllowedEntries(DWORD dwSize)
2927 return cacheBase::SetMaxAllowedEntries(dwSize);
2930 HRESULT STDMETHODCALLTYPE GetMaxAllowedEntries(DWORD *pdwSize)
2932 return cacheBase::GetMaxAllowedEntries(pdwSize);
2935 HRESULT STDMETHODCALLTYPE ResetCache()
2937 return cacheBase::ResetCache();
2941 // IMemoryCacheStats methods
2942 HRESULT STDMETHODCALLTYPE ClearStats()
2944 m_statObj.ResetCounters();
2945 return S_OK;
2948 HRESULT STDMETHODCALLTYPE GetHitCount(DWORD *pdwSize)
2950 if (!pdwSize)
2951 return E_POINTER;
2952 *pdwSize = m_statObj.GetHitCount();
2953 return S_OK;
2956 HRESULT STDMETHODCALLTYPE GetMissCount(DWORD *pdwSize)
2958 if (!pdwSize)
2959 return E_POINTER;
2960 *pdwSize = m_statObj.GetMissCount();
2961 return S_OK;
2964 HRESULT STDMETHODCALLTYPE GetMaxAllocSize(DWORD *pdwSize)
2966 if (!pdwSize)
2967 return E_POINTER;
2968 *pdwSize = m_statObj.GetMaxAllocSize();
2969 return S_OK;
2972 HRESULT STDMETHODCALLTYPE GetCurrentAllocSize(DWORD *pdwSize)
2974 if (!pdwSize)
2975 return E_POINTER;
2976 *pdwSize = m_statObj.GetCurrentAllocSize();
2977 return S_OK;
2980 HRESULT STDMETHODCALLTYPE GetMaxEntryCount(DWORD *pdwSize)
2982 if (!pdwSize)
2983 return E_POINTER;
2984 *pdwSize = m_statObj.GetMaxEntryCount();
2985 return S_OK;
2988 HRESULT STDMETHODCALLTYPE GetCurrentEntryCount(DWORD *pdwSize)
2990 if (!pdwSize)
2991 return E_POINTER;
2992 *pdwSize = m_statObj.GetCurrentEntryCount();
2993 return S_OK;
2995 }; // CFileCache
2997 class CDataConnection; // see atldbcli.h
2998 __interface __declspec(uuid("52E7759B-D6CC-4a03-BDF3-80A6BDCA1F94"))
2999 IDataSourceCache : public IUnknown
3001 // Method: Add
3002 // Params:
3003 // szConn: Connection string of data source to connect to
3004 // ppDS: Out pointer to the newly added data source
3005 // Comments:
3006 // Attempts to open a connection to the specified data source
3007 // using a CDataSource object. Once the connection is open, the
3008 // CDatasource is cached.
3009 STDMETHOD(Add)(LPCTSTR szID, LPCOLESTR szConn, CDataConnection *pDS);
3011 // Method: Remove
3012 // Params:
3013 // szConn: Specifies the connection string of the connection to close
3014 // Comments:
3015 // Closes the specified connection and removes it's entry from the cache
3016 STDMETHOD(Remove)(LPCTSTR szID);
3018 // Method: Lookup
3019 // Params:
3020 // szConn: Specifies the connection string of the connection to look up
3021 // ppDS: Out pointer to CDataSource object that is connected to the specified
3022 // data source.
3023 STDMETHOD(Lookup)(LPCTSTR szID, CDataConnection *pDS);
3025 // Method: Uninitialize
3026 // Params:
3027 // None
3028 // Comments:
3029 // Closes removes all connections from the cache.
3030 STDMETHOD(Uninitialize)();
3033 #ifndef ATL_DS_CONN_STRING_LEN
3034 #define ATL_DS_CONN_STRING_LEN 512
3035 #endif
3037 template <>
3038 class CElementTraits< CDataConnection > :
3039 public CElementTraitsBase< CDataConnection >
3041 public:
3042 static ULONG Hash( INARGTYPE t )
3044 return( ULONG( ULONG_PTR( &t ) ) );
3047 static bool CompareElements( INARGTYPE element1, INARGTYPE element2 )
3049 return( element1.m_session.m_spOpenRowset == element2.m_session.m_spOpenRowset);
3052 static int CompareElementsOrdered( INARGTYPE /*element1*/, INARGTYPE /*element2*/ )
3054 ATLASSERT(FALSE);
3055 return -1;
3060 typedef CFixedStringT<CString, ATL_DS_CONN_STRING_LEN> atlDataSourceKey;
3061 typedef CAtlMap<atlDataSourceKey, CDataConnection,
3062 CStringElementTraits<atlDataSourceKey>, CElementTraits<CDataConnection> > atlDataSourceCacheMap;
3064 template <class TCritSec=CComFakeCriticalSection>
3065 class CDataSourceCache :
3066 public IDataSourceCache,
3067 public CComObjectRootEx<CComGlobalsThreadModel>
3069 public:
3070 BEGIN_COM_MAP(CDataSourceCache)
3071 COM_INTERFACE_ENTRY(IDataSourceCache)
3072 END_COM_MAP()
3074 CDataSourceCache()
3076 m_cs.Init();
3079 virtual ~CDataSourceCache ()
3081 Uninitialize();
3084 STDMETHOD(Uninitialize)()
3086 HRESULT hr = m_cs.Lock();
3087 if (SUCCEEDED(hr))
3089 m_ConnectionMap.RemoveAll();
3090 m_cs.Unlock();
3093 return hr;
3096 STDMETHOD(Add)(LPCTSTR szID, LPCOLESTR szConn, CDataConnection *pSession)
3098 HRESULT hr = E_FAIL;
3100 if (!szID)
3101 return E_INVALIDARG; // must have session name
3103 // Do a lookup to make sure we don't add multiple entries
3104 // with the same name. Adding multiple entries with the same name
3105 // could cause some entries to get orphaned.
3106 hr = m_cs.Lock();
3107 if (FAILED(hr))
3109 return hr;
3112 const atlDataSourceCacheMap::CPair *pPair =
3113 m_ConnectionMap.Lookup(szID);
3114 if (!pPair)
3116 // try to open connection
3117 CDataConnection DS;
3118 hr = DS.Open(szConn);
3119 if (hr == S_OK)
3121 _ATLTRY
3123 if (m_ConnectionMap.SetAt(szID, DS))
3125 if (pSession) // we allow NULL here
3126 *pSession = DS; // copy connection to output.
3127 hr = S_OK;
3129 else
3130 hr = E_FAIL; // map add failed
3132 _ATLCATCHALL()
3134 hr = E_FAIL;
3138 else // lookup succeeded, entry is already in cache
3140 // Instead of opening a new connection, just copy
3141 // the one we already have in the cache.
3142 if (pSession)
3143 *pSession = pPair->m_value;
3144 hr = S_OK;
3146 m_cs.Unlock();
3147 return hr;
3150 STDMETHOD(Remove)(LPCTSTR szID)
3152 HRESULT hr = E_INVALIDARG;
3153 if (!szID)
3154 return hr; // must have session name
3156 hr = m_cs.Lock();
3157 if (SUCCEEDED(hr))
3159 hr = m_ConnectionMap.RemoveKey(szID) ? S_OK : E_FAIL;
3160 m_cs.Unlock();
3163 return hr;
3166 STDMETHOD(Lookup)(LPCTSTR szID, CDataConnection *pSession)
3168 if (!szID||!pSession)
3169 return E_POINTER;
3171 HRESULT hr = m_cs.Lock();
3172 bool bRet = true;
3173 if (SUCCEEDED(hr))
3175 bRet = m_ConnectionMap.Lookup(szID, *pSession);
3176 m_cs.Unlock();
3179 return (bRet && (bool)*pSession)? hr : E_FAIL;
3182 protected:
3183 atlDataSourceCacheMap m_ConnectionMap;
3184 TCritSec m_cs;
3188 // Some helpers for using the datasource cache.
3190 // Function: GetDataSource
3191 // Params:
3192 // pProvider: Pointer to IServiceProvider that provides the
3193 // data source cache service
3194 // szID: The name of the connection (can be same as szDS)
3195 // szDS: OLEDB connection string for data source
3196 // ppDS: Out pointer to CDataSource. The CDataSource will be connected
3197 // to the OLEDB provider specified by szDS on successful return.
3198 // RetVal:
3199 // Returns S_OK on success.
3200 static HRESULT ATL_NOINLINE GetDataSource(IServiceProvider *pProvider,
3201 LPCTSTR szID, LPCOLESTR szConn,
3202 CDataConnection *pSession)
3204 if (!pProvider || !szID || !szConn)
3205 return E_POINTER;
3207 CComPtr<IDataSourceCache> spDSCache;
3208 HRESULT hr;
3209 hr = pProvider->QueryService(__uuidof(IDataSourceCache), __uuidof(IDataSourceCache), (void**)&spDSCache);
3210 if (hr == S_OK && spDSCache)
3212 hr = spDSCache->Add(szID, szConn, pSession);
3214 return hr;
3218 // Function: RemoveDataSource
3219 // Params:
3220 // pProvider: Pointer to IServiceProvider that provides the
3221 // data source cache service
3222 // szID: Name of the datasource connection to remove from the cache
3223 // RetVal:
3224 // none
3225 // Comments:
3226 // Removes the datasource entry from the datasource cache. Since entries are
3227 // copied to the client on calls to lookup and add, removing an entry will not
3228 // release the connections of existing clients.
3229 static HRESULT ATL_NOINLINE RemoveDataSource(IServiceProvider *pProvider, LPCTSTR szID)
3231 if (!pProvider || !szID)
3232 return E_POINTER;
3234 CComPtr<IDataSourceCache> spDSCache;
3235 HRESULT hr = pProvider->QueryService(__uuidof(IDataSourceCache), __uuidof(IDataSourceCache), (void**)&spDSCache);
3236 if (spDSCache)
3237 hr = spDSCache->Remove(szID);
3238 return hr;
3241 } // namespace ATL
3242 #pragma pack(pop)
3244 #pragma warning (pop)
3246 #endif // __ATLCACHE_H__