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.

989 lines
18 KiB

  1. /*++
  2. cacheimp.h -
  3. This file contains all the template function definitions required
  4. to make the cache manager work.
  5. --*/
  6. #pragma warning(1:4700)
  7. template < class Data,
  8. class Key,
  9. class KEYREF,
  10. class Constructor,
  11. BOOL fAtomic
  12. >
  13. void
  14. Cache< Data, Key, KEYREF, Constructor, fAtomic >::Schedule() {
  15. /*++
  16. Routine Description :
  17. This function runs through all the items in the Cache
  18. bumping TTL's. If there are any items ready to go
  19. then we dump them from the Cache.
  20. Arguments :
  21. None.
  22. Return Value :
  23. Nothing
  24. --*/
  25. if( !m_fValid )
  26. return ;
  27. m_Lock.ShareLock() ;
  28. DWORD cExpungable = 0 ;
  29. BOOL fExpunge = m_ExpireList.Expire( cExpungable ) ;
  30. m_Lock.ShareUnlock() ;
  31. if( fExpunge ) {
  32. KEYREF key;
  33. Expunge( key ) ; // fIgnoreKey == TRUE by default
  34. }
  35. }
  36. template < class Data,
  37. class Key,
  38. class KEYREF,
  39. class Constructor,
  40. BOOL fAtomic
  41. >
  42. BOOL
  43. Cache<Data, Key, KEYREF, Constructor, fAtomic>::RemoveEntry(
  44. CacheState* pEntry
  45. ) {
  46. /*++
  47. Routine Description :
  48. This function removes an entry from the Cache.
  49. We call our hash table to delete the item.
  50. the CacheState destructor automagically removes
  51. the item from our linked lists.
  52. CALLER MUST HOLD APPROPRIATE LOCKS!
  53. Arguments :
  54. pEntry - item to be removed from cache
  55. Return Value :
  56. TRUE if successfully removed !
  57. --*/
  58. CACHEENTRY *pCacheEntry = (CACHEENTRY*)pEntry ;
  59. CACHEENTRY *pC = m_Lookup.DeleteData( pCacheEntry->GetKey(), pCacheEntry ) ;
  60. if (!pC)
  61. {
  62. return FALSE;
  63. }
  64. else
  65. {
  66. delete pC;
  67. }
  68. return TRUE;
  69. }
  70. template < class Data,
  71. class Key,
  72. class KEYREF,
  73. class Constructor,
  74. BOOL fAtomic
  75. >
  76. BOOL
  77. Cache<Data, Key, KEYREF, Constructor, fAtomic>::QueryRemoveEntry(
  78. CacheState* pEntry ) {
  79. /*++
  80. Routine Description :
  81. This function is called from CacheList object to
  82. determine whether we want to remove an item from the Cache.
  83. This function is used to implement the ExpungeSpecific
  84. function available to clients.
  85. CALLER MUST HOLD APPROPRIATE LOCKS!
  86. Arguments :
  87. pEntry - item we want to determine whether it should remain !
  88. Return Value :
  89. TRUE if successfully removed !
  90. --*/
  91. CACHEENTRY *pCacheEntry = (CACHEENTRY*) pEntry ;
  92. if( m_pCallbackObject ) {
  93. return m_pCallbackObject->fRemoveCacheItem( pCacheEntry->m_pData ) ;
  94. }
  95. return FALSE ;
  96. }
  97. template < class Data,
  98. class Key,
  99. class KEYREF,
  100. class Constructor,
  101. BOOL fAtomic
  102. >
  103. BOOL
  104. Cache<Data, Key, KEYREF, Constructor, fAtomic>::ExpungeInternal(
  105. KEYREF key,
  106. CACHEENTRY* pData,
  107. BOOL fIgnoreKey,
  108. const CACHEENTRY* pProtected,
  109. BOOL fDoCheap
  110. ) {
  111. /*++
  112. Routine Description :
  113. This function is called when we want to force some thing
  114. out of the cache. The caller can provide a key
  115. to force a particular item out of the cache.
  116. CALLER MUST HOLD APPROPRIATE LOCKS!
  117. Arguments :
  118. pProtected - an Element we want to make sure is not removed !
  119. key - Pointer to the key identifying the item to be removed
  120. pData - pointer to the CACHEENTRY object containing the data
  121. and key we wish to remove.
  122. Return Value :
  123. TRUE something is successfully removed from the cache.
  124. --*/
  125. if( !fIgnoreKey ) {
  126. CACHEENTRY* pC = m_Lookup.DeleteData( key, pData ) ;
  127. if (pC)
  128. {
  129. delete pC;
  130. }
  131. }
  132. return m_ExpireList.Expunge( this, fDoCheap, pProtected ) ;
  133. }
  134. #ifdef DEBUG
  135. template < class Data,
  136. class Key,
  137. class KEYREF,
  138. class Constructor,
  139. BOOL fAtomic
  140. >
  141. long Cache<Data, Key, KEYREF, Constructor, fAtomic>::s_cCreated = 0 ;
  142. #endif
  143. template < class Data,
  144. class Key,
  145. class KEYREF,
  146. class Constructor,
  147. BOOL fAtomic
  148. >
  149. Cache<Data, Key, KEYREF, Constructor, fAtomic>::Cache( ) : m_fValid( FALSE ) {
  150. /*++
  151. Routine Description :
  152. This function initializes our member variables.
  153. Arguments :
  154. cMax - maximum number of elements the cache should hold
  155. Return Value :
  156. Nothing
  157. --*/
  158. #ifdef DEBUG
  159. InterlockedIncrement( &s_cCreated ) ;
  160. #endif
  161. AddToSchedule() ;
  162. }
  163. template < class Data,
  164. class Key,
  165. class KEYREF,
  166. class Constructor,
  167. BOOL fAtomic
  168. >
  169. Cache<Data, Key, KEYREF, Constructor, fAtomic>::~Cache( ) {
  170. /*++
  171. Routine Description :
  172. This function destroys a Cache object !
  173. Arguments :
  174. None
  175. Return Value :
  176. Nothing
  177. --*/
  178. RemoveFromSchedule() ;
  179. //
  180. // Member and Base class destruction follows !!
  181. //
  182. #ifdef DEBUG
  183. InterlockedDecrement( &s_cCreated ) ;
  184. #endif
  185. }
  186. template < class Data,
  187. class Key,
  188. class KEYREF,
  189. class Constructor,
  190. BOOL fAtomic
  191. >
  192. BOOL
  193. Cache<Data, Key, KEYREF, Constructor, fAtomic>::Init(
  194. DWORD (*pfnHash)( KEYREF ),
  195. DWORD dwLifetimeSeconds,
  196. DWORD cMaxInstances,
  197. PSTOPHINT_FN pfnStopHint
  198. ) {
  199. /*++
  200. Routine Description :
  201. This function initializes the cache so that it is ready
  202. to take entries.
  203. Arguments :
  204. pfnHash - function to be used to compute hash values on keys
  205. dwLifetimeSeconds - The number of seconds objects should live in the Cache
  206. pfnStopHint - function to be used to send stop hints during
  207. long spins so shutdown's don't time out.
  208. Return Value :
  209. TRUE if successfull
  210. --*/
  211. m_ExpireList.m_cMax = long(cMaxInstances) ;
  212. m_ExpireList.m_pfnStopHint = pfnStopHint;
  213. m_TTL = 1 + (dwLifetimeSeconds / CScheduleThread::dwNotificationSeconds) ;
  214. return m_fValid = m_Lookup.Init( &CACHEENTRY::m_pNext, 100, 100,
  215. pfnHash, 2, CACHEENTRY::GetKey, CACHEENTRY::MatchKey ) ;
  216. }
  217. template < class Data,
  218. class Key,
  219. class KEYREF,
  220. class Constructor,
  221. BOOL fAtomic
  222. >
  223. BOOL
  224. Cache<Data, Key, KEYREF, Constructor, fAtomic>::Expunge(
  225. KEYREF key,
  226. CACHEENTRY* pData,
  227. BOOL fIgnoreKey
  228. ) {
  229. /*++
  230. Routine Description :
  231. This function is called when we want to force some thing
  232. out of the cache. The caller can provide a key
  233. to force a particular item out of the cache.
  234. WE WILL GRAB THE NECESSARY LOCKS !
  235. Arguments :
  236. pfnHash - function to be used to compute hash values on keys
  237. Return Value :
  238. TRUE if successfull
  239. --*/
  240. _ASSERT( m_fValid ) ;
  241. m_Lock.ExclusiveLock() ;
  242. BOOL fReturn = ExpungeInternal( key, pData, fIgnoreKey, 0, FALSE ) ;
  243. m_Lock.ExclusiveUnlock() ;
  244. return fReturn ;
  245. }
  246. template < class Data,
  247. class Key,
  248. class KEYREF,
  249. class Constructor,
  250. BOOL fAtomic
  251. >
  252. BOOL
  253. Cache<Data, Key, KEYREF, Constructor, fAtomic>::ExpungeSpecific(
  254. CALLBACKOBJ* pCallbackObject,
  255. BOOL fForced
  256. ) {
  257. /*++
  258. Routine Description :
  259. This function is called when we want to force some thing
  260. out of the cache. The caller can provide a key
  261. to force a particular item out of the cache.
  262. WE WILL GRAB THE NECESSARY LOCKS !
  263. Arguments :
  264. pfn - callback function used to determine what to remove
  265. from the Cache.
  266. fForced - if TRUE then we will remove from the Cache no matter
  267. what, even if other threads are using the object !
  268. Return Value :
  269. TRUE if successfull
  270. --*/
  271. m_Lock.ExclusiveLock() ;
  272. m_pCallbackObject = pCallbackObject ;
  273. BOOL fReturn = m_ExpireList.ExpungeSpecific( this, fForced ) ;
  274. m_Lock.ExclusiveUnlock() ;
  275. return fReturn ;
  276. }
  277. template < class Data,
  278. class Key,
  279. class KEYREF,
  280. class Constructor,
  281. BOOL fAtomic
  282. >
  283. BOOL
  284. Cache<Data, Key, KEYREF, Constructor, fAtomic>::FindOrCreateInternal(
  285. DWORD dwHash,
  286. KEYREF key,
  287. Constructor& constructor,
  288. CRefPtr< Data >& pDataOut
  289. ) {
  290. /*++
  291. Routine Description :
  292. This function is called when we want something out
  293. of the Cache. We will either find the object or create it.
  294. WE WILL GRAB THE NECESSARY LOCKS !
  295. Arguments :
  296. key - The unique key used to find the item in the Cache.
  297. constructor - An object to pass to the Data's constructor and Init
  298. functions.
  299. pDataOut - Smart pointer which receives the result
  300. Return Value :
  301. TRUE if successfull
  302. --*/
  303. BOOL fReturn = FALSE ;
  304. pDataOut = 0 ;
  305. if( !m_fValid ) {
  306. SetLastError( 0 ) ;
  307. return FALSE ;
  308. } else {
  309. m_Lock.ShareLock() ;
  310. //
  311. // See if we can find an Entry in the cache for the item we want.
  312. //
  313. CACHEENTRY *pEntry = m_Lookup.SearchKeyHash( dwHash, key ) ;
  314. if( pEntry ) {
  315. pDataOut = pEntry->m_pData ;
  316. fReturn = TRUE ;
  317. m_ExpireList.LiveLonger( pEntry, (m_TTL/2)+1 ) ;
  318. }
  319. m_Lock.ShareUnlock() ;
  320. if( pDataOut == 0 ) {
  321. m_Lock.ExclusiveLock() ;
  322. //
  323. // Check that the Item we want in the Cache didn't make it
  324. // into the Cache in the brief moment we switched from a shared
  325. // to an exclusive lock.
  326. //
  327. pEntry = m_Lookup.SearchKeyHash( dwHash, key ) ;
  328. if( pEntry != 0 ) {
  329. //
  330. // It's in the cache - return the cached object !
  331. //
  332. pDataOut = pEntry->m_pData ;
  333. fReturn = TRUE ;
  334. m_ExpireList.LiveLonger( pEntry, (m_TTL/2)+1 ) ;
  335. m_Lock.ExclusiveUnlock() ;
  336. } else {
  337. //
  338. // We're going to have to create one of the elements that goes
  339. // in the cache. Let's do that now !
  340. //
  341. const CACHEENTRY *pCacheEntry = 0 ;
  342. BOOL fOverFlow = FALSE ;
  343. Data *pData = 0 ;
  344. //
  345. // We Add another brace here because we want to explicitly manage
  346. // the lifetime of tempCacheEntry
  347. //
  348. {
  349. //
  350. // pEntry will be copied by the FHash template into
  351. // another CACHEENTRY object when it does its insertion.
  352. // NOTE: do this to use the new TFHashEx code.
  353. // pEntry is created on the stack and its refcount should remain!
  354. //
  355. CACHEENTRY* pEntry = new CACHEENTRY( m_TTL ) ;
  356. _ASSERT( pEntry );
  357. if (pEntry)
  358. {
  359. //
  360. // The constructor of the Data object is expected to initialize
  361. // only the 'key' data of the Data object so that the data can be
  362. // found in the Cache.
  363. //
  364. pEntry->m_pData = pData = new Data( key, constructor ) ;
  365. if( pEntry->m_pData ) {
  366. pCacheEntry = m_Lookup.InsertDataHash( dwHash, pEntry ) ;
  367. if( pCacheEntry ) {
  368. if( m_ExpireList.Append( pCacheEntry ) ) {
  369. if( !ExpungeInternal( key, 0, TRUE, pCacheEntry, TRUE ) ) { // ignore key by default
  370. m_ExpireList.ForceExpunge( this, pCacheEntry ) ;
  371. }
  372. }
  373. pDataOut = pCacheEntry->m_pData ;
  374. pDataOut->ExclusiveLock() ;
  375. } else {
  376. //
  377. // The Insertion failed !
  378. // Falling through correctly handles this error case !
  379. //
  380. //
  381. // No, No, No! We need to handle the failure case here!
  382. // Delete pEntry's reference to the Data object
  383. // if an error occurred inserting into hash tables.
  384. // This will automatically destroy the data object.
  385. pEntry->m_pData = 0;
  386. }
  387. }
  388. //
  389. // Delete pEntry's reference to the Data object.
  390. // if an error occurred inserting into hash tables etc...
  391. // this will automatically destroy the data object.
  392. //
  393. // We've handled in the above error case!
  394. // pEntry->m_pData = 0 ;
  395. }
  396. }
  397. //
  398. // Release the cache's lock. We do this now - the constructor
  399. // for the 'Data' object should do minimal work. We will not call
  400. // the init function for the Data object, with the 'cache' locks
  401. // relingquished. This lets others use the cache while expensive
  402. // construction operations continue.
  403. //
  404. m_Lock.ExclusiveUnlock() ;
  405. //
  406. // Complete the initialization of the new Data item if it exists !
  407. //
  408. if( pDataOut != 0 ) {
  409. BOOL fSuccess = pDataOut->Init( constructor ) ;
  410. pDataOut->ExclusiveUnlock() ;
  411. if( fSuccess ) {
  412. fReturn = TRUE ;
  413. } else {
  414. pDataOut = 0 ;
  415. //
  416. // Need to Expire the entry we just placed in the Cache !
  417. //
  418. // NOTE : Expunge() manages its own locks !!!
  419. //
  420. Expunge( key, 0, FALSE ) ; // don't ignore key!!!
  421. }
  422. }
  423. }
  424. }
  425. }
  426. return fReturn ;
  427. }
  428. template < class Data,
  429. class Key,
  430. class KEYREF,
  431. class Constructor,
  432. BOOL fAtomic
  433. >
  434. inline BOOL
  435. Cache<Data, Key, KEYREF, Constructor, fAtomic>::FindOrCreate(
  436. KEYREF key,
  437. Constructor& constructor,
  438. CRefPtr< Data >& pDataOut ) {
  439. /*++
  440. Routine Description :
  441. This function is called when we want something out
  442. of the Cache. We will either find the object or create it.
  443. WE WILL GRAB THE NECESSARY LOCKS !
  444. Arguments :
  445. key - The unique key used to find the item in the Cache.
  446. constructor - An object to pass to the Data's constructor and Init
  447. functions.
  448. pDataOut - Smart pointer which receives the result
  449. Return Value :
  450. TRUE if successfull
  451. --*/
  452. DWORD dw = m_pfnHash( key ) ;
  453. return FindOrCreateInternal( dw,
  454. key,
  455. constructor,
  456. pDataOut
  457. ) ;
  458. }
  459. template < class Data,
  460. class Key,
  461. class KEYREF,
  462. class Constructor,
  463. BOOL fAtomic
  464. >
  465. MultiCache< Data, Key, KEYREF, Constructor, fAtomic >::MultiCache() :
  466. m_fValid( FALSE ),
  467. m_pCaches( 0 ) ,
  468. m_cSubCaches( 0 ),
  469. m_pfnHash( 0 ) {
  470. /*++
  471. Routine Description :
  472. This function initializes the MultiCache's data structures
  473. Arguments :
  474. None.
  475. Return Value :
  476. Nothing
  477. --*/
  478. }
  479. template < class Data,
  480. class Key,
  481. class KEYREF,
  482. class Constructor,
  483. BOOL fAtomic
  484. >
  485. MultiCache< Data, Key, KEYREF, Constructor, fAtomic >::~MultiCache() {
  486. /*++
  487. Routine Description :
  488. This function destroys all of our data structures - release
  489. all of our subcaches !
  490. Arguments :
  491. None.
  492. Return Value :
  493. Nothing
  494. --*/
  495. if( m_pCaches ) {
  496. delete[] m_pCaches ;
  497. }
  498. }
  499. template < class Data,
  500. class Key,
  501. class KEYREF,
  502. class Constructor,
  503. BOOL fAtomic
  504. >
  505. BOOL
  506. MultiCache< Data, Key, KEYREF, Constructor, fAtomic >::Init(
  507. DWORD (*pfnHash)( KEYREF ),
  508. DWORD dwLifetimeSeconds,
  509. DWORD cSubCaches,
  510. DWORD cMaxElements,
  511. PSTOPHINT_FN pfnStopHint) {
  512. /*++
  513. Routine Description :
  514. This function initializes the MultiCache - we use
  515. multiple independent Caches to split the work
  516. of caching all the data.
  517. Arguments :
  518. None.
  519. Return Value :
  520. Nothing
  521. --*/
  522. //
  523. // Check that we're in the right state for this !
  524. //
  525. _ASSERT( !m_fValid ) ;
  526. _ASSERT( m_pCaches == 0 ) ;
  527. //
  528. // Validate our arguments !!!
  529. //
  530. _ASSERT( pfnHash != 0 ) ;
  531. _ASSERT( dwLifetimeSeconds != 0 ) ;
  532. _ASSERT( cSubCaches != 0 ) ;
  533. _ASSERT( cMaxElements != 0 ) ;
  534. m_pfnHash = pfnHash ;
  535. m_cSubCaches = cSubCaches ;
  536. //
  537. // Allocate the necessary subcaches !
  538. //
  539. m_pCaches = new CACHEINSTANCE[m_cSubCaches] ;
  540. if( !m_pCaches ) {
  541. return FALSE ;
  542. } else {
  543. for( DWORD i=0; i<cSubCaches; i++ ) {
  544. if( !m_pCaches[i].Init( m_pfnHash, dwLifetimeSeconds, (cMaxElements / cSubCaches) + 1, pfnStopHint ) ) {
  545. delete m_pCaches ;
  546. return FALSE ;
  547. }
  548. }
  549. }
  550. m_fValid = TRUE ;
  551. return TRUE ;
  552. }
  553. template < class Data,
  554. class Key,
  555. class KEYREF,
  556. class Constructor,
  557. BOOL fAtomic
  558. >
  559. DWORD
  560. MultiCache< Data, Key, KEYREF, Constructor, fAtomic >::ChooseInstance(
  561. DWORD dwHash
  562. ) {
  563. /*++
  564. Routine Description :
  565. Given a Key figure out which of our subinstances we wish to use.
  566. Arguments :
  567. None.
  568. Return Value :
  569. Nothing
  570. --*/
  571. //
  572. // Check that we're in the right state for this !
  573. //
  574. _ASSERT( m_fValid ) ;
  575. _ASSERT( m_pCaches != 0 ) ;
  576. //
  577. // Validate our arguments !!!
  578. //
  579. _ASSERT( m_pfnHash != 0 ) ;
  580. _ASSERT( m_cSubCaches != 0 ) ;
  581. //DWORD dwHash = m_pfnHash( k ) ;
  582. //
  583. // Constants below stolen from C-runtime rand() function !
  584. //
  585. dwHash = (((dwHash * 214013) +2531011) >> 8) % m_cSubCaches ;
  586. return dwHash ;
  587. }
  588. template < class Data,
  589. class Key,
  590. class KEYREF,
  591. class Constructor,
  592. BOOL fAtomic
  593. >
  594. BOOL
  595. MultiCache< Data, Key, KEYREF, Constructor, fAtomic >::Expunge(
  596. KEYREF key,
  597. CACHEENTRY* pData,
  598. BOOL fExpungeAll
  599. ) {
  600. /*++
  601. Routine Description :
  602. Either force a specified element out of the cache,
  603. or force out all those that are ready to go !
  604. Arguments :
  605. None.
  606. Return Value :
  607. Nothing
  608. --*/
  609. BOOL fReturn = TRUE;
  610. if( !fExpungeAll ) {
  611. DWORD dw = m_pfnHash( key ) ;
  612. CACHEINSTANCE* pInstance = &m_pCaches[ChooseInstance( dw )] ;
  613. return pInstance->Expunge( key, pData ) ;
  614. } else {
  615. for( DWORD i=0; i<m_cSubCaches; i++ ) {
  616. fReturn &= m_pCaches[i].Expunge( key ) ; // ignore key by default
  617. }
  618. }
  619. return fReturn ;
  620. }
  621. template < class Data,
  622. class Key,
  623. class KEYREF,
  624. class Constructor,
  625. BOOL fAtomic
  626. >
  627. BOOL
  628. MultiCache< Data, Key, KEYREF, Constructor, fAtomic >::ExpungeSpecific(
  629. CALLBACKOBJ* pCallbackObject,
  630. BOOL fForced
  631. ) {
  632. /*++
  633. Routine Description :
  634. Either force a specified element out of the cache,
  635. or force out all those that are ready to go !
  636. Arguments :
  637. None.
  638. Return Value :
  639. Nothing
  640. --*/
  641. BOOL fReturn = TRUE ;
  642. for( DWORD i=0; i<m_cSubCaches; i++ ) {
  643. fReturn &= m_pCaches[i].ExpungeSpecific( pCallbackObject, fForced ) ;
  644. }
  645. return fReturn ;
  646. }
  647. template < class Data,
  648. class Key,
  649. class KEYREF,
  650. class Constructor,
  651. BOOL fAtomic
  652. >
  653. BOOL
  654. MultiCache< Data, Key, KEYREF, Constructor, fAtomic >::FindOrCreate(
  655. KEYREF key,
  656. Constructor& constructor,
  657. CRefPtr< Data >& pDataOut
  658. ) {
  659. /*++
  660. Routine Description :
  661. Arguments :
  662. None.
  663. Return Value :
  664. Nothing
  665. --*/
  666. DWORD dw = m_pfnHash( key ) ;
  667. CACHEINSTANCE* pInstance = &m_pCaches[ChooseInstance(dw)] ;
  668. return pInstance->FindOrCreateInternal( dw, key, constructor, pDataOut ) ;
  669. }
  670. template < class Data,
  671. class Key,
  672. class KEYREF,
  673. class Constructor,
  674. BOOL fAtomic
  675. >
  676. BOOL
  677. MultiCache< Data, Key, KEYREF, Constructor, fAtomic >::FindOrCreate(
  678. KEYREF key,
  679. DWORD dw,
  680. Constructor& constructor,
  681. CRefPtr< Data >& pDataOut
  682. ) {
  683. /*++
  684. Routine Description :
  685. Arguments :
  686. None.
  687. Return Value :
  688. Nothing
  689. --*/
  690. _ASSERT( dw == m_pfnHash( key ) ) ;
  691. CACHEINSTANCE* pInstance = &m_pCaches[ChooseInstance(dw)] ;
  692. return pInstance->FindOrCreateInternal( dw, key, constructor, pDataOut ) ;
  693. }