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.

1995 lines
40 KiB

  1. /*++
  2. cache2i.h -
  3. This file contains all the template function definitions required
  4. to make the cache manager work.
  5. --*/
  6. template < class Data, class Key,
  7. class Constructor,
  8. class PerCacheData
  9. >
  10. void
  11. CacheEx< Data, Key, Constructor, PerCacheData >::Schedule() {
  12. /*++
  13. Routine Description :
  14. This function runs through all the items in the Cache
  15. bumping TTL's. If there are any items ready to go
  16. then we dump them from the Cache.
  17. Arguments :
  18. None.
  19. Return Value :
  20. Nothing
  21. --*/
  22. //TraceFunctEnter( "CacheEx::Schedule" ) ;
  23. if( !m_fValid )
  24. return ;
  25. DWORD cExpired = 0 ;
  26. Expire( ) ;
  27. }
  28. template < class Data, class Key,
  29. class Constructor,
  30. class PerCacheData
  31. >
  32. BOOL
  33. CacheEx<Data, Key, Constructor, PerCacheData>::RemoveEntry(
  34. CacheState* pEntry
  35. ) {
  36. /*++
  37. Routine Description :
  38. This function removes an entry from the Cache.
  39. We call our hash table to delete the item.
  40. the CacheState destructor automagically removes
  41. the item from our linked lists.
  42. CALLER MUST HOLD APPROPRIATE LOCKS!
  43. Arguments :
  44. pEntry - item to be removed from cache
  45. Return Value :
  46. TRUE if successfully removed !
  47. --*/
  48. TraceFunctEnter( "CacheEx::RemoveEntry" ) ;
  49. DebugTrace( (DWORD_PTR)this, "pEntry %x", pEntry ) ;
  50. CACHEENTRY *pCacheEntry = (CACHEENTRY*)pEntry ;
  51. m_Lookup.Delete( pCacheEntry ) ;
  52. m_ExpireList.DecrementItems() ;
  53. //
  54. // Update our counters !
  55. //
  56. DecrementStat( m_pStats, CACHESTATS::ITEMS ) ;
  57. return TRUE ;
  58. }
  59. template < class Data, class Key,
  60. class Constructor,
  61. class PerCacheData
  62. >
  63. BOOL
  64. CacheEx<Data, Key, Constructor, PerCacheData>::QueryRemoveEntry(
  65. CacheState* pEntry ) {
  66. /*++
  67. Routine Description :
  68. This function is called from CacheList object to
  69. determine whether we want to remove an item from the Cache.
  70. This function is used to implement the ExpungeSpecific
  71. function available to clients.
  72. CALLER MUST HOLD APPROPRIATE LOCKS!
  73. Arguments :
  74. pEntry - item we want to determine whether it should remain !
  75. Return Value :
  76. TRUE if successfully removed !
  77. --*/
  78. TraceFunctEnter( "CacheEx::QueryRemoveEntry" ) ;
  79. CACHEENTRY *pCacheEntry = (CACHEENTRY*) pEntry ;
  80. if( m_pCallbackObject ) {
  81. // return m_pCallbackObject->fRemoveCacheItem( *pCacheEntry->m_pData ) ;
  82. }
  83. return FALSE ;
  84. }
  85. #ifdef DEBUG
  86. template < class Data, class Key,
  87. class Constructor,
  88. class PerCacheData
  89. >
  90. long CacheEx<Data, Key, Constructor, PerCacheData>::s_cCreated = 0 ;
  91. #endif
  92. template < class Data, class Key,
  93. class Constructor,
  94. class PerCacheData
  95. >
  96. CacheEx<Data, Key, Constructor, PerCacheData>::CacheEx( ) :
  97. m_fValid( FALSE ),
  98. m_Cache( sizeof( CACHEENTRY ) ) {
  99. /*++
  100. Routine Description :
  101. This function initializes our member variables.
  102. Arguments :
  103. cMax - maximum number of elements the cache should hold
  104. Return Value :
  105. Nothing
  106. --*/
  107. #ifdef DEBUG
  108. InterlockedIncrement( &s_cCreated ) ;
  109. #endif
  110. AddToSchedule() ;
  111. }
  112. template < class Data, class Key,
  113. class Constructor,
  114. class PerCacheData
  115. >
  116. CacheEx<Data, Key, Constructor, PerCacheData>::~CacheEx( ) {
  117. /*++
  118. Routine Description :
  119. This function destroys a Cache object !
  120. Arguments :
  121. None
  122. Return Value :
  123. Nothing
  124. --*/
  125. RemoveFromSchedule() ;
  126. EnterCriticalSection( &g_CacheShutdown ) ;
  127. //
  128. // Member and Base class destruction follows !!
  129. //
  130. BOOL f = EmptyCache() ;
  131. LeaveCriticalSection( &g_CacheShutdown ) ;
  132. _ASSERT( f ) ;
  133. #ifdef DEBUG
  134. InterlockedDecrement( &s_cCreated ) ;
  135. #endif
  136. }
  137. #if 0
  138. template < class Data, class Key,
  139. class Constructor,
  140. class PerCacheData
  141. >
  142. BOOL
  143. CacheEx<Data, Key, Constructor, PerCacheData>::Init(
  144. PFNHASH pfnHash,
  145. PKEYCOMPARE pKeyCompare,
  146. DWORD dwLifetimeSeconds,
  147. DWORD cMaxInstances,
  148. PSTOPHINT_FN pfnStopHint
  149. ) {
  150. /*++
  151. Routine Description :
  152. This function initializes the cache so that it is ready
  153. to take entries.
  154. Arguments :
  155. pfnHash - function to be used to compute hash values on keys
  156. dwLifetimeSeconds - The number of seconds objects should live in the Cache
  157. pfnStopHint - function to be used to send stop hints during
  158. long spins so shutdown's don't time out.
  159. Return Value :
  160. TRUE if successfull
  161. --*/
  162. m_ExpireList.Init( cMaxInstances,
  163. dwLifeTimeSeconds
  164. ) ;
  165. return m_fValid = m_Lookup.Init(
  166. 256,
  167. 128,
  168. 4,
  169. pfnHash,
  170. &CACHEENTRY::GetKey,
  171. pKeyCompare
  172. ) ;
  173. }
  174. #endif
  175. template < class Data,
  176. class Key,
  177. class Constructor,
  178. class PerCacheData
  179. >
  180. BOOL
  181. CacheEx<Data, Key, Constructor, PerCacheData>::EmptyCache( ) {
  182. /*++
  183. Routine Description :
  184. This function removes everything from the cache.
  185. We are used during shutdown and destruction of the cache.
  186. Arguments :
  187. None
  188. Return Value :
  189. TRUE if successfull
  190. FALSE - means that there are still items checked out of the cache -
  191. a NO-NO !
  192. --*/
  193. if (!m_fValid) {
  194. return TRUE;
  195. }
  196. BOOL fReturn = TRUE ;
  197. FILETIME filetimeNow ;
  198. ZeroMemory( &filetimeNow, sizeof( filetimeNow ) ) ;
  199. BOOL fTerm = FALSE ;
  200. m_Lock.ExclusiveLock() ;
  201. m_ExpireList.ProcessWorkQueue(0,0) ;
  202. //BOOL fReturn = m_ExpireList.Empty( this, &m_Cache, &m_PerCacheData ) ;
  203. HASHITER Iter( m_Lookup ) ;
  204. while( !Iter.AtEnd() ) {
  205. CACHEENTRY* pEntry = Iter.Current() ;
  206. //BOOL fLock = pEntry->FLockCandidate( FALSE, filetimeNow, fTerm ) ;
  207. //_ASSERT( fLock ) ;
  208. pEntry->IsolateCandidate() ;
  209. CACHEENTRY* pTemp = Iter.RemoveItem() ;
  210. m_ExpireList.DecrementItems() ;
  211. _ASSERT( pTemp == pEntry ) ;
  212. long l = pTemp->Release( &m_Cache, &m_PerCacheData ) ;
  213. _ASSERT( l== 0 || l == 1) ;
  214. }
  215. m_ExpireList.DrainWorkQueue() ;
  216. m_Lock.ExclusiveUnlock() ;
  217. return fReturn ;
  218. }
  219. template < class Data,
  220. class Key,
  221. class Constructor,
  222. class PerCacheData
  223. >
  224. BOOL
  225. CacheEx<Data, Key, Constructor, PerCacheData>::ExpungeItems(
  226. EXPUNGEOBJECT* pExpunge
  227. ) {
  228. /*++
  229. Routine Description :
  230. This function removes everything from the cache.
  231. We are used during shutdown and destruction of the cache.
  232. Arguments :
  233. None
  234. Return Value :
  235. TRUE if successfull
  236. FALSE - means that there are still items checked out of the cache -
  237. a NO-NO !
  238. --*/
  239. BOOL fReturn = TRUE ;
  240. m_Lock.ExclusiveLock() ;
  241. //
  242. // Build an iterator that can walk our hash tables !
  243. //
  244. HASHITER Iter( m_Lookup ) ;
  245. //
  246. // Examine everything in the hash tables !
  247. //
  248. while( !Iter.AtEnd() ) {
  249. CACHEENTRY* pEntry = Iter.Current() ;
  250. //
  251. // We only let you Expunge Master Entries !
  252. //
  253. CacheState* pMaster = 0 ;
  254. BOOL fLocked = pEntry->FLockExpungeCandidate(pMaster) ;
  255. if( fLocked ) {
  256. //
  257. // Ask the user if they want to delete this item !
  258. //
  259. if( pExpunge->fRemoveCacheItem( pEntry->GetKey(),
  260. pEntry->m_pData
  261. ) ) {
  262. CACHEENTRY* pTemp = Iter.RemoveItem() ;
  263. m_ExpireList.DecrementItems() ;
  264. _ASSERT( pTemp == pEntry ) ;
  265. //
  266. // This removes the cache item from any association
  267. // it may have with other cache items ! In addition
  268. // it drops the locks we were holding !
  269. //
  270. pTemp->FinishCandidate(pMaster) ;
  271. //
  272. // Drop the cache's reference - this will leave the
  273. // item orphaned - to be destroyed when the final
  274. // client reference is released !
  275. //
  276. long l = pTemp->Release( &m_Cache, &m_PerCacheData ) ;
  277. continue ;
  278. } else {
  279. //
  280. // We're not deleting this guy - release any locks we're holding !
  281. //
  282. pEntry->ReleaseLocks(pMaster) ;
  283. }
  284. }
  285. Iter.Next() ;
  286. }
  287. m_Lock.ExclusiveUnlock() ;
  288. return fReturn ;
  289. }
  290. template < class Data,
  291. class Key,
  292. class Constructor,
  293. class PerCacheData
  294. >
  295. BOOL
  296. CacheEx<Data, Key, Constructor, PerCacheData>::ExpungeKey(
  297. DWORD dwHash,
  298. PKEY pKeyExpunge
  299. ) {
  300. /*++
  301. Routine Description :
  302. This function removes everything from the cache.
  303. We are used during shutdown and destruction of the cache.
  304. Arguments :
  305. None
  306. Return Value :
  307. TRUE if successfull
  308. FALSE - means that there are still items checked out of the cache -
  309. a NO-NO !
  310. --*/
  311. TraceFunctEnter( "CacheEx::ExpungeKey" ) ;
  312. _ASSERT( dwHash == m_Lookup.ComputeHash( pKeyExpunge ) ) ;
  313. BOOL fReturn = FALSE ;
  314. m_Lock.PartialLock() ;
  315. Data* pDataOut = 0 ;
  316. if( !m_fValid ) {
  317. SetLastError( ERROR_NOT_SUPPORTED ) ;
  318. } else {
  319. //
  320. // See if we can find an Entry in the cache for the item we want.
  321. //
  322. CACHEENTRY *pEntry = m_Lookup.SearchKey( dwHash, pKeyExpunge ) ;
  323. if( pEntry ) {
  324. //
  325. // We've found the entry we want to remove - now go to an Exclusive Lock
  326. // so we can ensure people don't blow up walking hash bucket chains
  327. //
  328. m_Lock.FirstPartialToExclusive() ;
  329. CacheState* pMaster ;
  330. BOOL fLocked = pEntry->FLockExpungeCandidate(pMaster) ;
  331. //
  332. // If we successfully locked the item, we're ready to go ahead an remove the item !
  333. //
  334. if( fLocked ) {
  335. //
  336. // get rid of the guy !
  337. //
  338. m_Lookup.Delete( pEntry ) ;
  339. m_ExpireList.DecrementItems() ;
  340. pEntry->FinishCandidate(pMaster) ;
  341. //
  342. // This could be the final reference, (but maybe not if clients are still holding it etc...)
  343. //
  344. long l = pEntry->Release( &m_Cache, &m_PerCacheData ) ;
  345. fReturn = TRUE ;
  346. }
  347. m_Lock.ExclusiveUnlock() ;
  348. return fReturn ;
  349. }
  350. }
  351. m_Lock.PartialUnlock() ;
  352. return FALSE ;
  353. }
  354. template < class Data, class Key,
  355. class Constructor,
  356. class PerCacheData
  357. >
  358. BOOL
  359. CacheEx<Data, Key, Constructor, PerCacheData>::FindOrCreateInternal(
  360. DWORD dwHash,
  361. Key& key,
  362. Constructor& constructor,
  363. Data* &pDataOut,
  364. BOOL fEarlyCreate
  365. ) {
  366. /*++
  367. Routine Description :
  368. This function is called when we want something out
  369. of the Cache. We will either find the object or create it.
  370. WE WILL GRAB THE NECESSARY LOCKS !
  371. WE ARE NOT REENTRANT -
  372. At several points we call user provided code - it must
  373. not re-enter this cache !
  374. Arguments :
  375. dwHash - The hash of the key we've been passed
  376. key - The unique key used to find the item in the Cache.
  377. constructor - An object to pass to the Data's constructor and Init
  378. functions.
  379. pDataOut - Pointer which gets the result !
  380. fEarlyCreate - If this is TRUE we should call the constructor.Create()
  381. function early in the Cache Insertion process - this allows us to
  382. correctly deal with the condition where the Create() call may return
  383. a reference to another cache.
  384. Return Value :
  385. TRUE if successfull
  386. --*/
  387. TraceFunctEnter( "CacheEx::FindOrCreateInternal" ) ;
  388. _ASSERT( dwHash == m_Lookup.ComputeHash( &key ) ) ;
  389. Data* pRelease = 0 ;
  390. pDataOut = 0 ;
  391. long cClientRefs = 1 ;
  392. DebugTrace( (DWORD_PTR)this, "Args - dwHash %x fEarlyCreate %x", dwHash, fEarlyCreate ) ;
  393. //
  394. // Check that the cache is initialized !
  395. //
  396. if( !m_fValid ) {
  397. SetLastError( ERROR_NOT_SUPPORTED ) ;
  398. return FALSE ;
  399. } else {
  400. m_Lock.ShareLock() ;
  401. //
  402. // See if we can find an Entry in the cache for the item we want.
  403. //
  404. CACHEENTRY *pEntry = 0 ;
  405. HASHTABLE::ITER iter = m_Lookup.SearchKeyHashIter( dwHash, &key, pEntry ) ;
  406. if( pEntry ) {
  407. pDataOut = pEntry->PublicData( &m_ExpireList ) ;
  408. }
  409. DebugTrace( (DWORD_PTR)this, "Initial Search - pDataOut %x", pDataOut ) ;
  410. if( pDataOut != 0 ) {
  411. //
  412. // We're all done - release the lock !
  413. //
  414. m_Lock.ShareUnlock() ;
  415. //
  416. // Increment our statistics !
  417. //
  418. IncrementStat( m_pStats, CACHESTATS::READHITS ) ;
  419. } else {
  420. //
  421. // Try to convert our shared lock to a partial lock -
  422. // if we can do so then we don't need to search the hash table again !
  423. //
  424. if( !m_Lock.SharedToPartial() ) {
  425. m_Lock.ShareUnlock() ;
  426. m_Lock.PartialLock() ;
  427. //
  428. // Search the table again - something could happen while we briefly
  429. // dropped the lock !
  430. //
  431. iter = m_Lookup.SearchKeyHashIter( dwHash, &key, pEntry ) ;
  432. IncrementStat( m_pStats, CACHESTATS::RESEARCH ) ;
  433. if( pEntry )
  434. IncrementStat( m_pStats, CACHESTATS::WRITEHITS ) ;
  435. }
  436. DebugTrace( (DWORD_PTR)this, "After possible Second Search - pEntry %x", pEntry ) ;
  437. //
  438. // Time to re-evaluate, is there an entry in the cache !
  439. //
  440. if( pEntry != 0 ) {
  441. //
  442. // Ok - we found an entry in the hash table, but it contained no data -
  443. // so we need to do our FindOrCreate protocol on this element !
  444. //
  445. pDataOut = pEntry->FindOrCreate(
  446. m_Lock,
  447. constructor,
  448. m_PerCacheData,
  449. &m_ExpireList,
  450. m_pStats
  451. ) ;
  452. //
  453. // NOTE : pEntry->FindOrCreate() manipulates the global lock !
  454. // it MUST unlock for us before it returns !
  455. //
  456. //m_Lock.PartialUnlock() ;
  457. } else {
  458. //
  459. // Ok - first try to create the item we will be placing into the cache !
  460. //
  461. if( fEarlyCreate )
  462. pRelease = pDataOut = constructor.Create( key, m_PerCacheData ) ;
  463. DebugTrace( (DWORD_PTR)this, "fEarlyCreate %x pDataOut %x", fEarlyCreate, pDataOut ) ;
  464. if( !fEarlyCreate || pDataOut ) {
  465. //
  466. // This must be the case !
  467. //
  468. _ASSERT( (pDataOut && fEarlyCreate) || (!pDataOut && !fEarlyCreate) ) ;
  469. CCacheItemBase<Data>* pRef = 0 ;
  470. if( pDataOut ) {
  471. pRef = (CCacheItemBase<Data>*)pDataOut->m_pCacheRefInterface ;
  472. //
  473. // Add a client reference IF this item is NOT contained in another cache !
  474. //
  475. cClientRefs = !pRef ? 1 : 0 ;
  476. }
  477. //
  478. // Now - we will see if we can make the container to hold this data item !
  479. // NOTE : we use pRef to figure out if we should add a client reference
  480. // to this object - only do so if pRef is NULL meaning that this CACHEENTRY object
  481. // OWNS the item in the cache !
  482. //
  483. // This all means that pEntry is constructed with 1 or 2 references altogether
  484. // which we need to handle in the error paths !
  485. //
  486. // NOTE : if failure occurs pEntry should only be destroyed with FailedCheckOut() !
  487. //
  488. #ifdef DEBUG
  489. if( fTimeToFail() ) {
  490. pEntry = 0 ;
  491. } else
  492. #endif
  493. pEntry = new( m_Cache ) CACHEENTRY( &m_ExpireList, key, 0, cClientRefs ) ;
  494. DebugTrace( (DWORD_PTR)this, "pEntry %x pRef %x pDataOut %x pEntry->m_pData %x", pEntry, pRef,
  495. pDataOut, pEntry ? pEntry->m_pData : 0 ) ;
  496. if( pEntry != 0 ) {
  497. //
  498. // Just check some basic things here !
  499. //
  500. _ASSERT( !pEntry->IsInLRUList() ) ;
  501. _ASSERT( pEntry->IsCheckedOut() || pRef ) ;
  502. //
  503. // Grab the lock on the entry !
  504. //
  505. if( pRef ) {
  506. pRef->ExclusiveLock() ;
  507. //
  508. // This must be checked out of its native cache,
  509. // otherwise it could be destroyed from under us !
  510. //
  511. _ASSERT( pRef->IsCheckedOut() ) ;
  512. _ASSERT( pRef->IsMatch( pDataOut ) ) ;
  513. }
  514. pEntry->ExclusiveLock() ;
  515. //
  516. // Convert to an Exclusive Lock for inserting into
  517. // the hash table !
  518. //
  519. m_Lock.FirstPartialToExclusive() ;
  520. BOOL fInsert ;
  521. #ifdef DEBUG
  522. //
  523. // Periodically fail to insert into the hash table !
  524. //
  525. if( fTimeToFail() ) {
  526. fInsert = FALSE ;
  527. } else
  528. #endif
  529. fInsert = m_Lookup.InsertDataHashIter( iter, dwHash, &key, pEntry ) ;
  530. DebugTrace( (DWORD_PTR)this, "Insert Results - %x pDataOut %x pEntry %x", fInsert, pDataOut, pEntry ) ;
  531. _ASSERT( pRelease == pDataOut ) ;
  532. //
  533. // Both the global Cache Lock - m_Lock and
  534. // the locks for pEntry must be released within the branches of
  535. // this if !
  536. //
  537. if( fInsert ) {
  538. m_ExpireList.IncrementItems() ;
  539. //
  540. // Don't need to hold the whole cache anymore
  541. //
  542. m_Lock.ExclusiveUnlock() ;
  543. //
  544. // Number of times we've created an item while holding hash lock exclusively !
  545. //
  546. IncrementStat( m_pStats, CACHESTATS::EXCLUSIVECREATES ) ;
  547. //
  548. // This must be the case !
  549. //
  550. _ASSERT( (pDataOut && fEarlyCreate) || (!pDataOut && !fEarlyCreate) ) ;
  551. //
  552. // Now do whatever is necessary to finish initialization ! -
  553. // must always call Init() unless we're going to give up on this thing !
  554. //
  555. // NOTE : Errors at this point can leave a dud CACHEENTRY in the
  556. // cache, which should be cleaned up by expiration !
  557. //
  558. _ASSERT( pRelease == pDataOut ) ;
  559. if( !pDataOut ) {
  560. //
  561. // This should only occur if we were not asked to do early creation !
  562. //
  563. _ASSERT( !fEarlyCreate ) ;
  564. pRelease = pDataOut = constructor.Create( key, m_PerCacheData ) ;
  565. pEntry->m_pData = pDataOut ;
  566. if( !pDataOut ) {
  567. //
  568. // Keep Track of our statistics !
  569. //
  570. IncrementStat( m_pStats, CACHESTATS::CLIENTALLOCFAILS ) ;
  571. }
  572. }
  573. _ASSERT( pRelease == pDataOut ) ;
  574. if( pDataOut && pDataOut->Init( key, constructor, m_PerCacheData ) ) {
  575. if( !pRef ) {
  576. pDataOut->m_pCacheRefInterface = pEntry ;
  577. pEntry->m_pData = pDataOut ;
  578. } else {
  579. #ifdef DEBUG
  580. //
  581. // Periodically fail these operations !
  582. //
  583. if( fTimeToFail() ) {
  584. pDataOut = 0 ;
  585. } else {
  586. #endif
  587. //
  588. // The item resides in another cache, and there must
  589. // already be a user reference on the item, the user
  590. // always gets a client ref through FindOrCreate - but
  591. // they will get the client ref taken out by the
  592. // constructor.Create call to provide us with the item !
  593. //
  594. if( !pRef->InsertRef( pEntry, pDataOut, 0 ) ) {
  595. pDataOut = 0 ;
  596. }
  597. #ifdef DEBUG
  598. } // Part of making the operation fail periodically !
  599. #endif
  600. }
  601. } else {
  602. //
  603. // Should check the item in so that expire catches it !
  604. //
  605. pEntry->m_pData = pDataOut = 0 ;
  606. IncrementStat( m_pStats, CACHESTATS::CLIENTINITFAILS ) ;
  607. }
  608. DebugTrace( (DWORD_PTR)this, "pDataOut is Now %x pEntry %x pEntry->m_pData %x",
  609. pDataOut, pEntry, pEntry->m_pData ) ;
  610. _ASSERT( pDataOut == pEntry->m_pData ) ;
  611. //
  612. // If things have gone bad - remove the client
  613. // ref and put in the expiration list !
  614. //
  615. if( pDataOut ) {
  616. IncrementStat( m_pStats, CACHESTATS::ITEMS ) ;
  617. pRelease = 0 ;
  618. } else {
  619. pEntry->FailedCheckOut( &m_ExpireList,
  620. cClientRefs,
  621. 0, // Can't give them the Allocation Cache - no lock held
  622. 0 // Can't give them per cache stuff - no lock held !
  623. ) ;
  624. _ASSERT( !pDataOut || pDataOut->m_pCacheRefInterface == pRef ) ;
  625. }
  626. //
  627. // Release the lock on the pEntry !
  628. //
  629. pEntry->ExclusiveUnlock() ;
  630. } else {
  631. DebugTrace( (DWORD_PTR)this, "Failed to Insert - pDataOut %x pRelease %x pRef %x pEntry %x",
  632. pDataOut, pRelease, pRef, pEntry ) ;
  633. _ASSERT( !pDataOut || pDataOut->m_pCacheRefInterface == pRef ) ;
  634. _ASSERT( pRelease == pDataOut ) ;
  635. pDataOut = 0 ;
  636. _ASSERT( pEntry->m_pData == pDataOut ) ;
  637. //
  638. // This item should have been checked out !
  639. //
  640. _ASSERT( pEntry->IsCheckedOut() || pRef ) ;
  641. //
  642. // Release the lock on the pEntry object before we destroy it !
  643. //
  644. pEntry->ExclusiveUnlock() ;
  645. //
  646. // Release back to the cache -did we have a client ref -
  647. // have to get rid of that if necessary !
  648. // NOTE : This should totally destroy pEntry - we will also
  649. // remove his last reference and return to the allocator cache !
  650. //
  651. pEntry->FailedCheckOut( &m_ExpireList,
  652. cClientRefs,
  653. &m_Cache,
  654. &m_PerCacheData
  655. ) ;
  656. //
  657. // To prevent a deadlock we have to release this lock !
  658. // This is because the locks used within CACHEENTRY's are not re-entrant
  659. // but if we call constructor.Release() below, we may try to re-enter
  660. // pRef's lock (our client may try to Re-Enter !)
  661. //
  662. if( pRef ) {
  663. _ASSERT( pRef->IsCheckedOut() ) ;
  664. pRef->ExclusiveUnlock() ;
  665. pRef = 0 ; // set to 0
  666. }
  667. if( pRelease != 0 ) {
  668. _ASSERT( pDataOut == 0 ) ;
  669. //
  670. // Well we called the constructors create call - need to
  671. // release the object back to the constructor !
  672. //
  673. constructor.Release( pRelease, &m_PerCacheData ) ;
  674. }
  675. pRelease = 0 ;
  676. //
  677. // Release the hash table lock
  678. //
  679. m_Lock.ExclusiveUnlock() ;
  680. }
  681. if( pRef ) {
  682. //
  683. // This must be checked out of its native cache,
  684. // otherwise it could be destroyed from under us !
  685. //
  686. _ASSERT( pRef->IsCheckedOut() ) ;
  687. pRef->ExclusiveUnlock() ;
  688. pRef = 0 ;
  689. }
  690. //
  691. // pRef should be unlocked by this point -
  692. //
  693. _ASSERT( pRef == 0 ) ;
  694. } else {
  695. //
  696. // Number of times we've failed to alloc a CACHEENTRY object !
  697. //
  698. IncrementStat( m_pStats, CACHESTATS::CEFAILS ) ;
  699. _ASSERT( pRelease == pDataOut ) ;
  700. pDataOut = 0 ;
  701. //
  702. // We were unable to allocate the necessary object to
  703. // hold in the cache !
  704. //
  705. m_Lock.PartialUnlock() ;
  706. SetLastError( ERROR_OUTOFMEMORY ) ;
  707. }
  708. } else {
  709. m_Lock.PartialUnlock() ;
  710. //
  711. // Keep Track of our statistics !
  712. //
  713. IncrementStat( m_pStats, CACHESTATS::CLIENTALLOCFAILS ) ;
  714. }
  715. }
  716. }
  717. }
  718. //
  719. // One of these had better be NULL !
  720. //
  721. _ASSERT( pRelease == 0 || pDataOut == 0 ) ;
  722. DebugTrace( (DWORD_PTR)this, "pRelease %x", pRelease ) ;
  723. if( pRelease != 0 ) {
  724. _ASSERT( pDataOut == 0 ) ;
  725. //
  726. // Well we called the constructors create call - need to
  727. // release the object back to the constructor !
  728. //
  729. constructor.Release( pRelease, 0 ) ;
  730. }
  731. //
  732. // Well return TRUE if we got something to return after all !
  733. //
  734. return pDataOut != 0 ;
  735. }
  736. template < class Data, class Key,
  737. class Constructor,
  738. class PerCacheData
  739. >
  740. Data*
  741. CacheEx<Data, Key, Constructor, PerCacheData>::FindInternal(
  742. DWORD dwHash,
  743. Key& key
  744. ) {
  745. /*++
  746. Routine Description :
  747. This function will try to find something within the cache !
  748. Arguments :
  749. dwHash - the hash value of the item we're looking for !
  750. key - The unique key used to find the item in the Cache.
  751. fClient - the kind of reference to stick on the item we find !
  752. Return Value :
  753. TRUE if successfull
  754. --*/
  755. TraceFunctEnter( "CacheEx::FindInternal" ) ;
  756. _ASSERT( dwHash == m_Lookup.ComputeHash( &key ) ) ;
  757. Data* pDataOut = 0 ;
  758. if( !m_fValid ) {
  759. SetLastError( ERROR_NOT_SUPPORTED ) ;
  760. return FALSE ;
  761. } else {
  762. m_Lock.ShareLock() ;
  763. //
  764. // See if we can find an Entry in the cache for the item we want.
  765. //
  766. CACHEENTRY *pEntry = m_Lookup.SearchKey( dwHash, &key ) ;
  767. if( pEntry ) {
  768. pDataOut = pEntry->PublicData( &m_ExpireList ) ;
  769. }
  770. m_Lock.ShareUnlock() ;
  771. DebugTrace( (DWORD_PTR)this, "After Search pEntry %x pDataOut %x", pEntry, pDataOut ) ;
  772. //
  773. // Set up the error codes for why we may have failed !
  774. //
  775. if( !pEntry ) {
  776. SetLastError( ERROR_FILE_NOT_FOUND ) ;
  777. } else if( !pDataOut ) {
  778. SetLastError( ERROR_INVALID_DATA ) ;
  779. }
  780. }
  781. if( pDataOut )
  782. IncrementStat( m_pStats, CACHESTATS::SUCCESSSEARCH ) ;
  783. else
  784. IncrementStat( m_pStats, CACHESTATS::FAILSEARCH ) ;
  785. return pDataOut ;
  786. }
  787. template < class Data, class Key,
  788. class Constructor,
  789. class PerCacheData
  790. >
  791. BOOL
  792. CacheEx<Data, Key, Constructor, PerCacheData>::InsertInternal(
  793. DWORD dwHash,
  794. Key& key,
  795. Data* pDataIn,
  796. long cClientRefs
  797. ) {
  798. /*++
  799. Routine Description :
  800. This function is called when we have a data item,
  801. and we wish to insert it into the cache !
  802. Arguments :
  803. key - The unique key used to find the item in the Cache.
  804. constructor - An object to pass to the Data's constructor and Init
  805. functions.
  806. pData - Pointer which gets the result !
  807. Return Value :
  808. TRUE if successfull
  809. --*/
  810. TraceFunctEnter( "CacheEx::InsertInternal" ) ;
  811. DebugTrace( (DWORD_PTR)this, "Args dwHash %x pDataIn %x cClientRefs %x",
  812. dwHash, pDataIn, cClientRefs ) ;
  813. _ASSERT( dwHash == m_Lookup.ComputeHash( &key ) ) ;
  814. BOOL fReturn = FALSE ;
  815. if( !m_fValid ) {
  816. SetLastError( ERROR_NOT_SUPPORTED ) ;
  817. return FALSE ;
  818. } else {
  819. //
  820. // Grab the cache lock with a Partial Lock - this
  821. // guarantees that nobody else can insert or delete
  822. // from the hash table untill we finish.
  823. //
  824. // Note : every branch in the following if's must
  825. // ensure that the lock is properly released/manipulated !
  826. //
  827. m_Lock.PartialLock() ;
  828. //
  829. // See if we can find an Entry in the cache for the item we want.
  830. //
  831. CACHEENTRY *pEntry = 0 ;
  832. HASHTABLE::ITER iter = m_Lookup.SearchKeyHashIter( dwHash, &key, pEntry ) ;
  833. //CACHEENTRY *pEntry = m_Lookup.SearchKey( dwHash, &key ) ;
  834. DebugTrace( (DWORD_PTR)this, "After Search pEntry %x pEntry->m_pData %x",
  835. pEntry, pEntry ? pEntry->m_pData : 0 ) ;
  836. if( pEntry != 0 ) {
  837. fReturn = pEntry->SetData( pDataIn, &m_ExpireList, cClientRefs ) ;
  838. m_Lock.PartialUnlock() ;
  839. } else {
  840. CCacheItemBase<Data>* pRef = (CCacheItemBase<Data>*)pDataIn->m_pCacheRefInterface ;
  841. #ifdef DEBUG
  842. //
  843. // Periodically fail to allocate memory !
  844. //
  845. if( fTimeToFail() ) {
  846. pEntry = 0 ;
  847. } else
  848. #endif
  849. pEntry = new( m_Cache ) CACHEENTRY( &m_ExpireList, key, 0, cClientRefs ) ;
  850. DebugTrace( (DWORD_PTR)this, "pEntry %x pRef %x", pEntry, pRef ) ;
  851. if( pEntry != 0 ) {
  852. //
  853. // Just check some basic things here !
  854. //
  855. _ASSERT( !pEntry->IsCheckedOut() || cClientRefs != 0 ) ;
  856. _ASSERT( !pEntry->IsInLRUList() ) ;
  857. //
  858. // Grab the lock on the entry !
  859. //
  860. if( pRef ) {
  861. pRef->ExclusiveLock() ;
  862. //
  863. // This must be checked out of its native cache,
  864. // otherwise it could be destroyed from under us !
  865. //
  866. _ASSERT( pRef->IsCheckedOut() ) ;
  867. _ASSERT( pRef->IsMatch( pDataIn ) ) ;
  868. }
  869. pEntry->ExclusiveLock() ;
  870. //
  871. // Convert to an Exclusive Lock for inserting into
  872. // the hash table !
  873. //
  874. m_Lock.FirstPartialToExclusive() ;
  875. #ifdef DEBUG
  876. //
  877. // Periodically fail to insert into the hash table !
  878. //
  879. if( fTimeToFail() ) {
  880. fReturn = FALSE ;
  881. } else
  882. #endif
  883. fReturn = m_Lookup.InsertDataHashIter( iter, dwHash, &key, pEntry ) ;
  884. DebugTrace( (DWORD_PTR)this, "Insert results %x", fReturn ) ;
  885. //
  886. // Both the global Cache Lock - m_Lock and
  887. // the locks for pEntry must be released within the branches of
  888. // this if !
  889. //
  890. if( fReturn ) {
  891. m_Lock.ExclusiveUnlock() ;
  892. m_ExpireList.IncrementItems() ;
  893. if( !pRef ) {
  894. //
  895. // Ok, lets set up our Entry
  896. //
  897. pEntry->m_pData = pDataIn ;
  898. _ASSERT( pDataIn->m_pCacheRefInterface == 0 ) ;
  899. pDataIn->m_pCacheRefInterface = pEntry ;
  900. } else {
  901. //
  902. // If the user checked this out of one cache, to make
  903. // a reference in ours - he/she shouldn't ask us to add
  904. // another check-out reference !
  905. //
  906. _ASSERT( cClientRefs == 0 ) ;
  907. #ifdef DEBUG
  908. //
  909. // Periodically fail these operations !
  910. //
  911. if( fTimeToFail() ) {
  912. fReturn = FALSE ;
  913. } else
  914. #endif
  915. fReturn = pRef->InsertRef( pEntry, pDataIn, cClientRefs ) ;
  916. DebugTrace( (DWORD_PTR)this, "InsertRef Resutls - fReturn %x", fReturn ) ;
  917. //
  918. // Failure at this point leaves a dangling dummy entry in
  919. // the cache - we need to insure that expiration gets it !
  920. //
  921. if( !fReturn ) {
  922. pEntry->FailedCheckOut( &m_ExpireList,
  923. cClientRefs,
  924. 0,
  925. 0
  926. ) ;
  927. _ASSERT( pEntry->m_pData == 0 ) ;
  928. }
  929. }
  930. _ASSERT( (!fReturn && pEntry->m_pData==0) || (fReturn && pEntry->m_pData) ) ;
  931. _ASSERT( pDataIn->m_pCacheRefInterface != pEntry ||
  932. pEntry->m_pData == pDataIn ) ;
  933. pEntry->ExclusiveUnlock() ;
  934. } else {
  935. _ASSERT( pEntry->m_pData == 0 ) ;
  936. //
  937. // Since the client provided this item, we set this to 0 as
  938. // we have not been able to add a reference to the item !
  939. // This prevents us from calling Release when our pEntry is destroyed !
  940. //
  941. pEntry->m_pData = 0 ;
  942. _ASSERT( pDataIn->m_pCacheRefInterface != pEntry ||
  943. pEntry->m_pData == pDataIn ) ;
  944. pEntry->ExclusiveUnlock() ;
  945. //
  946. // Release back to the cache -did we have a client ref -
  947. // have to get rid of that if necessary !
  948. //
  949. pEntry->FailedCheckOut( &m_ExpireList,
  950. cClientRefs,
  951. &m_Cache,
  952. &m_PerCacheData
  953. ) ;
  954. //
  955. // Let this go after we've accessed the allocation cache !
  956. //
  957. m_Lock.ExclusiveUnlock() ;
  958. }
  959. //
  960. // Release any remaining locks !
  961. //
  962. if( pRef ) {
  963. //
  964. // This must be checked out of its native cache,
  965. // otherwise it could be destroyed from under us !
  966. //
  967. _ASSERT( pRef->IsCheckedOut() ) ;
  968. pRef->ExclusiveUnlock() ;
  969. }
  970. } else {
  971. //
  972. // Either we found the item after all,
  973. // or we were unable to allocate memory
  974. // to make the container !
  975. //
  976. m_Lock.PartialUnlock() ;
  977. }
  978. }
  979. }
  980. return fReturn ;
  981. }
  982. template < class Data, class Key,
  983. class Constructor,
  984. class PerCacheData
  985. >
  986. void
  987. CacheEx<Data, Key, Constructor, PerCacheData>::CheckIn(
  988. Data* pData
  989. ) {
  990. /*++
  991. Routine Description :
  992. This function is called when we want to return something
  993. to the cache.
  994. Arguments :
  995. p - the item being returned to the cache.
  996. Return Value :
  997. none
  998. --*/
  999. _ASSERT( pData ) ;
  1000. if( pData ) {
  1001. CacheState* p = (CacheState*)pData->m_pCacheRefInterface ;
  1002. _ASSERT( p ) ;
  1003. p->ExternalCheckIn( ) ;
  1004. }
  1005. }
  1006. template < class Data, class Key,
  1007. class Constructor,
  1008. class PerCacheData
  1009. >
  1010. void
  1011. CacheEx<Data, Key, Constructor, PerCacheData>::CheckInNoLocks(
  1012. Data* pData
  1013. ) {
  1014. /*++
  1015. Routine Description :
  1016. This function is called when we want to return something
  1017. to the cache.
  1018. Arguments :
  1019. p - the item being returned to the cache.
  1020. Return Value :
  1021. none
  1022. --*/
  1023. _ASSERT( pData ) ;
  1024. if( pData ) {
  1025. CacheState* p = (CacheState*)pData->m_pCacheRefInterface ;
  1026. _ASSERT( p ) ;
  1027. p->ExternalCheckInNoLocks( ) ;
  1028. }
  1029. }
  1030. template < class Data, class Key,
  1031. class Constructor,
  1032. class PerCacheData
  1033. >
  1034. void
  1035. CacheEx<Data, Key, Constructor, PerCacheData>::CheckOut(
  1036. Data* pData,
  1037. long cClientRefs
  1038. ) {
  1039. /*++
  1040. Routine Description :
  1041. This function is called when we want to add a client
  1042. reference to something that is already in the cache !
  1043. Arguments :
  1044. p - the item being returned to the cache.
  1045. Return Value :
  1046. none
  1047. --*/
  1048. _ASSERT( pData ) ;
  1049. _ASSERT( cClientRefs > 0 ) ;
  1050. if( pData ) {
  1051. CacheState* p = (CacheState*)pData->m_pCacheRefInterface ;
  1052. _ASSERT( p ) ;
  1053. p->CheckOut( 0, cClientRefs ) ;
  1054. }
  1055. }
  1056. template < class Data,
  1057. class Key,
  1058. class Constructor,
  1059. class PerCacheData
  1060. >
  1061. MultiCacheEx< Data, Key, Constructor, PerCacheData >::MultiCacheEx() :
  1062. m_fValid( FALSE ),
  1063. m_pCaches( 0 ) ,
  1064. m_cSubCaches( 0 ),
  1065. m_pfnHash( 0 ) {
  1066. /*++
  1067. Routine Description :
  1068. This function initializes the MultiCache's data structures
  1069. Arguments :
  1070. None.
  1071. Return Value :
  1072. Nothing
  1073. --*/
  1074. }
  1075. template < class Data,
  1076. class Key,
  1077. class Constructor,
  1078. class PerCacheData
  1079. >
  1080. MultiCacheEx< Data, Key, Constructor, PerCacheData >::~MultiCacheEx() {
  1081. /*++
  1082. Routine Description :
  1083. This function destroys all of our data structures - release
  1084. all of our subcaches !
  1085. Arguments :
  1086. None.
  1087. Return Value :
  1088. Nothing
  1089. --*/
  1090. if( m_pCaches ) {
  1091. delete[] m_pCaches ;
  1092. }
  1093. }
  1094. template < class Data,
  1095. class Key,
  1096. class Constructor,
  1097. class PerCacheData
  1098. >
  1099. BOOL
  1100. MultiCacheEx< Data, Key, Constructor, PerCacheData >::Init(
  1101. PFNHASH pfnHash,
  1102. PKEYCOMPARE pfnCompare,
  1103. DWORD dwLifetimeSeconds,
  1104. DWORD cMaxElements,
  1105. DWORD cSubCaches,
  1106. CACHESTATS* pStats,
  1107. PSTOPHINT_FN pfnStopHint
  1108. ) {
  1109. /*++
  1110. Routine Description :
  1111. This function initializes the MultiCache - we use
  1112. multiple independent Caches to split the work
  1113. of caching all the data.
  1114. Arguments :
  1115. None.
  1116. Return Value :
  1117. Nothing
  1118. --*/
  1119. //
  1120. // Check that we're in the right state for this !
  1121. //
  1122. _ASSERT( !m_fValid ) ;
  1123. _ASSERT( m_pCaches == 0 ) ;
  1124. //
  1125. // Validate our arguments !!!
  1126. //
  1127. _ASSERT( pfnHash != 0 ) ;
  1128. _ASSERT( pfnCompare != 0 ) ;
  1129. _ASSERT( dwLifetimeSeconds != 0 ) ;
  1130. _ASSERT( cSubCaches != 0 ) ;
  1131. _ASSERT( cMaxElements != 0 ) ;
  1132. m_pfnHash = pfnHash ;
  1133. m_cSubCaches = cSubCaches ;
  1134. //
  1135. // Allocate the necessary subcaches !
  1136. //
  1137. m_pCaches = new CACHEINSTANCE[m_cSubCaches] ;
  1138. if( !m_pCaches ) {
  1139. return FALSE ;
  1140. } else {
  1141. for( DWORD i=0; i<cSubCaches; i++ ) {
  1142. if( !m_pCaches[i].Init( m_pfnHash,
  1143. pfnCompare,
  1144. dwLifetimeSeconds,
  1145. (cMaxElements / cSubCaches) + 1,
  1146. pStats,
  1147. pfnStopHint
  1148. ) ) {
  1149. delete[] m_pCaches ;
  1150. m_pCaches = NULL;
  1151. return FALSE ;
  1152. }
  1153. }
  1154. }
  1155. m_fValid = TRUE ;
  1156. return TRUE ;
  1157. }
  1158. template < class Data,
  1159. class Key,
  1160. class Constructor,
  1161. class PerCacheData
  1162. >
  1163. DWORD
  1164. MultiCacheEx< Data, Key, Constructor, PerCacheData >::ChooseInstance(
  1165. DWORD dwHash
  1166. ) {
  1167. /*++
  1168. Routine Description :
  1169. Given a Key figure out which of our subinstances we wish to use.
  1170. Arguments :
  1171. None.
  1172. Return Value :
  1173. Nothing
  1174. --*/
  1175. //
  1176. // Check that we're in the right state for this !
  1177. //
  1178. _ASSERT( m_fValid ) ;
  1179. _ASSERT( m_pCaches != 0 ) ;
  1180. //
  1181. // Validate our arguments !!!
  1182. //
  1183. _ASSERT( m_pfnHash != 0 ) ;
  1184. _ASSERT( m_cSubCaches != 0 ) ;
  1185. //DWORD dwHash = m_pfnHash( k ) ;
  1186. //
  1187. // Constants below stolen from C-runtime rand() function !
  1188. //
  1189. dwHash = (((dwHash * 214013) +2531011) >> 8) % m_cSubCaches ;
  1190. return dwHash ;
  1191. }
  1192. template < class Data,
  1193. class Key,
  1194. class Constructor,
  1195. class PerCacheData
  1196. >
  1197. BOOL
  1198. MultiCacheEx<Data, Key, Constructor, PerCacheData>::ExpungeItems(
  1199. EXPUNGEOBJECT* pExpunge
  1200. ) {
  1201. /*++
  1202. Routine Description :
  1203. This function allows the user to remove a specific set of objects
  1204. from the cache - a callback object is provided to visit each element !
  1205. Arguments :
  1206. None
  1207. Return Value :
  1208. TRUE if successfull
  1209. FALSE - means that there are still items checked out of the cache -
  1210. a NO-NO !
  1211. --*/
  1212. TraceFunctEnter( "MultiCacheEx::ExpungeItems" ) ;
  1213. BOOL fReturn = TRUE ;
  1214. for( DWORD i=0; i<m_cSubCaches; i++ ) {
  1215. fReturn &= m_pCaches[i].ExpungeItems( pExpunge ) ;
  1216. }
  1217. return fReturn ;
  1218. }
  1219. template < class Data,
  1220. class Key,
  1221. class Constructor,
  1222. class PerCacheData
  1223. >
  1224. BOOL
  1225. MultiCacheEx<Data, Key, Constructor, PerCacheData>::ExpungeKey(
  1226. PKEY pKeyExpunge
  1227. ) {
  1228. /*++
  1229. Routine Description :
  1230. This function allows us to remove a particular key from the cache !
  1231. Arguments :
  1232. None
  1233. Return Value :
  1234. TRUE if successfull
  1235. FALSE - means that there are still items checked out of the cache -
  1236. a NO-NO !
  1237. --*/
  1238. TraceFunctEnter( "MultiCacheEx::ExpungeKey" ) ;
  1239. DWORD dw = m_pfnHash( pKeyExpunge ) ;
  1240. CACHEINSTANCE* pInstance = &m_pCaches[ChooseInstance(dw)] ;
  1241. return pInstance->ExpungeKey( dw, pKeyExpunge ) ;
  1242. }
  1243. template < class Data,
  1244. class Key,
  1245. class Constructor,
  1246. class PerCacheData
  1247. >
  1248. Data*
  1249. MultiCacheEx< Data, Key, Constructor, PerCacheData >::FindOrCreate(
  1250. Key& key,
  1251. Constructor& constructor,
  1252. BOOL fEarlyCreate
  1253. ) {
  1254. /*++
  1255. Routine Description :
  1256. Arguments :
  1257. None.
  1258. Return Value :
  1259. Nothing
  1260. --*/
  1261. DWORD dw = m_pfnHash( &key ) ;
  1262. CACHEINSTANCE* pInstance = &m_pCaches[ChooseInstance(dw)] ;
  1263. Data* pDataOut = 0 ;
  1264. BOOL fSuccess = pInstance->FindOrCreateInternal( dw, key, constructor, pDataOut, fEarlyCreate ) ;
  1265. _ASSERT( fSuccess || pDataOut == 0 ) ;
  1266. return pDataOut ;
  1267. }
  1268. template < class Data,
  1269. class Key,
  1270. class Constructor,
  1271. class PerCacheData
  1272. >
  1273. Data*
  1274. MultiCacheEx< Data, Key, Constructor, PerCacheData >::FindOrCreate(
  1275. DWORD dw,
  1276. Key& key,
  1277. Constructor& constructor,
  1278. BOOL fEarlyCreate
  1279. ) {
  1280. /*++
  1281. Routine Description :
  1282. Arguments :
  1283. None.
  1284. Return Value :
  1285. Nothing
  1286. --*/
  1287. _ASSERT( dw == m_pfnHash( &key ) ) ;
  1288. CACHEINSTANCE* pInstance = &m_pCaches[ChooseInstance(dw)] ;
  1289. Data* pDataOut = 0 ;
  1290. BOOL fSuccess = pInstance->FindOrCreateInternal( dw, key, constructor, pDataOut ) ;
  1291. _ASSERT( fSuccess || pDataOut == 0 ) ;
  1292. return pDataOut ;
  1293. }
  1294. template < class Data,
  1295. class Key,
  1296. class Constructor,
  1297. class PerCacheData
  1298. >
  1299. Data*
  1300. MultiCacheEx< Data, Key, Constructor, PerCacheData >::Find(
  1301. Key& key
  1302. ) {
  1303. /*++
  1304. Routine Description :
  1305. Given the key of an element - find it in the cache !
  1306. Arguments :
  1307. None.
  1308. Return Value :
  1309. Nothing
  1310. --*/
  1311. DWORD dw = m_pfnHash( &key ) ;
  1312. CACHEINSTANCE* pInstance = &m_pCaches[ChooseInstance(dw)] ;
  1313. return pInstance->FindInternal( dw, key ) ;
  1314. }
  1315. template < class Data,
  1316. class Key,
  1317. class Constructor,
  1318. class PerCacheData
  1319. >
  1320. Data*
  1321. MultiCacheEx< Data, Key, Constructor, PerCacheData >::Find(
  1322. DWORD dw,
  1323. Key& key
  1324. ) {
  1325. /*++
  1326. Routine Description :
  1327. Given the key of an element - find it in the cache !
  1328. Arguments :
  1329. None.
  1330. Return Value :
  1331. Nothing
  1332. --*/
  1333. _ASSERT( dw == m_pfnHash( &key ) ) ;
  1334. CACHEINSTANCE* pInstance = &m_pCaches[ChooseInstance(dw)] ;
  1335. return pInstance->FindInternal( dw, key ) ;
  1336. }
  1337. template < class Data, class Key,
  1338. class Constructor,
  1339. class PerCacheData
  1340. >
  1341. void
  1342. MultiCacheEx<Data, Key, Constructor, PerCacheData>::CheckIn(
  1343. Data* pData
  1344. ) {
  1345. /*++
  1346. Routine Description :
  1347. This function is called when we want to return something
  1348. to the cache.
  1349. Arguments :
  1350. p - the item being returned to the cache.
  1351. Return Value :
  1352. none
  1353. --*/
  1354. _ASSERT( pData ) ;
  1355. CACHEINSTANCE::CheckIn( pData ) ;
  1356. }
  1357. template < class Data, class Key,
  1358. class Constructor,
  1359. class PerCacheData
  1360. >
  1361. void
  1362. MultiCacheEx<Data, Key, Constructor, PerCacheData>::CheckInNoLocks(
  1363. Data* pData
  1364. ) {
  1365. /*++
  1366. Routine Description :
  1367. This function is called when we want to return something
  1368. to the cache.
  1369. Arguments :
  1370. p - the item being returned to the cache.
  1371. Return Value :
  1372. none
  1373. --*/
  1374. _ASSERT( pData ) ;
  1375. CACHEINSTANCE::CheckInNoLocks( pData ) ;
  1376. }
  1377. template < class Data, class Key,
  1378. class Constructor,
  1379. class PerCacheData
  1380. >
  1381. void
  1382. MultiCacheEx<Data, Key, Constructor, PerCacheData>::CheckOut(
  1383. Data* pData,
  1384. long cClientRefs
  1385. ) {
  1386. /*++
  1387. Routine Description :
  1388. This function is called when we want to add a client
  1389. reference to something in the cache !
  1390. Arguments :
  1391. p - the item checked out of the cache !
  1392. Return Value :
  1393. none
  1394. --*/
  1395. _ASSERT( pData ) ;
  1396. _ASSERT( cClientRefs > 0 ) ;
  1397. CACHEINSTANCE::CheckOut( pData, cClientRefs ) ;
  1398. }
  1399. template < class Data,
  1400. class Key,
  1401. class Constructor,
  1402. class PerCacheData
  1403. >
  1404. void
  1405. MultiCacheEx< Data, Key, Constructor, PerCacheData >::Expire() {
  1406. /*++
  1407. Routine Description :
  1408. Remove old items from the cache !
  1409. Arguments :
  1410. None.
  1411. Return Value :
  1412. Nothing
  1413. --*/
  1414. for( DWORD i=0; i<m_cSubCaches; i++ ) {
  1415. m_pCaches[i].Expire( ) ;
  1416. }
  1417. }
  1418. template < class Data,
  1419. class Key,
  1420. class Constructor,
  1421. class PerCacheData
  1422. >
  1423. BOOL
  1424. MultiCacheEx< Data, Key, Constructor, PerCacheData >::Insert(
  1425. Key& key,
  1426. Data* pData,
  1427. long cClientRefs
  1428. ) {
  1429. /*++
  1430. Routine Description :
  1431. Given the key of an element and a piece of data - insert it
  1432. into the cache !
  1433. Arguments :
  1434. key - key of the item being inserted into the cache
  1435. pData - The data item to go into the cache
  1436. cClientRefs - the number of client references we want to stick on the item !
  1437. Return Value :
  1438. TRUE if successfull
  1439. --*/
  1440. DWORD dw = m_pfnHash( &key ) ;
  1441. CACHEINSTANCE* pInstance = &m_pCaches[ChooseInstance(dw)] ;
  1442. return pInstance->InsertInternal( dw, key, pData, cClientRefs ) ;
  1443. }
  1444. template < class Data,
  1445. class Key,
  1446. class Constructor,
  1447. class PerCacheData
  1448. >
  1449. BOOL
  1450. MultiCacheEx< Data, Key, Constructor, PerCacheData >::Insert(
  1451. DWORD dw,
  1452. Key& key,
  1453. Data* pData,
  1454. long cClientRefs
  1455. ) {
  1456. /*++
  1457. Routine Description :
  1458. Given the key of an element - find it in the cache !
  1459. Arguments :
  1460. dw - hash of the key !
  1461. key - key of the item being inserted into the cache
  1462. pData - The data item to go into the cache
  1463. fReference - do we want the item checked out !
  1464. Return Value :
  1465. TRUE if successfull
  1466. --*/
  1467. _ASSERT( dw == m_pfnHash( &key ) ) ;
  1468. CACHEINSTANCE* pInstance = &m_pCaches[ChooseInstance(dw)] ;
  1469. return pInstance->InsertInternal( dw, key, pData, cClientRefs ) ;
  1470. }