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.

1365 lines
28 KiB

  1. /*++
  2. cintrnl.h
  3. This file contains internal definitions for the cache library -
  4. this stuff should not be of interest for most users.
  5. --*/
  6. #ifndef _CINTRNL_H_
  7. #define _CINTRNL_H_
  8. class CScheduleThread {
  9. /*++
  10. Class Description :
  11. This is a base class for those objects which wish to be
  12. called on a regularily scheduled basis.
  13. The constructors for this class will automatically
  14. put the object in a doubly linked list walked by a
  15. background thread which periodically executes a virtual function.
  16. --*/
  17. private :
  18. //
  19. // Special constructor for the 'Head' element of the
  20. // doubly linked list this class maintains.
  21. //
  22. CScheduleThread( BOOL fSpecial ) ;
  23. protected :
  24. //
  25. // Has the scheduler been initialized ?
  26. //
  27. static BOOL s_fInitialized ;
  28. //
  29. // Crit sect protecting doubly linked list.
  30. //
  31. static CRITICAL_SECTION s_critScheduleList ;
  32. //
  33. // Handle to event used to terminate background thread.
  34. //
  35. static HANDLE s_hShutdown ;
  36. //
  37. // Handle to background thread.
  38. //
  39. static HANDLE s_hThread ;
  40. //
  41. // The head element of the doubly linked list.
  42. //
  43. static CScheduleThread s_Head ;
  44. //
  45. // The thread which calls our virtual functions
  46. //
  47. static DWORD WINAPI ScheduleThread( LPVOID lpv ) ;
  48. //
  49. // Previous and Next pointers which maintain doubly linked
  50. // list of scheduled itesm.
  51. //
  52. class CScheduleThread* m_pPrev ;
  53. class CScheduleThread* m_pNext ;
  54. protected :
  55. //
  56. // Derived classes should override this function -
  57. // it will be called on a regular basis by the scheduler thread.
  58. //
  59. virtual void Schedule( void ) {}
  60. //
  61. // Constructor and Destructor automagically manage
  62. // insertion into doubly linked list of other scheduled items.
  63. // These are protected as we want people to buid only
  64. // derived objects which use this.
  65. //
  66. CScheduleThread() ;
  67. //
  68. // Member functions which put us into the regular schedule !
  69. //
  70. void AddToSchedule() ;
  71. void RemoveFromSchedule() ;
  72. public :
  73. //
  74. // Initialize the class - don't construct
  75. // any derived objects before this is called.
  76. //
  77. static BOOL Init() ;
  78. //
  79. // Terminate class and background thread !
  80. //
  81. static void Term() ;
  82. //
  83. // Global which tells clients how frequently they
  84. // will be called !
  85. //
  86. static DWORD dwNotificationSeconds ;
  87. //
  88. // Destructor is protected - we should only be invoked
  89. // by derived class constructors
  90. //
  91. virtual ~CScheduleThread() ;
  92. } ;
  93. class CacheState : public ICacheRefInterface {
  94. /*++
  95. Class Description :
  96. This class will provide the base support for LRU
  97. removal of items in the cache !
  98. Base Class :
  99. CQElement - we put these objects into
  100. a TLockQueue to amortize LRU operations !
  101. --*/
  102. private :
  103. //
  104. // The following operations are not allowed !
  105. //
  106. CacheState() ;
  107. CacheState( CacheState& ) ;
  108. CacheState& operator=( CacheState& ) ;
  109. //
  110. // Tell us if this entry is older than the specified time !
  111. //
  112. BOOL
  113. OlderThan( FILETIME& filetime ) {
  114. return CompareFileTime( &m_LastAccess, &filetime ) <= 0 ;
  115. }
  116. protected :
  117. //
  118. // The LRU List which holds all of these items gets to
  119. // know are innards !
  120. //
  121. friend class CLRUList ;
  122. //
  123. // For debug purposes - make the memory recognizable !
  124. //
  125. DWORD m_dwSignature ;
  126. //
  127. // The lock used to protect operations within the derived
  128. // classes of this object - most operations within this class
  129. // don't require locking, however all derived classes need
  130. // various locking services, so we provide one lock for all users !
  131. //
  132. CACHELOCK m_lock ;
  133. //
  134. // Our signaure, and constants for supporting us implementing two
  135. // kinds of reference counts !
  136. //
  137. enum CACHESTATE_CONSTANTS {
  138. CACHESTATE_SIGNATURE = 'hcaC',
  139. CLIENT_REF = 0x10000,
  140. CLIENT_BITS = 0xFFFF0000
  141. } ;
  142. //
  143. // Keep a reference count - number of references holding
  144. // this in memory !
  145. // NOTE : we hold two types of references in here -
  146. // regular references added by AddRef() and Release()
  147. // which are references from other caches
  148. // As well as client references. To Add a client reference
  149. // add CLIENT_REF atomically to this !
  150. //
  151. volatile long m_cRefs ;
  152. //
  153. // a long that we use as a lock for calls to LRUReference
  154. //
  155. volatile long m_lLRULock ;
  156. //
  157. // The last time this was touched - helps LRU algorithm !
  158. //
  159. FILETIME m_LastAccess ;
  160. //
  161. // The LRU List that owns this object !
  162. //
  163. public: class CLRUList* m_pOwner ;
  164. //
  165. // Pointers to maintain doubly linked list of
  166. // items in the LRU chain !
  167. //
  168. protected: DLIST_ENTRY m_LRUList ;
  169. //
  170. // structure to keep track of all the References to a
  171. // particular item ! - note m_lock is used to protect
  172. // access to this list
  173. //
  174. DLIST_ENTRY m_ReferencesList ;
  175. //
  176. // The field used to chain in Hash Table buckets
  177. //
  178. DLIST_ENTRY m_HashList ;
  179. //
  180. // This Constructor is protected as we only ever want to see
  181. // classes derived from this thing created !
  182. //
  183. CacheState( class CLRUList*,
  184. long cClientRefs = 1
  185. ) ;
  186. //
  187. // Destructor must be virtual - lots of derived classes !
  188. //
  189. virtual ~CacheState() ;
  190. //
  191. // Touch the structure in a fashion so that the LRU state
  192. // is updated !
  193. //
  194. void
  195. LRUReference( class CLRUList* pLRU ) ;
  196. //
  197. // calling this function insures that the correct Destructor is called !
  198. //
  199. void virtual
  200. Destroy( void* pv ) = 0 ;
  201. //
  202. // Reference counting support - Add a reference
  203. //
  204. long
  205. AddRef() ;
  206. //
  207. // Keeping track of clients - remove a client ref !
  208. //
  209. long
  210. CheckIn( class CAllocatorCache* pAlloc = 0
  211. ) ;
  212. //
  213. // Keeping track of clients - remove a client ref !
  214. //
  215. long
  216. CheckInNoLocks( class CAllocatorCache* pAlloc = 0
  217. ) ;
  218. virtual
  219. BOOL
  220. IsMasterReference() {
  221. return TRUE ;
  222. }
  223. virtual
  224. CacheState*
  225. GetMasterReference() {
  226. return this ;
  227. }
  228. CacheState*
  229. AddRefMaster( ) {
  230. TraceFunctEnter( "CacheState::AddRefMaster()" ) ;
  231. m_lock.ShareLock() ;
  232. CacheState* pReturn = GetMasterReference() ;
  233. if( pReturn ) {
  234. long l = pReturn->AddRef() ;
  235. DebugTrace( DWORD_PTR(pReturn), "Added a reference to %x this %x", pReturn, this ) ;
  236. }
  237. m_lock.ShareUnlock() ;
  238. return pReturn ;
  239. }
  240. public :
  241. #ifdef DEBUG
  242. //
  243. // The number of these things that have been allocated !
  244. //
  245. static long g_cCacheState ;
  246. #endif
  247. //
  248. // Remove a reference - when we return 0 we're destroyed !
  249. //
  250. long
  251. Release( class CAllocatorCache *pAlloc,
  252. void* pv
  253. ) ;
  254. //
  255. // Provided to deal with failures during initialization of items
  256. // being insert into the cache - this function ensures that the
  257. // cache item ends up on the list for destruction !
  258. //
  259. void
  260. FailedCheckOut( class CLRUList* p,
  261. long cClientRefs,
  262. CAllocatorCache* pAllocator,
  263. void* pv
  264. ) ;
  265. //
  266. // For allocating these we use this special operator new
  267. // which goes through our allocation cache !
  268. //
  269. // NOTE : We take a reference because the cache MUST be provided !
  270. //
  271. void*
  272. operator new( size_t size,
  273. class CAllocatorCache& cache
  274. ) {
  275. return cache.Allocate( size ) ;
  276. }
  277. //
  278. // Exclusive Lock ourselves !
  279. //
  280. void
  281. ExclusiveLock() {
  282. m_lock.ExclusiveLock() ;
  283. }
  284. //
  285. // Unlock ourselves
  286. //
  287. void
  288. ExclusiveUnlock() {
  289. m_lock.ExclusiveUnlock() ;
  290. }
  291. //
  292. // Keeping track of clients - Add a client ref !
  293. //
  294. long
  295. CheckOut( class CLRUList* p,
  296. long cClientRefs = 1
  297. ) ;
  298. long
  299. ExternalCheckIn( ) ;
  300. long
  301. ExternalCheckInNoLocks( ) ;
  302. //
  303. // Check to see whether this element is still referenced
  304. // by the hash table containing the cache !
  305. //
  306. BOOL
  307. InCache() {
  308. return !m_HashList.IsEmpty() ;
  309. }
  310. //
  311. // Check to see whether any Cache clients have a
  312. // reference to this item !
  313. // Return TRUE if any clients have added a reference !
  314. //
  315. BOOL
  316. IsCheckedOut() {
  317. _ASSERT( m_dwSignature == CACHESTATE_SIGNATURE ) ;
  318. return (m_cRefs & CLIENT_BITS) != 0 ;
  319. }
  320. //
  321. // Return TRUE if this item is in our LRU list !
  322. //
  323. BOOL
  324. IsInLRUList() {
  325. _ASSERT( m_dwSignature == CACHESTATE_SIGNATURE ) ;
  326. // return whether we are in the LRU List !
  327. return !m_LRUList.IsEmpty() ;
  328. }
  329. #ifdef DEBUG
  330. //
  331. // This is used in _ASSERT's and stuff to check that the cache is correctly ordered
  332. // by time !
  333. //
  334. BOOL
  335. IsOlder( FILETIME filetimeIn,
  336. FILETIME& filetimeOut
  337. ) ;
  338. #endif
  339. //
  340. // The following support functions are used to support manipulating
  341. // these objects in the various kinds of doubly linked lists we may reside in
  342. //
  343. //
  344. BOOL
  345. FLockCandidate( BOOL fExpireChecks,
  346. FILETIME& filetime,
  347. BOOL& fToYoung
  348. ) ;
  349. //
  350. // The following function locks an element of the cache.
  351. // If this is the master cache entry, we will also look all associated items and return TRUE.
  352. // If this is not the master, we lock only the one entry !
  353. //
  354. BOOL
  355. FLockExpungeCandidate( CacheState*& pMaster ) ;
  356. //
  357. // This function pairs with FLockExpungeCandidate()
  358. //
  359. void
  360. ReleaseLocks( CacheState* pMaster ) ;
  361. //
  362. // This is the other half of FLockExpungeCandidate -
  363. // we ensure the destruction of the selected item !
  364. //
  365. void
  366. FinishCandidate( CacheState* pMaster ) ;
  367. //
  368. // The following is not thread safe and is only for use during
  369. // shutdown, where there should be no thread issues !
  370. //
  371. void
  372. IsolateCandidate() ;
  373. //
  374. // This removes the item from the LRU List - Cache lock must be held exclusive or partially !
  375. //
  376. void
  377. RemoveFromLRU() ;
  378. //
  379. // Define the Pointer to Function type that is so usefull
  380. // for using the DLIST templates !
  381. //
  382. typedef DLIST_ENTRY* (*PFNDLIST)( class CacheState* pState ) ;
  383. //
  384. // Helper function for Doubly linked lists of these items in the
  385. // Cache's hash tables !
  386. //
  387. inline static
  388. DLIST_ENTRY*
  389. HashDLIST( CacheState* p ) {
  390. return &p->m_HashList ;
  391. }
  392. //
  393. // Helper function for Doubly linked lists of these items in the
  394. // LRU lists
  395. //
  396. inline static
  397. DLIST_ENTRY*
  398. LRUDLIST( CacheState* p ) {
  399. return &p->m_LRUList ;
  400. }
  401. //
  402. // Helper function for Doubly linked lists of these items in
  403. // the reference lists - the list of items referencing the same
  404. // cache item !
  405. //
  406. inline static
  407. DLIST_ENTRY*
  408. REFSDLIST( CacheState* p ) {
  409. return &p->m_ReferencesList ;
  410. }
  411. } ;
  412. //
  413. // Now Define some Doubly Linked Lists that we can use to manipulate the
  414. // items in the cache in various ways !
  415. //
  416. typedef TDListHead< CacheState, &CacheState::LRUDLIST > LRULIST ;
  417. typedef TDListHead< CacheState, &CacheState::REFSDLIST > REFSLIST ;
  418. typedef TDListIterator< REFSLIST > REFSITER ;
  419. class CacheTable : public CScheduleThread {
  420. /*++
  421. Class Description :
  422. This class defines a call back interface which we hand to
  423. the LRU List to manipulate the items in the cache !
  424. --*/
  425. public :
  426. //
  427. // Get the lock we use for manipulating the lock state of the table !
  428. //
  429. virtual CACHELOCK&
  430. GetLock() = 0 ;
  431. //
  432. // Remove an item from the Cache's hash table !
  433. //
  434. virtual BOOL
  435. RemoveEntry(
  436. CacheState* pEntry
  437. ) = 0 ;
  438. //
  439. // Ask if we want to remove this particular item !
  440. //
  441. virtual BOOL
  442. QueryRemoveEntry(
  443. CacheState* pEntry
  444. ) = 0 ;
  445. } ;
  446. class CLRUList {
  447. /*++
  448. Class Description :
  449. This class implements our LRU algorithms and selects
  450. the elements that are to be deleted from the cache !
  451. --*/
  452. private :
  453. //
  454. // The head of the LRU List !
  455. //
  456. LRULIST m_LRUList ;
  457. //
  458. // A list of items that have been touched in the LRU List !
  459. //
  460. TLockQueue< CacheState > m_lqModify ;
  461. //
  462. // Maximum number of elements we should hold in this cache !
  463. //
  464. DWORD m_cMaxElements ;
  465. //
  466. // Number of items we inserted !
  467. //
  468. DWORD m_dwAverageInserts ;
  469. //
  470. // Number of items NOT in the LRU List !
  471. //
  472. DWORD m_cCheckedOut ;
  473. //
  474. // The number of subtract from the current time to
  475. // determine our expire date !
  476. //
  477. ULARGE_INTEGER m_qwExpire ;
  478. //
  479. // This function actually selects the items that will be expired
  480. //
  481. void
  482. SelectExpirations( DLIST_ENTRY& expireList ) ;
  483. //
  484. // Do the expires !
  485. //
  486. DWORD
  487. DoExpirations( DLIST_ENTRY& expireList ) ;
  488. //
  489. // Don't allow copies!
  490. //
  491. CLRUList( CLRUList& ) ;
  492. CLRUList& operator=( CLRUList& ) ;
  493. public :
  494. //
  495. // Number of items in the cache !
  496. //
  497. long m_cItems ;
  498. //CLRUList( ULARGE_INTEGER qwExpire ) ;
  499. CLRUList() ;
  500. //
  501. // Initialize the LRU List with the parameters
  502. // controlling the maximum number of elements and the time to live !
  503. //
  504. void
  505. Init( DWORD cMaxInstances,
  506. DWORD cLifeTimeSeconds
  507. ) ;
  508. //
  509. // Something has changed - put it in the list of items needing
  510. // to be examined !
  511. //
  512. // This function should only be called when the containing cache has
  513. // a lock on the cache !
  514. //
  515. void
  516. AddWorkQueue( CacheState* pbase ) ;
  517. //
  518. // This function examines each item in the Work Queue and does
  519. // appropriate processing !
  520. //
  521. void
  522. ProcessWorkQueue( CAllocatorCache* pAllocCache,
  523. LPVOID lpv
  524. ) ;
  525. //
  526. // This function drains each item out of the Work Queue and releases
  527. // them - called during shutdown/destruction of a cache !
  528. //
  529. void
  530. DrainWorkQueue() ;
  531. //
  532. // Bump the number of items in the cache !
  533. //
  534. long
  535. IncrementItems() {
  536. return InterlockedIncrement( &m_cItems ) ;
  537. }
  538. //
  539. // Decrease the number of items in the cache !
  540. //
  541. long
  542. DecrementItems() {
  543. return InterlockedDecrement( &m_cItems ) ;
  544. }
  545. //
  546. // Do the work required to expire an item !
  547. //
  548. void
  549. Expire( CacheTable* pTable,
  550. CAllocatorCache* pCache,
  551. DWORD& countExpired,
  552. void* pv //per cache data !
  553. ) ;
  554. BOOL
  555. Empty( CacheTable* pTable,
  556. CAllocatorCache* pCache,
  557. void* pv
  558. ) ;
  559. //
  560. // Remove a random set of elements in the table !
  561. //
  562. void
  563. ExpungeItems(
  564. CacheTable* pTable,
  565. DWORD& countExpunged
  566. ) ;
  567. } ;
  568. template< class Data
  569. >
  570. class CCacheItemBase : public CacheState {
  571. /*++
  572. Class Desceription :
  573. This class provides the interface defination for
  574. items in the cache which may or may not hold the
  575. key as well within the cache !
  576. Arguments :
  577. Data - the item we will hold within the Cache
  578. --*/
  579. protected:
  580. //
  581. // Constructor - initialize stuff to zero !
  582. //
  583. CCacheItemBase( class CLRUList* p,
  584. Data* pData,
  585. BOOL fClientRef
  586. ) :
  587. CacheState( p, fClientRef ),
  588. m_pData( pData ) {
  589. }
  590. ~CCacheItemBase() {
  591. //
  592. // Somebody else must free our data elements !
  593. //
  594. _ASSERT(m_pData == 0 ) ;
  595. }
  596. BOOL
  597. IsMasterReference() {
  598. return
  599. m_pData == 0 ||
  600. m_pData->m_pCacheRefInterface == this ;
  601. }
  602. CacheState*
  603. GetMasterReference( ) {
  604. if( m_pData == 0 ) {
  605. return this ;
  606. }
  607. return (CacheState*)m_pData->m_pCacheRefInterface ;
  608. }
  609. public :
  610. //
  611. // The actual item we are holding within the Cache !
  612. //
  613. Data* m_pData ;
  614. //
  615. // Check that our data items match !
  616. // used for _ASSERT's
  617. //
  618. BOOL
  619. IsMatch( Data* p ) {
  620. return p == m_pData ;
  621. }
  622. //
  623. // Must be able extract the Data Item !
  624. //
  625. Data*
  626. PublicData( class CLRUList* p ) {
  627. /*++
  628. Routine Description :
  629. This function adds a client reference to an item in the cache,
  630. and sets up the necessary LRU Manipulations!
  631. We also return the data pointer to the client, and add a reference
  632. to ourselves to keep track of !
  633. Arguments :
  634. p - the LRUList that owns us !
  635. Return Value :
  636. Pointer to the Data item we contain - can be NULL !
  637. --*/
  638. TraceFunctEnter( "CCacheItemBase::PublicData" ) ;
  639. _ASSERT( p != 0 ) ;
  640. _ASSERT( p == m_pOwner ) ;
  641. Data* pReturn = 0 ;
  642. m_lock.ShareLock() ;
  643. DebugTrace( (DWORD_PTR)this, "m_pData %x m_pCacheRef %x",
  644. m_pData, m_pData ? m_pData->m_pCacheRefInterface : 0 ) ;
  645. if( m_pData ) {
  646. CacheState* pState = (CacheState*)m_pData->m_pCacheRefInterface ;
  647. pState->CheckOut( p ) ;
  648. pReturn = m_pData ;
  649. }
  650. //
  651. // If there's no data in this item - it must not be checked out !
  652. //
  653. _ASSERT( pReturn || !IsCheckedOut() ) ;
  654. m_lock.ShareUnlock() ;
  655. return pReturn ;
  656. }
  657. //
  658. // This is called when the locks are NOT held -
  659. // do all the correct logic for setting this item up !
  660. //
  661. BOOL
  662. SetData( Data* pData,
  663. CLRUList* pList,
  664. long cClientRefs
  665. ) {
  666. /*++
  667. Routine Description :
  668. Make this Cache Element point to some piece of data that was provided
  669. by the end user.
  670. NOTE :
  671. The piece of data may be referenced by another cache - in which case
  672. we must work well with it !!!!
  673. Arguments :
  674. pData - the Item the client wants us to refer to with our key
  675. pList - the LRU List containing us !
  676. fReference - Is the client putting the item in the cache and not keeping
  677. his reference, this is TRUE if
  678. --*/
  679. TraceFunctEnter( "CCacheItemBase::SetData" ) ;
  680. BOOL fReturn = FALSE ;
  681. _ASSERT( pData != 0 ) ;
  682. _ASSERT( pList != 0 ) ;
  683. _ASSERT( pList == m_pOwner ) ;
  684. DebugTrace( (DWORD_PTR)this, "pData %x pList %x cClientRefs %x", pData, pList, cClientRefs ) ;
  685. //
  686. // Is this an item referenced by another cache -
  687. // NOTE : IF it is an item from another Cache it MUST be checked out with a
  688. // client reference ! The m_pCacheRefInterface of an item MUST NEVER CHANGED
  689. // as long as an item is checked out by clients, so the following dereference
  690. // is safe !
  691. //
  692. CCacheItemBase* p = (CCacheItemBase*)pData->m_pCacheRefInterface ;
  693. DebugTrace( (DWORD_PTR)this, "Examined Item - m_pCacheRefInterface %x", p ) ;
  694. if( p != 0 ) {
  695. //
  696. // In this scenario it is meaningless to access for a client reference -
  697. // the client MUST have gotten one from the other cache !
  698. //
  699. _ASSERT( cClientRefs == 0 ) ;
  700. _ASSERT( p->IsCheckedOut() ) ;
  701. //
  702. // Ok make our cache reference the same item !
  703. //
  704. fReturn = p->AddCacheReference( this, pData ) ;
  705. //
  706. // This can fail if the user tries to insert the same name twice !
  707. //
  708. //_ASSERT( fReturn ) ;
  709. } else {
  710. m_lock.ExclusiveLock() ;
  711. if( !m_pData ) {
  712. //
  713. // The Cache should never have refences outstanding with
  714. // the data pointer equal to NULL - so _ASSERT that we're
  715. // not checked out by a client !
  716. //
  717. _ASSERT( !IsCheckedOut() ) ;
  718. //
  719. // Now point to the data the client wants us to point at !
  720. //
  721. m_pData = pData ;
  722. pData->m_pCacheRefInterface = this ;
  723. //
  724. // It worked - return TRUE !
  725. //
  726. fReturn = TRUE ;
  727. //
  728. // Now we are checked out by a client !
  729. //
  730. if( cClientRefs )
  731. CheckOut( pList, cClientRefs ) ;
  732. }
  733. m_lock.ExclusiveUnlock() ;
  734. }
  735. return fReturn ;
  736. }
  737. //
  738. // Add an item to the list of caches referencing
  739. // this cache item !
  740. //
  741. BOOL
  742. InsertRef( CCacheItemBase<Data>* p,
  743. Data* pData,
  744. long cClientRefs = 0
  745. ) {
  746. /*++
  747. Routine Description :
  748. --*/
  749. TraceFunctEnter( "CCacheItemBase::InsertRef" ) ;
  750. _ASSERT( p != 0 ) ;
  751. _ASSERT( pData != 0 ) ;
  752. _ASSERT( IsCheckedOut() ) ;
  753. _ASSERT( pData->m_pCacheRefInterface == this ) ;
  754. BOOL fReturn = FALSE ;
  755. REFSITER refsiter( &m_ReferencesList ) ;
  756. DebugTrace( (DWORD_PTR)this, "m_pData %x p %x p->m_pData %x cClientRefs %x",
  757. m_pData, p, p->m_pData, cClientRefs ) ;
  758. if( m_pData != 0 ) {
  759. //
  760. // Now grab the second lock !
  761. //
  762. // This _ASSERT isn't valid, the user can insert names that are already in use !
  763. //
  764. //_ASSERT( p->m_pData == 0 || p->m_pData == m_pData ) ;
  765. if( p->m_pData == 0 ) {
  766. //
  767. // Insert p into the list of item in the list !
  768. //
  769. refsiter.InsertBefore( p ) ;
  770. //
  771. // We've added another reference to ourself !
  772. // This comes from another Cache item, so count it specially !
  773. //
  774. long l = AddRef() ;
  775. DebugTrace( (DWORD_PTR)this, "AddRef result %x this %x p %x", l, this, p ) ;
  776. //
  777. // Give a reference to the data we are holding !
  778. //
  779. p->m_pData = m_pData ;
  780. fReturn = TRUE ;
  781. } else if( p->m_pData == m_pData ) {
  782. fReturn = TRUE ;
  783. }
  784. // This _ASSERT isn't valid, the user can insert names that are already in use !
  785. //
  786. //_ASSERT( p->m_pData == m_pData ) ;
  787. }
  788. if( fReturn && cClientRefs ) {
  789. CheckOut( m_pOwner, cClientRefs ) ;
  790. }
  791. return fReturn ;
  792. }
  793. //
  794. // Add an item to the list of caches referencing
  795. // this cache item !
  796. //
  797. virtual BOOL
  798. AddCacheReference( class ICacheRefInterface* pInterface,
  799. void* pv,
  800. BOOL fReference = FALSE
  801. ) {
  802. /*++
  803. Routine Description :
  804. --*/
  805. TraceFunctEnter( "CCacheItemBase::AddCacheReference" ) ;
  806. Data* pData = (Data*)pv ;
  807. CCacheItemBase<Data>* p = (CCacheItemBase<Data>*)pInterface ;
  808. BOOL fReturn = TRUE ;
  809. _ASSERT( pData != 0 ) ;
  810. _ASSERT( p != 0 ) ;
  811. REFSITER refsiter( &m_ReferencesList ) ;
  812. DebugTrace( (DWORD_PTR)this, "pInterface %x pv %x fReference %x", pInterface, pv, fReference ) ;
  813. m_lock.ExclusiveLock() ;
  814. p->m_lock.ExclusiveLock() ;
  815. fReturn = InsertRef( p,
  816. pData,
  817. fReference
  818. ) ;
  819. p->m_lock.ExclusiveUnlock() ;
  820. _ASSERT( m_pData == pData ) ;
  821. m_lock.ExclusiveUnlock() ;
  822. return fReturn ;
  823. }
  824. //
  825. // Remove an item from the list of caches referencing
  826. // this cache item !
  827. //
  828. virtual BOOL
  829. RemoveCacheReference( BOOL fQueue ) {
  830. TraceFunctEnter( "CCacheItemBase::RemoveCacheReference" ) ;
  831. DebugTrace( (DWORD_PTR)this, "m_pData %x m_pData->m_pCacheRefInterface %x",
  832. m_pData, m_pData ? m_pData->m_pCacheRefInterface: 0 ) ;
  833. m_pData = 0 ;
  834. //
  835. // Well now - we're on our own - we should be expired !
  836. //
  837. if( fQueue )
  838. LRUReference( m_pOwner ) ;
  839. //
  840. // p no longer has a reference - you can remove it !
  841. //
  842. return FALSE ;
  843. }
  844. //
  845. // Remove all references to the cache item !
  846. //
  847. virtual BOOL
  848. RemoveAllReferences( ) {
  849. return FALSE ;
  850. }
  851. } ;
  852. template< class Data,
  853. class Key,
  854. class Constructor,
  855. class PerCacheData
  856. >
  857. class CCacheItemKey : public CCacheItemBase< Data > {
  858. /*++
  859. Class Description :
  860. This class item holds the key of the item we are
  861. referencing within the cache !
  862. We don't assume that the data objects hold the
  863. keys for us - we do this ourselves.
  864. --*/
  865. private :
  866. //
  867. // This is the type of the key we are going to be holding !
  868. //
  869. Key m_key ;
  870. //
  871. // Make all these constructors and copiers private !
  872. //
  873. CCacheItemKey() ;
  874. CCacheItemKey& operator=( CCacheItemKey& ) ;
  875. protected :
  876. //
  877. // Destroy ourselves
  878. //
  879. void
  880. Destroy( void* pv ) {
  881. TraceFunctEnter( "CCacheItemKey::Destroy" ) ;
  882. DebugTrace( (DWORD_PTR)this, "m_pData %x pv %x", m_pData, pv ) ;
  883. PerCacheData* p = (PerCacheData*)pv ;
  884. if( m_pData )
  885. Constructor::StaticRelease( m_pData, pv ) ;
  886. m_pData = 0 ;
  887. CCacheItemKey::~CCacheItemKey() ;
  888. }
  889. public :
  890. ~CCacheItemKey() {
  891. if( m_pData ) {
  892. Constructor::StaticRelease( m_pData, 0 ) ;
  893. m_pData = 0 ;
  894. }
  895. }
  896. //
  897. // Can only create by initializing the key !
  898. //
  899. CCacheItemKey( class CLRUList* p,
  900. Key& k,
  901. Data* pData,
  902. long cClientRefs
  903. ) :
  904. CCacheItemBase<Data>( p, pData, cClientRefs ),
  905. m_key( k ) {
  906. }
  907. Key* GetKey() {
  908. return &m_key ;
  909. }
  910. //
  911. // This is called when no locks are held - assumes
  912. // that we may find that data is in the item, or that
  913. // we need to build it !
  914. //
  915. Data*
  916. FindOrCreate(
  917. CACHELOCK& cachelock,
  918. Constructor& constructor,
  919. PerCacheData& cachedata,
  920. CLRUList* plrulist,
  921. class CacheStats* pStats
  922. ) {
  923. /*++
  924. Routine Description :
  925. This function executes our creation protocol with the client.
  926. The constructor.Create() function is called to create a partially
  927. constructed Item for the cache. We will check if the returned item
  928. is referenced in another cache - if it is we will build the list
  929. of CacheState objects referencing the same cache item.
  930. The caller assumes that cachelock is released by the time we return !
  931. Arguments :
  932. cachelock - The lock for the containing cache, we get this so that
  933. we can minimize the time it is held !
  934. constructor -
  935. The object that can build an item for the cache !
  936. cachedata -
  937. Some client data that they get for free !
  938. plrulist -
  939. The LRU list that we should use !
  940. Return Value :
  941. Pointer to a Data item if successfull !
  942. --*/
  943. TraceFunctEnter( "CCacheItemKey::FindOrCreate" ) ;
  944. _ASSERT( plrulist == m_pOwner || plrulist == 0 ) ;
  945. DebugTrace( (DWORD_PTR)this, "plrulist %x", plrulist ) ;
  946. Data* pReturn = 0 ;
  947. //
  948. // First look to see if there happens to be something here already !
  949. //
  950. m_lock.ShareLock() ;
  951. if( m_pData ) {
  952. DebugTrace( (DWORD_PTR)this, "m_pData %x m_pData->m_pCacheRefInterface %x",
  953. m_pData, m_pData->m_pCacheRefInterface ) ;
  954. //
  955. // We've found this item constructed in the cache -
  956. // so check it out, AND put on the LRU work list !
  957. //
  958. CacheState* pState = (CacheState*)m_pData->m_pCacheRefInterface ;
  959. pState->CheckOut( plrulist ) ;
  960. pReturn = m_pData ;
  961. }
  962. m_lock.ShareUnlock() ;
  963. DebugTrace( (DWORD_PTR)this, "pReturn %x", pReturn ) ;
  964. //
  965. // Did we find something !
  966. //
  967. if( pReturn ) {
  968. //
  969. // Because caller assumes this is unlocked - do so now !
  970. //
  971. cachelock.PartialUnlock() ;
  972. } else {
  973. //
  974. // An item with no data - Must not be checked out !
  975. //
  976. _ASSERT( !IsCheckedOut() ) ;
  977. _ASSERT( m_pData == 0 ) ;
  978. //
  979. // Partially build the data object we want to hold !
  980. //
  981. Data* pData = constructor.Create( m_key, cachedata ) ;
  982. DebugTrace( (DWORD_PTR)this, "Created pData %x", pData ) ;
  983. if( !pData ) {
  984. //
  985. // We failed - release our locks and go away !
  986. //
  987. cachelock.PartialUnlock() ;
  988. } else {
  989. //
  990. // Figure out if we got a reference to an item already in the cache !
  991. //
  992. CCacheItemBase<Data>* p = (CCacheItemBase<Data>*)pData->m_pCacheRefInterface ;
  993. //
  994. // Grab the locks in the correct order for what we might need !
  995. //
  996. if( p ) {
  997. p->ExclusiveLock() ;
  998. //
  999. // If he gave us an item, it must be checked out of whatever cache
  1000. // it came from - this ensures that it will not be destroyed while we
  1001. // access it, because we are not at the point of adding our own reference
  1002. // yet !
  1003. //
  1004. _ASSERT( p->IsCheckedOut() ) ;
  1005. _ASSERT( p->IsMatch( pData ) ) ;
  1006. }
  1007. m_lock.ExclusiveLock() ;
  1008. //
  1009. // Don't need to hold onto the cache anymore !
  1010. //
  1011. cachelock.PartialUnlock() ;
  1012. DebugTrace( (DWORD_PTR)this, "Create path - pData %x p %x m_pData %x", pData, p, m_pData ) ;
  1013. //
  1014. // Now do whatever is necessary to finish initialization ! -
  1015. // must always call Init() unless we're going to give up on this thing !
  1016. //
  1017. //
  1018. // We should not change state as long as we've been holding either
  1019. // the cachelock or our item lock up until this point - which is the case !
  1020. //
  1021. _ASSERT( m_pData == 0 ) ;
  1022. if( pData->Init( m_key,
  1023. constructor,
  1024. cachedata ) ) {
  1025. if( !p ) {
  1026. m_pData = pData ;
  1027. pData->m_pCacheRefInterface = this ;
  1028. pReturn = m_pData ;
  1029. CheckOut( m_pOwner ) ;
  1030. } else {
  1031. //
  1032. // NOTE : If the client's constructor gave us an object from another
  1033. // cache they MUST add the client's reference for us - so we pass 0 to
  1034. // InsertRef(), so that we don't add yet another reference.
  1035. //
  1036. if( p->InsertRef( this, pData, 0 ) ) {
  1037. //
  1038. // Insert Ref should setup our m_pData pointer !
  1039. //
  1040. _ASSERT( m_pData == pData ) ;
  1041. pReturn = m_pData ;
  1042. }
  1043. }
  1044. }
  1045. DebugTrace( (DWORD_PTR)this, "Create path - pReturn %x", pReturn ) ;
  1046. //
  1047. // NOTE :
  1048. // If pReturn==0 indicating some kind of error, than there should
  1049. // have been no client refs added at this point !
  1050. //
  1051. _ASSERT( pReturn || !IsCheckedOut() ) ;
  1052. _ASSERT( pReturn == m_pData ) ;
  1053. if( p ) {
  1054. //
  1055. // If he gave us an item, it must be checked out of whatever cache
  1056. // it came from - this ensures that it will not be destroyed while we
  1057. // access it, because we are not at the point of adding our own reference
  1058. // yet !
  1059. //
  1060. _ASSERT( p->IsCheckedOut() ) ;
  1061. p->ExclusiveUnlock() ;
  1062. }
  1063. if( pReturn ) {
  1064. IncrementStat( pStats, CACHESTATS::ITEMS ) ;
  1065. } else {
  1066. //
  1067. // Release the data item back to the user
  1068. // NOTE - Don't have the cachelock so can't give them
  1069. // the cachedata at this point !
  1070. //
  1071. constructor.Release( pData, 0 ) ;
  1072. //
  1073. // Insure that we get onto the expiration list - this CACHEENTRY
  1074. // should be removed at some point !
  1075. //
  1076. FailedCheckOut( plrulist, FALSE, 0, 0 ) ;
  1077. }
  1078. //
  1079. // Release the locks - this can be done before we go down the
  1080. // error path because we know that we won't get destroyed !
  1081. //
  1082. m_lock.ExclusiveUnlock() ;
  1083. }
  1084. }
  1085. return pReturn ;
  1086. }
  1087. } ;
  1088. #endif // _CINTRNL_H_