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.

1969 lines
64 KiB

  1. #ifndef _RESMGR_HXX_INCLUDED
  2. #define _RESMGR_HXX_INCLUDED
  3. // asserts
  4. //
  5. // #define RESMGRAssert to point to your favorite assert function per #include
  6. #ifdef RESMGRAssert
  7. #else // !RESMGRAssert
  8. #define RESMGRAssert Assert
  9. #endif // RESMGRAssert
  10. #ifdef COLLAssert
  11. #else // !COLLAssert
  12. #define COLLAssert RESMGRAssert
  13. #endif // COLLAssert
  14. #include "collection.hxx"
  15. #include <math.h>
  16. namespace RESMGR {
  17. //////////////////////////////////////////////////////////////////////////////////////////
  18. // CDBAResourceAllocationManager
  19. //
  20. // Implements a class used to manage how much memory a resource pool should use as a
  21. // function of the real-time usage characteristics of that pool and the entire system.
  22. // Naturally, the managed resource would have to be of a type where retention in memory
  23. // is optional, at least to some extent.
  24. //
  25. // The resource pool's size is managed by the Dynamic Buffer Allocation (DBA) algorithm.
  26. // DBA was originally invented by Andrew E. Goodsell in 1997 to manage the size of the
  27. // multiple database page caches in Exchange Server 5.5. DBA is not patented and is a
  28. // trade secret of Microsoft Corporation.
  29. //
  30. // The class is written to be platform independent. As such, there are several pure
  31. // virtual functions that must be implemented to provide the data about the OS that the
  32. // resource manager needs to make decisions. To provide these functions, derive the
  33. // class for the desired platform and then define these functions:
  34. //
  35. // TotalPhysicalMemoryPages()
  36. //
  37. // Returns the total number of pageable physical memory pages in the system.
  38. //
  39. // AvailablePhysicalMemoryPages()
  40. //
  41. // Returns the number of available pageable physical memory pages in the system.
  42. //
  43. // PhysicalMemoryPageSize()
  44. //
  45. // Returns the size of a single physical memory page.
  46. //
  47. // TotalPhysicalMemoryPageEvictions()
  48. //
  49. // Returns the total number of physical page evictions that have occurred.
  50. //
  51. // There are also several pure virtual functions that must be implemented to provide
  52. // resource specific data. To provide these functions, derive the class for each
  53. // resource to manage and then define these functions:
  54. //
  55. // TotalResources()
  56. //
  57. // Returns the total number of resources in the resource pool.
  58. //
  59. // ResourceSize()
  60. //
  61. // Returns the size of a single resource.
  62. //
  63. // TotalResourceEvictions()
  64. //
  65. // Returns the total number of resource evictions that have occurred.
  66. //
  67. // There is one final pure virtual function that must be implemented to provide the
  68. // resource pool with the size recommendation issued by the resource manager:
  69. //
  70. // SetOptimalResourcePoolSize( size_t cResource )
  71. //
  72. // Delivers the recommendation of the resource manager for the total number of
  73. // resources that should be in the resource pool at this time. Note that this
  74. // size is merely a suggestion that can be followed to maximize overall system
  75. // performance. It is not mandatory to follow this suggestion exactly or
  76. // instantaneously.
  77. //
  78. // This recommendation can be queried at any time via OptimalResourcePoolSize().
  79. //
  80. // Finally, UpdateStatistics() must be called once per second to sample this data and
  81. // and to update the current resource pool size recommendation. The statistics need to
  82. // be reset before use via ResetStatistics(). This function can be called at any time
  83. // to reset the manager's statistics for whatever reason.
  84. class CDBAResourceAllocationManager
  85. {
  86. public:
  87. CDBAResourceAllocationManager();
  88. virtual ~CDBAResourceAllocationManager();
  89. virtual void ResetStatistics();
  90. virtual void UpdateStatistics();
  91. virtual size_t OptimalResourcePoolSize();
  92. protected:
  93. virtual size_t TotalPhysicalMemoryPages() = 0;
  94. virtual size_t AvailablePhysicalMemoryPages() = 0;
  95. virtual size_t PhysicalMemoryPageSize() = 0;
  96. virtual long TotalPhysicalMemoryPageEvictions() = 0;
  97. virtual size_t TotalResources() = 0;
  98. virtual size_t ResourceSize() = 0;
  99. virtual long TotalResourceEvictions() = 0;
  100. virtual void SetOptimalResourcePoolSize( size_t cResource ) = 0;
  101. private:
  102. enum { m_cRollingAvgDepth = 3 };
  103. size_t m_cTotalPage[ m_cRollingAvgDepth ];
  104. int m_icTotalPageOldest;
  105. double m_cTotalPageSum;
  106. double m_cTotalPageAvg;
  107. size_t m_cAvailPage[ m_cRollingAvgDepth ];
  108. int m_icAvailPageOldest;
  109. double m_cAvailPageSum;
  110. double m_cAvailPageAvg;
  111. size_t m_cbPage[ m_cRollingAvgDepth ];
  112. int m_icbPageOldest;
  113. double m_cbPageSum;
  114. double m_cbPageAvg;
  115. long m_cPageEvict2;
  116. long m_cPageEvict1;
  117. long m_dcPageEvict[ m_cRollingAvgDepth ];
  118. int m_idcPageEvictOldest;
  119. double m_dcPageEvictSum;
  120. double m_dcPageEvictAvg;
  121. size_t m_cbResource[ m_cRollingAvgDepth ];
  122. int m_icbResourceOldest;
  123. double m_cbResourceSum;
  124. double m_cbResourceAvg;
  125. size_t m_cTotalResource[ m_cRollingAvgDepth ];
  126. int m_icTotalResourceOldest;
  127. double m_cTotalResourceSum;
  128. double m_cTotalResourceAvg;
  129. long m_cResourceEvict2;
  130. long m_cResourceEvict1;
  131. long m_dcResourceEvict[ m_cRollingAvgDepth ];
  132. int m_idcResourceEvictOldest;
  133. double m_dcResourceEvictSum;
  134. double m_dcResourceEvictAvg;
  135. size_t m_cResourceMin;
  136. size_t m_cResourceMax;
  137. };
  138. // ctor
  139. inline CDBAResourceAllocationManager::CDBAResourceAllocationManager()
  140. {
  141. // nothing to do
  142. }
  143. // virtual dtor
  144. inline CDBAResourceAllocationManager::~CDBAResourceAllocationManager()
  145. {
  146. // nothing to do
  147. }
  148. // resets the observed statistics for the resource pool and the system such
  149. // that the current state appears to have been the steady state for as long as
  150. // past behavioral data is retained
  151. inline void CDBAResourceAllocationManager::ResetStatistics()
  152. {
  153. // load all statistics
  154. size_t cTotalPageInit = TotalPhysicalMemoryPages();
  155. for ( m_icTotalPageOldest = m_cRollingAvgDepth - 1; m_icTotalPageOldest >= 0; m_icTotalPageOldest-- )
  156. {
  157. m_cTotalPage[ m_icTotalPageOldest ] = cTotalPageInit;
  158. }
  159. m_icTotalPageOldest = 0;
  160. m_cTotalPageSum = m_cRollingAvgDepth * (double)cTotalPageInit;
  161. m_cTotalPageAvg = (double)cTotalPageInit;
  162. size_t cAvailPageInit = AvailablePhysicalMemoryPages();
  163. for ( m_icAvailPageOldest = m_cRollingAvgDepth - 1; m_icAvailPageOldest >= 0; m_icAvailPageOldest-- )
  164. {
  165. m_cAvailPage[ m_icAvailPageOldest ] = cAvailPageInit;
  166. }
  167. m_icAvailPageOldest = 0;
  168. m_cAvailPageSum = m_cRollingAvgDepth * (double)cAvailPageInit;
  169. m_cAvailPageAvg = (double)cAvailPageInit;
  170. size_t cbPageInit = PhysicalMemoryPageSize();
  171. for ( m_icbPageOldest = m_cRollingAvgDepth - 1; m_icbPageOldest >= 0; m_icbPageOldest-- )
  172. {
  173. m_cbPage[ m_icbPageOldest ] = cbPageInit;
  174. }
  175. m_icbPageOldest = 0;
  176. m_cbPageSum = m_cRollingAvgDepth * (double)cbPageInit;
  177. m_cbPageAvg = (double)cbPageInit;
  178. long cPageEvictInit = TotalPhysicalMemoryPageEvictions();
  179. m_cPageEvict2 = cPageEvictInit;
  180. m_cPageEvict1 = cPageEvictInit;
  181. for ( m_idcPageEvictOldest = m_cRollingAvgDepth - 1; m_idcPageEvictOldest >= 0; m_idcPageEvictOldest-- )
  182. {
  183. m_dcPageEvict[ m_idcPageEvictOldest ] = 0;
  184. }
  185. m_idcPageEvictOldest = 0;
  186. m_dcPageEvictSum = 0;
  187. m_dcPageEvictAvg = 0;
  188. size_t cbResourceInit = ResourceSize();
  189. for ( m_icbResourceOldest = m_cRollingAvgDepth - 1; m_icbResourceOldest >= 0; m_icbResourceOldest-- )
  190. {
  191. m_cbResource[ m_icbResourceOldest ] = cbResourceInit;
  192. }
  193. m_icbResourceOldest = 0;
  194. m_cbResourceSum = m_cRollingAvgDepth * (double)cbResourceInit;
  195. m_cbResourceAvg = (double)cbResourceInit;
  196. size_t cTotalResourceInit = TotalResources();
  197. for ( m_icTotalResourceOldest = m_cRollingAvgDepth - 1; m_icTotalResourceOldest >= 0; m_icTotalResourceOldest-- )
  198. {
  199. m_cTotalResource[ m_icTotalResourceOldest ] = cTotalResourceInit;
  200. }
  201. m_icTotalResourceOldest = 0;
  202. m_cTotalResourceSum = m_cRollingAvgDepth * (double)cTotalResourceInit;
  203. m_cTotalResourceAvg = (double)cTotalResourceInit;
  204. long cResourceEvictInit = TotalResourceEvictions();
  205. m_cResourceEvict2 = cResourceEvictInit;
  206. m_cResourceEvict1 = cResourceEvictInit;
  207. for ( m_idcResourceEvictOldest = m_cRollingAvgDepth - 1; m_idcResourceEvictOldest >= 0; m_idcResourceEvictOldest-- )
  208. {
  209. m_dcResourceEvict[ m_idcResourceEvictOldest ] = 0;
  210. }
  211. m_idcResourceEvictOldest = 0;
  212. m_dcResourceEvictSum = 0;
  213. m_dcResourceEvictAvg = 0;
  214. m_cResourceMin = 1;
  215. m_cResourceMax = size_t( m_cTotalPageAvg * m_cbPageAvg / m_cbResourceAvg );
  216. }
  217. // updates the observed characteristics of the resource pool and the system as
  218. // a whole for the purpose of making real-time suggestions as to the size of
  219. // the resource pool. this function should be called at approximately 1 Hz
  220. inline void CDBAResourceAllocationManager::UpdateStatistics()
  221. {
  222. // update all statistics
  223. m_cTotalPageSum -= m_cTotalPage[ m_icTotalPageOldest ];
  224. m_cTotalPage[ m_icTotalPageOldest ] = TotalPhysicalMemoryPages();
  225. m_cTotalPageSum += m_cTotalPage[ m_icTotalPageOldest ];
  226. m_icTotalPageOldest = ( m_icTotalPageOldest + 1 ) % m_cRollingAvgDepth;
  227. m_cTotalPageAvg = m_cTotalPageSum / m_cRollingAvgDepth;
  228. m_cAvailPageSum -= m_cAvailPage[ m_icAvailPageOldest ];
  229. m_cAvailPage[ m_icAvailPageOldest ] = AvailablePhysicalMemoryPages();
  230. m_cAvailPageSum += m_cAvailPage[ m_icAvailPageOldest ];
  231. m_icAvailPageOldest = ( m_icAvailPageOldest + 1 ) % m_cRollingAvgDepth;
  232. m_cAvailPageAvg = m_cAvailPageSum / m_cRollingAvgDepth;
  233. m_cbPageSum -= m_cbPage[ m_icbPageOldest ];
  234. m_cbPage[ m_icbPageOldest ] = PhysicalMemoryPageSize();
  235. m_cbPageSum += m_cbPage[ m_icbPageOldest ];
  236. m_icbPageOldest = ( m_icbPageOldest + 1 ) % m_cRollingAvgDepth;
  237. m_cbPageAvg = m_cbPageSum / m_cRollingAvgDepth;
  238. m_cPageEvict2 = m_cPageEvict1;
  239. m_cPageEvict1 = TotalPhysicalMemoryPageEvictions();
  240. m_dcPageEvictSum -= m_dcPageEvict[ m_idcPageEvictOldest ];
  241. m_dcPageEvict[ m_idcPageEvictOldest ] = m_cPageEvict1 - m_cPageEvict2;
  242. m_dcPageEvictSum += m_dcPageEvict[ m_idcPageEvictOldest ];
  243. m_idcPageEvictOldest = ( m_idcPageEvictOldest + 1 ) % m_cRollingAvgDepth;
  244. m_dcPageEvictAvg = m_dcPageEvictSum / m_cRollingAvgDepth;
  245. m_cbResourceSum -= m_cbResource[ m_icbResourceOldest ];
  246. m_cbResource[ m_icbResourceOldest ] = ResourceSize();
  247. m_cbResourceSum += m_cbResource[ m_icbResourceOldest ];
  248. m_icbResourceOldest = ( m_icbResourceOldest + 1 ) % m_cRollingAvgDepth;
  249. m_cbResourceAvg = m_cbResourceSum / m_cRollingAvgDepth;
  250. m_cTotalResourceSum -= m_cTotalResource[ m_icTotalResourceOldest ];
  251. m_cTotalResource[ m_icTotalResourceOldest ] = TotalResources();
  252. m_cTotalResourceSum += m_cTotalResource[ m_icTotalResourceOldest ];
  253. m_icTotalResourceOldest = ( m_icTotalResourceOldest + 1 ) % m_cRollingAvgDepth;
  254. m_cTotalResourceAvg = m_cTotalResourceSum / m_cRollingAvgDepth;
  255. m_cResourceEvict2 = m_cResourceEvict1;
  256. m_cResourceEvict1 = TotalResourceEvictions();
  257. m_dcResourceEvictSum -= m_dcResourceEvict[ m_idcResourceEvictOldest ];
  258. m_dcResourceEvict[ m_idcResourceEvictOldest ] = m_cResourceEvict1 - m_cResourceEvict2;
  259. m_dcResourceEvictSum += m_dcResourceEvict[ m_idcResourceEvictOldest ];
  260. m_idcResourceEvictOldest = ( m_idcResourceEvictOldest + 1 ) % m_cRollingAvgDepth;
  261. m_dcResourceEvictAvg = m_dcResourceEvictSum / m_cRollingAvgDepth;
  262. m_cResourceMin = 1;
  263. m_cResourceMax = size_t( m_cTotalPageAvg * m_cbPageAvg / m_cbResourceAvg );
  264. // recommend a size for the resource pool
  265. SetOptimalResourcePoolSize( OptimalResourcePoolSize() );
  266. }
  267. // returns the current recommended size for the resource pool according to the
  268. // observed characteristics of that pool and the system as a whole as sampled
  269. // via UpdateStatistics(). the recommended size is given in bytes. note that
  270. // the recommended size is merely a suggestion that can be used to maximize
  271. // overall system performance. it is not mandatory to follow this suggestion
  272. // exactly or instantaneously
  273. inline size_t CDBAResourceAllocationManager::OptimalResourcePoolSize()
  274. {
  275. // our goal is to equalize the internal memory pressure in our resource
  276. // pool with the external memory pressure from the rest of the system. we
  277. // do this using the following differential equation:
  278. //
  279. // dRM/dt = k1 * AM/TM * dRE/dt - k2 * RM/TM * dPE/dt
  280. //
  281. // RM = Total Resource Memory
  282. // k1 = fudge factor 1 (1.0 by default)
  283. // AM = Available System Memory
  284. // TM = Total System Memory
  285. // RE = Resource Evictions
  286. // k2 = fudge factor 2 (1.0 by default)
  287. // PE = System Page Evictions
  288. //
  289. // this equation has two parts:
  290. //
  291. // the first half tries to grow the resource pool proportional to the
  292. // internal resource pool memory pressure and to the amount of memory
  293. // available in the system
  294. //
  295. // the second half tries to shrink the resource pool proportional to the
  296. // external resource pool memory pressure and to the amount of resource
  297. // memory we own
  298. //
  299. // in other words, the more available memory and the faster resources are
  300. // evicted, the faster the pool grows. the larger the pool size and the
  301. // faster memory pages are evicted, the faster the pool shrinks
  302. //
  303. // the method behind the madness is an approximation of page victimization
  304. // by the OS under memory pressure. the more memory we have then the more
  305. // likely it is that one of our pages will be evicted. to pre-emptively
  306. // avoid this, we'll voluntarily reduce our memory usage when we determine
  307. // that memory pressure will cause our pages to get evicted. note that
  308. // the reverse of this is also true. if we make it clear to the OS that
  309. // we need this memory by slowly growing while under memory pressure, we
  310. // force pages of memory owned by others to be victimized to be used by
  311. // our pool. the more memory any one of those others owns, the more
  312. // likely it is that one of their pages will be stolen. if any of the
  313. // others is another resource allocation manager then they can communicate
  314. // their memory needs indirectly via the memory pressure they each apply
  315. // on the system. the net result is an equilibrium where each pool gets
  316. // the memory it "deserves"
  317. // compute the change in the amount of memory that this resource pool
  318. // should have
  319. double k1 = 1.0;
  320. double cbAvailMemory = m_cAvailPageAvg * m_cbPageAvg;
  321. double dcbResourceEvict = m_dcResourceEvictAvg * m_cbResourceAvg;
  322. double k2 = 1.0;
  323. double cbResourcePool = m_cTotalResourceAvg * m_cbResourceAvg;
  324. double dcbPageEvict = m_dcPageEvictAvg * m_cbPageAvg;
  325. double cbTotalMemory = m_cTotalPageAvg * m_cbPageAvg;
  326. double dcTotalResource = (
  327. k1 * cbAvailMemory * dcbResourceEvict -
  328. k2 * cbResourcePool * dcbPageEvict
  329. ) /
  330. (
  331. cbTotalMemory * m_cbResourceAvg
  332. );
  333. // round the delta away from zero so that we don't get stuck at the same
  334. // number of resources if the delta is commonly less than one resource
  335. dcTotalResource += dcTotalResource < 0 ? -0.5 : 0.5;
  336. modf( dcTotalResource, &dcTotalResource );
  337. // get the new amount of memory that this resource pool should have
  338. double cResourceNew = TotalResources() + dcTotalResource;
  339. // limit the new resource pool size to the realm of sanity just in case...
  340. cResourceNew = max( cResourceNew, m_cResourceMin );
  341. cResourceNew = min( cResourceNew, m_cResourceMax );
  342. // return the suggested resource pool size
  343. return size_t( cResourceNew );
  344. }
  345. //////////////////////////////////////////////////////////////////////////////////////////
  346. // CLRUKResourceUtilityManager
  347. //
  348. // Implements a class used to manage a resource via the LRUK Replacement Policy. Each
  349. // resource can be cached, touched, and evicted. The current pool of resources can
  350. // also be scanned in order by ascending utility to allow eviction of less useful
  351. // resouces by a clean procedure.
  352. //
  353. // m_Kmax = the maximum K-ness of the LRUK Replacement Policy (the actual
  354. // K-ness can be set at init time)
  355. // CResource = class representing the managed resource
  356. // OffsetOfIC = inline function returning the offset of the CInvasiveContext
  357. // contained in each CResource
  358. // CKey = class representing the key which uniquely identifies each
  359. // instance of a CResource. CKey must implement a default ctor,
  360. // operator=(), and operator==()
  361. //
  362. // You must use the DECLARE_LRUK_RESOURCE_UTILITY_MANAGER macro to declare this class.
  363. //
  364. // You must implement the following inline function and pass its hashing characteristics
  365. // into ErrInit():
  366. //
  367. // int CLRUKResourceUtilityManager< m_Kmax, CResource, OffsetOfIC, CKey >::CHistoryTable::CKeyEntry::Hash( const CKey& key );
  368. // CONSIDER: add code to special case LRU
  369. // CONSIDER: add a fast allocator for CHistorys
  370. template< int m_Kmax, class CResource, PfnOffsetOf OffsetOfIC, class CKey >
  371. class CLRUKResourceUtilityManager
  372. {
  373. public:
  374. // timestamp used for resource touch times
  375. typedef DWORD TICK;
  376. enum { tickNil = ~TICK( 0 ) };
  377. enum { tickMask = ~TICK( 0 ) ^ TICK( 1 ) };
  378. // class containing context needed per CResource
  379. class CInvasiveContext
  380. {
  381. public:
  382. CInvasiveContext() {}
  383. ~CInvasiveContext() {}
  384. static SIZE_T OffsetOfILE() { return OffsetOfIC() + OffsetOf( CInvasiveContext, m_aiic ); }
  385. static SIZE_T OffsetOfAIIC() { return OffsetOfIC() + OffsetOf( CInvasiveContext, m_aiic ); }
  386. private:
  387. friend class CLRUKResourceUtilityManager< m_Kmax, CResource, OffsetOfIC, CKey >;
  388. CApproximateIndex< TICK, CResource, OffsetOfAIIC >::CInvasiveContext m_aiic;
  389. TICK m_tickLast;
  390. TICK m_rgtick[ m_Kmax ];
  391. TICK m_tickIndex;
  392. };
  393. // API Error Codes
  394. enum ERR
  395. {
  396. errSuccess,
  397. errInvalidParameter,
  398. errOutOfMemory,
  399. errResourceNotCached,
  400. errNoCurrentResource,
  401. };
  402. // API Lock Context
  403. class CLock;
  404. public:
  405. // ctor / dtor
  406. CLRUKResourceUtilityManager( const int Rank );
  407. ~CLRUKResourceUtilityManager();
  408. // API
  409. ERR ErrInit( const int K,
  410. const double csecCorrelatedTouch,
  411. const double csecTimeout,
  412. const double csecUncertainty,
  413. const double dblHashLoadFactor,
  414. const double dblHashUniformity,
  415. const double dblSpeedSizeTradeoff );
  416. void Term();
  417. ERR ErrCacheResource( const CKey& key, CResource* const pres, const BOOL fUseHistory = fTrue );
  418. void TouchResource( CResource* const pres, const TICK tickNow = _TickCurrentTime() );
  419. BOOL FHotResource( CResource* const pres );
  420. ERR ErrEvictResource( const CKey& key, CResource* const pres, const BOOL fKeepHistory = fTrue );
  421. void LockResourceForEvict( CResource* const pres, CLock* const plock );
  422. void UnlockResourceForEvict( CLock* const plock );
  423. void BeginResourceScan( CLock* const plock );
  424. ERR ErrGetCurrentResource( CLock* const plock, CResource** const ppres );
  425. ERR ErrGetNextResource( CLock* const plock, CResource** const ppres );
  426. ERR ErrEvictCurrentResource( CLock* const plock, const CKey& key, const BOOL fKeepHistory = fTrue );
  427. void EndResourceScan( CLock* const plock );
  428. DWORD CHistoryRecord();
  429. DWORD CHistoryHit();
  430. DWORD CHistoryRequest();
  431. DWORD CResourceScanned();
  432. DWORD CResourceScannedOutOfOrder();
  433. public:
  434. // class containing the history of an evicted CResource instance
  435. class CHistory
  436. {
  437. public:
  438. CHistory() {}
  439. ~CHistory() {}
  440. static SIZE_T OffsetOfAIIC() { return OffsetOf( CHistory, m_aiic ); }
  441. public:
  442. CApproximateIndex< TICK, CHistory, OffsetOfAIIC >::CInvasiveContext m_aiic;
  443. CKey m_key;
  444. TICK m_rgtick[ m_Kmax ];
  445. };
  446. private:
  447. // index over CResources by LRUK
  448. typedef CApproximateIndex< TICK, CResource, CInvasiveContext::OffsetOfAIIC > CResourceLRUK;
  449. // index over history by LRUK
  450. typedef CApproximateIndex< TICK, CHistory, CHistory::OffsetOfAIIC > CHistoryLRUK;
  451. // list of resources that need to have their positions updated in the
  452. // resource LRUK
  453. typedef CInvasiveList< CResource, CInvasiveContext::OffsetOfILE > CUpdateList;
  454. // list of resources that couldn't be placed in their correct positions
  455. // in the resource LRUK
  456. typedef CInvasiveList< CResource, CInvasiveContext::OffsetOfILE > CStuckList;
  457. public:
  458. // API Lock Context
  459. class CLock
  460. {
  461. public:
  462. CLock() {}
  463. ~CLock() {}
  464. private:
  465. friend class CLRUKResourceUtilityManager< m_Kmax, CResource, OffsetOfIC, CKey >;
  466. CResourceLRUK::CLock m_lock;
  467. TICK m_tickIndexCurrent;
  468. CUpdateList m_UpdateList;
  469. CStuckList m_StuckList;
  470. CResource* m_presStuckList;
  471. CResource* m_presStuckListNext;
  472. };
  473. // entry used in the hash table used to map CKeys to CHistorys
  474. class CHistoryEntry
  475. {
  476. public:
  477. CHistoryEntry() {}
  478. ~CHistoryEntry() {}
  479. CHistoryEntry& operator=( const CHistoryEntry& he )
  480. {
  481. m_KeySignature = he.m_KeySignature;
  482. m_phist = he.m_phist;
  483. return *this;
  484. }
  485. public:
  486. ULONG_PTR m_KeySignature;
  487. CHistory* m_phist;
  488. };
  489. private:
  490. // table that maps CKeys to CHistoryEntrys
  491. typedef CDynamicHashTable< CKey, CHistoryEntry > CHistoryTable;
  492. private:
  493. void _TouchResource( CInvasiveContext* const pic, const TICK tickNow, const TICK tickLastBIExpected );
  494. void _RestoreHistory( const CKey& key, CInvasiveContext* const pic, const TICK tickNow );
  495. void _StoreHistory( const CKey& key, CInvasiveContext* const pic );
  496. CHistory* _PhistAllocHistory();
  497. CHistoryLRUK::ERR _ErrInsertHistory( CHistory* const phist );
  498. CResource* _PresFromPic( CInvasiveContext* const pic ) const;
  499. CInvasiveContext* _PicFromPres( CResource* const pres ) const;
  500. static TICK _TickCurrentTime();
  501. long _CmpTick( const TICK tick1, const TICK tick2 ) const;
  502. private:
  503. // never updated
  504. BOOL m_fInitialized;
  505. DWORD m_K;
  506. TICK m_ctickCorrelatedTouch;
  507. TICK m_ctickTimeout;
  508. TICK m_dtickUncertainty;
  509. BYTE m_rgbReserved1[ 12 ];
  510. // rarely updated
  511. volatile DWORD m_cHistoryRecord;
  512. volatile DWORD m_cHistoryHit;
  513. volatile DWORD m_cHistoryReq;
  514. volatile DWORD m_cResourceScanned;
  515. volatile DWORD m_cResourceScannedOutOfOrder;
  516. BYTE m_rgbReserved2[ 12 ];
  517. CHistoryLRUK m_HistoryLRUK;
  518. // BYTE m_rgbReserved3[ 0 ];
  519. CHistoryTable m_KeyHistory;
  520. // BYTE m_rgbReserved4[ 0 ];
  521. // commonly updated
  522. CResourceLRUK m_ResourceLRUK;
  523. // BYTE m_rgbReserved5[ 0 ];
  524. };
  525. // ctor
  526. template< int m_Kmax, class CResource, PfnOffsetOf OffsetOfIC, class CKey >
  527. inline CLRUKResourceUtilityManager< m_Kmax, CResource, OffsetOfIC, CKey >::
  528. CLRUKResourceUtilityManager( const int Rank )
  529. : m_fInitialized( fFalse ),
  530. m_HistoryLRUK( Rank - 1 ),
  531. m_KeyHistory( Rank - 1 ),
  532. m_ResourceLRUK( Rank )
  533. {
  534. }
  535. // dtor
  536. template< int m_Kmax, class CResource, PfnOffsetOf OffsetOfIC, class CKey >
  537. inline CLRUKResourceUtilityManager< m_Kmax, CResource, OffsetOfIC, CKey >::
  538. ~CLRUKResourceUtilityManager()
  539. {
  540. }
  541. // initializes the LRUK resource utility manager using the given parameters.
  542. // if the RUM cannot be initialized, errOutOfMemory is returned
  543. template< int m_Kmax, class CResource, PfnOffsetOf OffsetOfIC, class CKey >
  544. inline CLRUKResourceUtilityManager< m_Kmax, CResource, OffsetOfIC, CKey >::ERR CLRUKResourceUtilityManager< m_Kmax, CResource, OffsetOfIC, CKey >::
  545. ErrInit( const int K,
  546. const double csecCorrelatedTouch,
  547. const double csecTimeout,
  548. const double csecUncertainty,
  549. const double dblHashLoadFactor,
  550. const double dblHashUniformity,
  551. const double dblSpeedSizeTradeoff )
  552. {
  553. // validate all parameters
  554. if ( K < 1 || K > m_Kmax ||
  555. csecCorrelatedTouch < 0 ||
  556. csecTimeout < 0 ||
  557. dblHashLoadFactor < 0 ||
  558. dblHashUniformity < 1.0 )
  559. {
  560. return errInvalidParameter;
  561. }
  562. // init our parameters
  563. m_K = DWORD( K );
  564. m_ctickCorrelatedTouch = TICK( csecCorrelatedTouch * 1000.0 );
  565. m_ctickTimeout = TICK( csecTimeout * 1000.0 );
  566. m_dtickUncertainty = TICK( max( csecUncertainty * 1000.0, 2.0 ) );
  567. // init our counters
  568. m_cHistoryRecord = 0;
  569. m_cHistoryHit = 0;
  570. m_cHistoryReq = 0;
  571. m_cResourceScanned = 0;
  572. m_cResourceScannedOutOfOrder = 0;
  573. // init our indexes
  574. //
  575. // NOTE: the precision for the history LRUK is intentionally low so that
  576. // we are forced to purge history records older than twice the timeout
  577. // in order to insert new history records. we would normally only purge
  578. // history records on demand which could allow a sudden burst of activity
  579. // to permanently allocate an abnormally high number of history records
  580. if ( m_HistoryLRUK.ErrInit( 4 * m_K * m_ctickTimeout,
  581. m_dtickUncertainty,
  582. dblSpeedSizeTradeoff ) != errSuccess )
  583. {
  584. Term();
  585. return errOutOfMemory;
  586. }
  587. if ( m_KeyHistory.ErrInit( dblHashLoadFactor,
  588. dblHashUniformity ) != errSuccess )
  589. {
  590. Term();
  591. return errOutOfMemory;
  592. }
  593. #ifdef DEBUG
  594. const TICK dtickLRUKPrecision = 2 * m_K * m_ctickTimeout;
  595. #else // !DEBUG
  596. #ifdef RTM
  597. const TICK dtickLRUKPrecision = ~TICK( 0 ); // 49.7 days
  598. #else // !RTM
  599. const TICK dtickLRUKPrecision = 2 * 24 * 60 * 60 * 1000; // 2 days
  600. #endif // RTM
  601. #endif // DEBUG
  602. if ( m_ResourceLRUK.ErrInit( dtickLRUKPrecision,
  603. m_dtickUncertainty,
  604. dblSpeedSizeTradeoff ) != errSuccess )
  605. {
  606. Term();
  607. return errOutOfMemory;
  608. }
  609. m_fInitialized = fTrue;
  610. return errSuccess;
  611. }
  612. // terminates the LRUK RUM. this function can be called even if the RUM has
  613. // never been initialized or is only partially initialized
  614. //
  615. // NOTE: any data stored in the RUM at this time will be lost!
  616. template< int m_Kmax, class CResource, PfnOffsetOf OffsetOfIC, class CKey >
  617. inline void CLRUKResourceUtilityManager< m_Kmax, CResource, OffsetOfIC, CKey >::
  618. Term()
  619. {
  620. // purge all history records from the history LRUK. we must do this from
  621. // the history LRUK and not the history table as it is possible for the
  622. // history table to lose pointers to history records in the history LRUK.
  623. // see ErrEvictCurrentResource for details
  624. if ( m_fInitialized )
  625. {
  626. CHistoryLRUK::CLock lockHLRUK;
  627. m_HistoryLRUK.MoveBeforeFirst( &lockHLRUK );
  628. while ( m_HistoryLRUK.ErrMoveNext( &lockHLRUK ) == errSuccess )
  629. {
  630. CHistory* phist;
  631. CHistoryLRUK::ERR errHLRUK = m_HistoryLRUK.ErrRetrieveEntry( &lockHLRUK, &phist );
  632. RESMGRAssert( errHLRUK == CHistoryLRUK::errSuccess );
  633. errHLRUK = m_HistoryLRUK.ErrDeleteEntry( &lockHLRUK );
  634. RESMGRAssert( errHLRUK == CHistoryLRUK::errSuccess );
  635. delete phist;
  636. }
  637. m_HistoryLRUK.UnlockKeyPtr( &lockHLRUK );
  638. }
  639. // terminate our indexes
  640. m_ResourceLRUK.Term();
  641. m_KeyHistory.Term();
  642. m_HistoryLRUK.Term();
  643. m_fInitialized = fFalse;
  644. }
  645. // starts management of the specified resource optionally using any previous
  646. // knowledge the RUM has about this resource. the resource must currently be
  647. // evicted from the RUM. the resource will be touched by this call. if we
  648. // cannot start management of this resource, errOutOfMemory will be returned
  649. template< int m_Kmax, class CResource, PfnOffsetOf OffsetOfIC, class CKey >
  650. inline CLRUKResourceUtilityManager< m_Kmax, CResource, OffsetOfIC, CKey >::ERR CLRUKResourceUtilityManager< m_Kmax, CResource, OffsetOfIC, CKey >::
  651. ErrCacheResource( const CKey& key, CResource* const pres, const BOOL fUseHistory )
  652. {
  653. ERR err = errSuccess;
  654. CLock lock;
  655. CResourceLRUK::ERR errLRUK;
  656. CInvasiveContext* const pic = _PicFromPres( pres );
  657. // initialize this resource to be touched once by setting all of its
  658. // touch times to the current time
  659. const TICK tickNow = _TickCurrentTime();
  660. for ( int K = 1; K <= m_Kmax; K++ )
  661. {
  662. pic->m_rgtick[ K - 1 ] = tickNow;
  663. }
  664. pic->m_tickLast = tickNow;
  665. // we are supposed to use the history for this resource if available
  666. if ( fUseHistory )
  667. {
  668. // restore the history for this resource
  669. _RestoreHistory( key, pic, tickNow );
  670. }
  671. // insert this resource into the resource LRUK at its Kth touch time or as
  672. // close as we can get to that Kth touch time
  673. do
  674. {
  675. const TICK tickK = pic->m_rgtick[ m_K - 1 ];
  676. m_ResourceLRUK.LockKeyPtr( tickK, pres, &lock.m_lock );
  677. pic->m_tickIndex = tickK;
  678. errLRUK = m_ResourceLRUK.ErrInsertEntry( &lock.m_lock, pres, fTrue );
  679. if ( errLRUK != CResourceLRUK::errSuccess )
  680. {
  681. pic->m_tickIndex = tickNil;
  682. m_ResourceLRUK.UnlockKeyPtr( &lock.m_lock );
  683. const TICK tickYoungest = m_ResourceLRUK.KeyInsertMost();
  684. m_ResourceLRUK.LockKeyPtr( tickYoungest, pres, &lock.m_lock );
  685. pic->m_tickIndex = tickYoungest;
  686. errLRUK = m_ResourceLRUK.ErrInsertEntry( &lock.m_lock, pres, fTrue );
  687. if ( errLRUK != CResourceLRUK::errSuccess )
  688. {
  689. pic->m_tickIndex = tickNil;
  690. }
  691. }
  692. m_ResourceLRUK.UnlockKeyPtr( &lock.m_lock );
  693. }
  694. while ( errLRUK == CResourceLRUK::errKeyRangeExceeded );
  695. RESMGRAssert( errLRUK == CResourceLRUK::errSuccess ||
  696. errLRUK == CResourceLRUK::errOutOfMemory );
  697. return errLRUK == CResourceLRUK::errSuccess ? errSuccess : errOutOfMemory;
  698. }
  699. // touches the specifed resource according to the LRUK Replacement Policy
  700. //
  701. // NOTE: only the touch times are updated here. the actual update of this
  702. // resource's position in the LRUK index is amortized to ErrGetNextResource().
  703. // this scheme minimizes updates to the index and localizes those updates
  704. // to the thread(s) that perform cleanup of the resources. this scheme also
  705. // makes it impossible to block while touching a resource
  706. template< int m_Kmax, class CResource, PfnOffsetOf OffsetOfIC, class CKey >
  707. inline void CLRUKResourceUtilityManager< m_Kmax, CResource, OffsetOfIC, CKey >::
  708. TouchResource( CResource* const pres, const TICK tickNow )
  709. {
  710. CInvasiveContext* const pic = _PicFromPres( pres );
  711. // get the current last touch time of the resource. we will use this time
  712. // to lock the resource for touching. if the last touch time ever changes
  713. // to not match this touch time then we can no longer touch the resource
  714. //
  715. // NOTE: munge the last tick time so that it will never match tickNil
  716. const TICK tickLastBIExpected = pic->m_tickLast & tickMask;
  717. // if the current time is equal to the last touch time then there is no
  718. // need to touch the resource
  719. //
  720. // NOTE: also handle the degenerate case where the current time is less
  721. // than the last touch time
  722. if ( _CmpTick( tickNow, tickLastBIExpected ) <= 0 )
  723. {
  724. // do not touch the resource
  725. }
  726. // if the current time is not equal to the last touch time then we need
  727. // to update the touch times of the resource
  728. else
  729. {
  730. _TouchResource( pic, tickNow, tickLastBIExpected );
  731. }
  732. }
  733. // returns fTrue if the specified resource is touched frequently
  734. template< int m_Kmax, class CResource, PfnOffsetOf OffsetOfIC, class CKey >
  735. inline BOOL CLRUKResourceUtilityManager< m_Kmax, CResource, OffsetOfIC, CKey >::
  736. FHotResource( CResource* const pres )
  737. {
  738. CInvasiveContext* const pic = _PicFromPres( pres );
  739. // a resource is hot if it's Kth touch time is within the Kth time region
  740. // in the index
  741. const TICK tickNow = _TickCurrentTime();
  742. const TICK tickKTimeout = ( m_K - 2 ) * m_ctickTimeout;
  743. const TICK tickKHot = tickNow + tickKTimeout;
  744. return _CmpTick( pic->m_rgtick[ m_K - 1 ], tickKHot ) > 0;
  745. }
  746. // stops management of the specified resource optionally saving any current
  747. // knowledge the RUM has about this resource. if the resource is not currently
  748. // cached by the RUM, errResourceNotCached will be returned
  749. template< int m_Kmax, class CResource, PfnOffsetOf OffsetOfIC, class CKey >
  750. inline CLRUKResourceUtilityManager< m_Kmax, CResource, OffsetOfIC, CKey >::ERR CLRUKResourceUtilityManager< m_Kmax, CResource, OffsetOfIC, CKey >::
  751. ErrEvictResource( const CKey& key, CResource* const pres, const BOOL fKeepHistory )
  752. {
  753. ERR err = errSuccess;
  754. CLock lock;
  755. // lock and evict this resource from the resource LRUK by setting up our
  756. // currency to look like we navigated to this resource normally and then
  757. // calling our normal eviction routine
  758. LockResourceForEvict( pres, &lock );
  759. if ( ( err = ErrEvictCurrentResource( &lock, key, fKeepHistory ) ) != errSuccess )
  760. {
  761. RESMGRAssert( err == errNoCurrentResource );
  762. err = errResourceNotCached;
  763. }
  764. UnlockResourceForEvict( &lock );
  765. return err;
  766. }
  767. // sets up the specified lock context in preparation for evicting the given
  768. // resource
  769. template< int m_Kmax, class CResource, PfnOffsetOf OffsetOfIC, class CKey >
  770. inline void CLRUKResourceUtilityManager< m_Kmax, CResource, OffsetOfIC, CKey >::
  771. LockResourceForEvict( CResource* const pres, CLock* const plock )
  772. {
  773. // get the current index touch time for this resource
  774. CInvasiveContext* const pic = _PicFromPres( pres );
  775. const TICK tickIndex = pic->m_tickIndex;
  776. // lock this resource for this Kth touch time in the LRUK
  777. m_ResourceLRUK.LockKeyPtr( tickIndex, pres, &plock->m_lock );
  778. // the index touch time has changed for this resource or it is tickNil
  779. if ( pic->m_tickIndex != tickIndex || tickIndex == tickNil )
  780. {
  781. // release our lock and lock tickNil for this resource. we know that
  782. // we will never find a resource in this bucket so we will have the
  783. // desired errNoCurrentResource from ErrEvictCurrentResource
  784. m_ResourceLRUK.UnlockKeyPtr( &plock->m_lock );
  785. m_ResourceLRUK.MoveBeforeKeyPtr( tickNil, NULL, &plock->m_lock );
  786. }
  787. }
  788. // releases the lock acquired with LockResourceForEvict()
  789. template< int m_Kmax, class CResource, PfnOffsetOf OffsetOfIC, class CKey >
  790. inline void CLRUKResourceUtilityManager< m_Kmax, CResource, OffsetOfIC, CKey >::
  791. UnlockResourceForEvict( CLock* const plock )
  792. {
  793. m_ResourceLRUK.UnlockKeyPtr( &plock->m_lock );
  794. }
  795. // sets up the specified lock context in preparation for scanning all resources
  796. // managed by the RUM by ascending utility
  797. //
  798. // NOTE: this function will acquire a lock that must eventually be released
  799. // via EndResourceScan()
  800. template< int m_Kmax, class CResource, PfnOffsetOf OffsetOfIC, class CKey >
  801. inline void CLRUKResourceUtilityManager< m_Kmax, CResource, OffsetOfIC, CKey >::
  802. BeginResourceScan( CLock* const plock )
  803. {
  804. // move before the first entry in the resource LRUK
  805. m_ResourceLRUK.MoveBeforeFirst( &plock->m_lock );
  806. }
  807. // retrieves the current resource managed by the RUM by ascending utility
  808. // locked by the specified lock context. if there is no current resource,
  809. // errNoCurrentResource is returned
  810. template< int m_Kmax, class CResource, PfnOffsetOf OffsetOfIC, class CKey >
  811. inline CLRUKResourceUtilityManager< m_Kmax, CResource, OffsetOfIC, CKey >::ERR CLRUKResourceUtilityManager< m_Kmax, CResource, OffsetOfIC, CKey >::
  812. ErrGetCurrentResource( CLock* const plock, CResource** const ppres )
  813. {
  814. // we are currently walking the resource LRUK
  815. if ( plock->m_StuckList.FEmpty() )
  816. {
  817. CResourceLRUK::ERR errLRUK;
  818. // get the current resource in the resource LRUK
  819. errLRUK = m_ResourceLRUK.ErrRetrieveEntry( &plock->m_lock, ppres );
  820. // return the status of our currency
  821. return errLRUK == CResourceLRUK::errSuccess ? errSuccess : errNoCurrentResource;
  822. }
  823. // we are currently walking the list of resources that couldn't be
  824. // moved
  825. else
  826. {
  827. // return the status of our currency on the stuck list
  828. *ppres = plock->m_presStuckList;
  829. return *ppres ? errSuccess : errNoCurrentResource;
  830. }
  831. }
  832. // retrieves the next resource managed by the RUM by ascending utility locked
  833. // by the specified lock context. if there are no more resources to be
  834. // scanned, errNoCurrentResource is returned
  835. template< int m_Kmax, class CResource, PfnOffsetOf OffsetOfIC, class CKey >
  836. inline CLRUKResourceUtilityManager< m_Kmax, CResource, OffsetOfIC, CKey >::ERR CLRUKResourceUtilityManager< m_Kmax, CResource, OffsetOfIC, CKey >::
  837. ErrGetNextResource( CLock* const plock, CResource** const ppres )
  838. {
  839. CResourceLRUK::ERR errLRUK;
  840. // keep scanning until we establish currency on a resource or reach the
  841. // end of the resource LRUK
  842. m_cResourceScanned++;
  843. *ppres = NULL;
  844. errLRUK = CResourceLRUK::errSuccess;
  845. do
  846. {
  847. // we are currently walking the resource LRUK
  848. if ( plock->m_StuckList.FEmpty() )
  849. {
  850. // scan forward in the index until we find the least useful
  851. // resource that is in the correct place in the index or we reach
  852. // the end of the index
  853. while ( ( errLRUK = m_ResourceLRUK.ErrMoveNext( &plock->m_lock ) ) == CResourceLRUK::errSuccess )
  854. {
  855. CResourceLRUK::ERR errLRUK2 = CResourceLRUK::errSuccess;
  856. CResource* pres;
  857. errLRUK2 = m_ResourceLRUK.ErrRetrieveEntry( &plock->m_lock, &pres );
  858. RESMGRAssert( errLRUK2 == CResourceLRUK::errSuccess );
  859. CInvasiveContext* const pic = _PicFromPres( pres );
  860. // if our update list contains resources and the tickIndex for
  861. // those resources doesn't match the tickIndex for this
  862. // resource then we need to stop and empty the update list
  863. if ( !plock->m_UpdateList.FEmpty() &&
  864. m_ResourceLRUK.CmpKey( pic->m_tickIndex, plock->m_tickIndexCurrent ) )
  865. {
  866. break;
  867. }
  868. // if the Kth touch time of this resource matches the tickIndex
  869. // of the resource then we can return this resource as the
  870. // current resource because it is in the correct place in the
  871. // index
  872. if ( !m_ResourceLRUK.CmpKey( pic->m_tickIndex, pic->m_rgtick[ m_K - 1 ] ) )
  873. {
  874. *ppres = pres;
  875. break;
  876. }
  877. // if the Kth touch time of this resource doesn't match the
  878. // tickIndex of the resource then add the resource to the
  879. // update list so that we can move it to the correct place
  880. // later. leave space reserved for this resource here so
  881. // that we can always move it back in case of an error
  882. errLRUK2 = m_ResourceLRUK.ErrReserveEntry( &plock->m_lock );
  883. RESMGRAssert( errLRUK2 == CResourceLRUK::errSuccess );
  884. errLRUK2 = m_ResourceLRUK.ErrDeleteEntry( &plock->m_lock );
  885. RESMGRAssert( errLRUK2 == CResourceLRUK::errSuccess );
  886. plock->m_tickIndexCurrent = pic->m_tickIndex;
  887. pic->m_tickIndex = tickNil;
  888. plock->m_UpdateList.InsertAsNextMost( pres );
  889. }
  890. RESMGRAssert( errLRUK == CResourceLRUK::errSuccess ||
  891. errLRUK == CResourceLRUK::errNoCurrentEntry );
  892. // we still do not have a current resource
  893. if ( *ppres == NULL )
  894. {
  895. // release our lock on the resource LRUK
  896. m_ResourceLRUK.UnlockKeyPtr( &plock->m_lock );
  897. // try to update the positions of any resources we have in our
  898. // update list. remove the reservation for any resource we
  899. // can move and put any resource we can't move in the stuck
  900. // list
  901. while ( !plock->m_UpdateList.FEmpty() )
  902. {
  903. CResource* const pres = plock->m_UpdateList.PrevMost();
  904. CInvasiveContext* const pic = _PicFromPres( pres );
  905. plock->m_UpdateList.Remove( pres );
  906. CResourceLRUK::ERR errLRUK2;
  907. do
  908. {
  909. const TICK tickK = pic->m_rgtick[ m_K - 1 ];
  910. m_ResourceLRUK.LockKeyPtr( tickK, pres, &plock->m_lock );
  911. pic->m_tickIndex = tickK;
  912. errLRUK2 = m_ResourceLRUK.ErrInsertEntry( &plock->m_lock, pres, fTrue );
  913. if ( errLRUK2 != CResourceLRUK::errSuccess )
  914. {
  915. pic->m_tickIndex = tickNil;
  916. m_ResourceLRUK.UnlockKeyPtr( &plock->m_lock );
  917. const TICK tickYoungest = m_ResourceLRUK.KeyInsertMost();
  918. m_ResourceLRUK.LockKeyPtr( tickYoungest, pres, &plock->m_lock );
  919. pic->m_tickIndex = tickYoungest;
  920. if ( m_ResourceLRUK.CmpKey( pic->m_tickIndex, plock->m_tickIndexCurrent ) )
  921. {
  922. errLRUK2 = m_ResourceLRUK.ErrInsertEntry( &plock->m_lock, pres, fTrue );
  923. }
  924. else
  925. {
  926. errLRUK2 = CResourceLRUK::errOutOfMemory;
  927. }
  928. if ( errLRUK2 != CResourceLRUK::errSuccess )
  929. {
  930. pic->m_tickIndex = tickNil;
  931. }
  932. }
  933. m_ResourceLRUK.UnlockKeyPtr( &plock->m_lock );
  934. }
  935. while ( errLRUK2 == CResourceLRUK::errKeyRangeExceeded );
  936. if ( errLRUK2 == CResourceLRUK::errSuccess )
  937. {
  938. m_ResourceLRUK.LockKeyPtr( plock->m_tickIndexCurrent, pres, &plock->m_lock );
  939. m_ResourceLRUK.UnreserveEntry( &plock->m_lock );
  940. m_ResourceLRUK.UnlockKeyPtr( &plock->m_lock );
  941. errLRUK = CResourceLRUK::errSuccess;
  942. }
  943. else
  944. {
  945. plock->m_StuckList.InsertAsNextMost( pres );
  946. }
  947. }
  948. // set our currency so that if we have any resources in the
  949. // stuck list then we will walk them as if they are in the
  950. // correct place in the resource LRUK
  951. if ( plock->m_StuckList.FEmpty() )
  952. {
  953. m_ResourceLRUK.MoveAfterKeyPtr( plock->m_tickIndexCurrent, (CResource*)~NULL, &plock->m_lock );
  954. }
  955. else
  956. {
  957. plock->m_presStuckList = NULL;
  958. plock->m_presStuckListNext = plock->m_StuckList.PrevMost();
  959. }
  960. }
  961. }
  962. // we are currently walking the list of resources that couldn't be
  963. // moved
  964. if ( !plock->m_StuckList.FEmpty() )
  965. {
  966. // move to the next resource in the list
  967. plock->m_presStuckList = ( plock->m_presStuckList ?
  968. plock->m_StuckList.Next( plock->m_presStuckList ) :
  969. plock->m_presStuckListNext );
  970. plock->m_presStuckListNext = NULL;
  971. // we have a current resource
  972. if ( plock->m_presStuckList )
  973. {
  974. // return the current resource
  975. m_cResourceScannedOutOfOrder++;
  976. *ppres = plock->m_presStuckList;
  977. }
  978. // we still do not have a current resource
  979. else
  980. {
  981. // we have walked off of the end of the list of resources that
  982. // couldn't be moved so we need to put them back into their
  983. // source buckets before we move on to the next entry in the
  984. // resource LRUK. we are guaranteed to be able to put them
  985. // back because we reserved space for them
  986. while ( !plock->m_StuckList.FEmpty() )
  987. {
  988. CResource* pres = plock->m_StuckList.PrevMost();
  989. CResourceLRUK::ERR errLRUK2 = CResourceLRUK::errSuccess;
  990. plock->m_StuckList.Remove( pres );
  991. m_ResourceLRUK.LockKeyPtr( plock->m_tickIndexCurrent, pres, &plock->m_lock );
  992. m_ResourceLRUK.UnreserveEntry( &plock->m_lock );
  993. _PicFromPres( pres )->m_tickIndex = plock->m_tickIndexCurrent;
  994. errLRUK2 = m_ResourceLRUK.ErrInsertEntry( &plock->m_lock, pres, fTrue );
  995. RESMGRAssert( errLRUK2 == CResourceLRUK::errSuccess );
  996. m_ResourceLRUK.UnlockKeyPtr( &plock->m_lock );
  997. }
  998. // we are no longer walking the stuck list so restore our
  999. // currency on the resource LRUK
  1000. m_ResourceLRUK.MoveAfterKeyPtr( plock->m_tickIndexCurrent, (CResource*)~NULL, &plock->m_lock );
  1001. }
  1002. }
  1003. }
  1004. while ( *ppres == NULL && errLRUK != CResourceLRUK::errNoCurrentEntry );
  1005. // return the state of our currency
  1006. return *ppres ? errSuccess : errNoCurrentResource;
  1007. }
  1008. // stops management of the current resource optionally saving any current
  1009. // knowledge the RUM has about this resource. if there is no current
  1010. // resource, errNoCurrentResource will be returned
  1011. template< int m_Kmax, class CResource, PfnOffsetOf OffsetOfIC, class CKey >
  1012. inline CLRUKResourceUtilityManager< m_Kmax, CResource, OffsetOfIC, CKey >::ERR CLRUKResourceUtilityManager< m_Kmax, CResource, OffsetOfIC, CKey >::
  1013. ErrEvictCurrentResource( CLock* const plock, const CKey& key, const BOOL fKeepHistory )
  1014. {
  1015. ERR err;
  1016. CResourceLRUK::ERR errLRUK;
  1017. CResource* pres;
  1018. // get the current resource in the resource LRUK, if any
  1019. if ( ( err = ErrGetCurrentResource( plock, &pres ) ) != errSuccess )
  1020. {
  1021. return err;
  1022. }
  1023. // we are currently walking the resource LRUK
  1024. if ( plock->m_StuckList.FEmpty() )
  1025. {
  1026. // delete the current resource from the resource LRUK
  1027. errLRUK = m_ResourceLRUK.ErrDeleteEntry( &plock->m_lock );
  1028. RESMGRAssert( errLRUK == CResourceLRUK::errSuccess );
  1029. }
  1030. // we are currently walking the list of resources that couldn't be
  1031. // moved
  1032. else
  1033. {
  1034. // delete the current resource from the stuck list and remove its
  1035. // reservation from the resource LRUK
  1036. plock->m_presStuckListNext = plock->m_StuckList.Next( pres );
  1037. plock->m_StuckList.Remove( pres );
  1038. plock->m_presStuckList = NULL;
  1039. m_ResourceLRUK.LockKeyPtr( plock->m_tickIndexCurrent, pres, &plock->m_lock );
  1040. m_ResourceLRUK.UnreserveEntry( &plock->m_lock );
  1041. m_ResourceLRUK.UnlockKeyPtr( &plock->m_lock );
  1042. // if we are no longer walking the stuck list then restore our currency
  1043. // on the resource LRUK
  1044. if ( plock->m_StuckList.FEmpty() )
  1045. {
  1046. m_ResourceLRUK.MoveAfterKeyPtr( plock->m_tickIndexCurrent, (CResource*)~NULL, &plock->m_lock );
  1047. }
  1048. }
  1049. // we are to keep history for this resource
  1050. if ( fKeepHistory )
  1051. {
  1052. // store the history for this resource
  1053. _StoreHistory( key, _PicFromPres( pres ) );
  1054. }
  1055. return errSuccess;
  1056. }
  1057. // ends the scan of all resources managed by the RUM by ascending utility
  1058. // associated with the specified lock context and releases all locks held
  1059. template< int m_Kmax, class CResource, PfnOffsetOf OffsetOfIC, class CKey >
  1060. inline void CLRUKResourceUtilityManager< m_Kmax, CResource, OffsetOfIC, CKey >::
  1061. EndResourceScan( CLock* const plock )
  1062. {
  1063. // we are currently walking the resource LRUK
  1064. if ( plock->m_StuckList.FEmpty() )
  1065. {
  1066. // release our lock on the resource LRUK
  1067. m_ResourceLRUK.UnlockKeyPtr( &plock->m_lock );
  1068. // try to update the positions of any resources we have in our
  1069. // update list. remove the reservation for any resource we
  1070. // can move and put any resource we can't move in the stuck
  1071. // list
  1072. while ( !plock->m_UpdateList.FEmpty() )
  1073. {
  1074. CResource* const pres = plock->m_UpdateList.PrevMost();
  1075. CInvasiveContext* const pic = _PicFromPres( pres );
  1076. plock->m_UpdateList.Remove( pres );
  1077. CResourceLRUK::ERR errLRUK;
  1078. do
  1079. {
  1080. const TICK tickK = pic->m_rgtick[ m_K - 1 ];
  1081. m_ResourceLRUK.LockKeyPtr( tickK, pres, &plock->m_lock );
  1082. pic->m_tickIndex = tickK;
  1083. errLRUK = m_ResourceLRUK.ErrInsertEntry( &plock->m_lock, pres, fTrue );
  1084. if ( errLRUK != CResourceLRUK::errSuccess )
  1085. {
  1086. pic->m_tickIndex = tickNil;
  1087. m_ResourceLRUK.UnlockKeyPtr( &plock->m_lock );
  1088. const TICK tickYoungest = m_ResourceLRUK.KeyInsertMost();
  1089. m_ResourceLRUK.LockKeyPtr( tickYoungest, pres, &plock->m_lock );
  1090. pic->m_tickIndex = tickYoungest;
  1091. errLRUK = m_ResourceLRUK.ErrInsertEntry( &plock->m_lock, pres, fTrue );
  1092. if ( errLRUK != CResourceLRUK::errSuccess )
  1093. {
  1094. pic->m_tickIndex = tickNil;
  1095. }
  1096. }
  1097. m_ResourceLRUK.UnlockKeyPtr( &plock->m_lock );
  1098. }
  1099. while ( errLRUK == CResourceLRUK::errKeyRangeExceeded );
  1100. if ( errLRUK == CResourceLRUK::errSuccess )
  1101. {
  1102. m_ResourceLRUK.LockKeyPtr( plock->m_tickIndexCurrent, pres, &plock->m_lock );
  1103. m_ResourceLRUK.UnreserveEntry( &plock->m_lock );
  1104. m_ResourceLRUK.UnlockKeyPtr( &plock->m_lock );
  1105. }
  1106. else
  1107. {
  1108. plock->m_StuckList.InsertAsNextMost( pres );
  1109. }
  1110. }
  1111. }
  1112. // put all the resources we were not able to move back into their
  1113. // source buckets. we know that we will succeed because we reserved
  1114. // space for them
  1115. while ( !plock->m_StuckList.FEmpty() )
  1116. {
  1117. CResource* pres = plock->m_StuckList.PrevMost();
  1118. CResourceLRUK::ERR errLRUK = CResourceLRUK::errSuccess;
  1119. plock->m_StuckList.Remove( pres );
  1120. m_ResourceLRUK.LockKeyPtr( plock->m_tickIndexCurrent, pres, &plock->m_lock );
  1121. m_ResourceLRUK.UnreserveEntry( &plock->m_lock );
  1122. _PicFromPres( pres )->m_tickIndex = plock->m_tickIndexCurrent;
  1123. errLRUK = m_ResourceLRUK.ErrInsertEntry( &plock->m_lock, pres, fTrue );
  1124. RESMGRAssert( errLRUK == CResourceLRUK::errSuccess );
  1125. m_ResourceLRUK.UnlockKeyPtr( &plock->m_lock );
  1126. }
  1127. }
  1128. // returns the current number of resource history records retained for
  1129. // supporting the LRUK replacement policy
  1130. template< int m_Kmax, class CResource, PfnOffsetOf OffsetOfIC, class CKey >
  1131. inline DWORD CLRUKResourceUtilityManager< m_Kmax, CResource, OffsetOfIC, CKey >::
  1132. CHistoryRecord()
  1133. {
  1134. return m_cHistoryRecord;
  1135. }
  1136. // returns the cumulative number of successful resource history record loads
  1137. // when caching a resource
  1138. template< int m_Kmax, class CResource, PfnOffsetOf OffsetOfIC, class CKey >
  1139. inline DWORD CLRUKResourceUtilityManager< m_Kmax, CResource, OffsetOfIC, CKey >::
  1140. CHistoryHit()
  1141. {
  1142. return m_cHistoryHit;
  1143. }
  1144. // returns the cumulative number of attempted resource history record loads
  1145. // when caching a resource
  1146. template< int m_Kmax, class CResource, PfnOffsetOf OffsetOfIC, class CKey >
  1147. inline DWORD CLRUKResourceUtilityManager< m_Kmax, CResource, OffsetOfIC, CKey >::
  1148. CHistoryRequest()
  1149. {
  1150. return m_cHistoryReq;
  1151. }
  1152. // returns the cumulative number of resources scanned in the LRUK
  1153. template< int m_Kmax, class CResource, PfnOffsetOf OffsetOfIC, class CKey >
  1154. inline DWORD CLRUKResourceUtilityManager< m_Kmax, CResource, OffsetOfIC, CKey >::
  1155. CResourceScanned()
  1156. {
  1157. return m_cResourceScanned;
  1158. }
  1159. // returns the cumulative number of resources scanned in the LRUK that were
  1160. // returned out of order
  1161. template< int m_Kmax, class CResource, PfnOffsetOf OffsetOfIC, class CKey >
  1162. inline DWORD CLRUKResourceUtilityManager< m_Kmax, CResource, OffsetOfIC, CKey >::
  1163. CResourceScannedOutOfOrder()
  1164. {
  1165. return m_cResourceScannedOutOfOrder;
  1166. }
  1167. // updates the touch times of a given resource
  1168. template< int m_Kmax, class CResource, PfnOffsetOf OffsetOfIC, class CKey >
  1169. inline void CLRUKResourceUtilityManager< m_Kmax, CResource, OffsetOfIC, CKey >::
  1170. _TouchResource( CInvasiveContext* const pic, const TICK tickNow, const TICK tickLastBIExpected )
  1171. {
  1172. RESMGRAssert( tickLastBIExpected != tickLastBIExpected ||
  1173. _CmpTick( tickLastBIExpected, tickNow ) < 0 );
  1174. // if the current time and the last historical touch time are closer than
  1175. // the correlation interval then this is a correlated touch
  1176. if ( tickNow - pic->m_rgtick[ 1 - 1 ] <= m_ctickCorrelatedTouch )
  1177. {
  1178. // update the last tick time to the current time iff the last touch time
  1179. // has not changed and is not tickNil
  1180. AtomicCompareExchange( (long*)&pic->m_tickLast, tickLastBIExpected, tickNow );
  1181. }
  1182. // if the current time and the last historical touch time are not closer
  1183. // than the correlation interval then this is not a correlated touch
  1184. else
  1185. {
  1186. // to determine who will get to actually touch the resource, we will do an
  1187. // AtomicCompareExchange on the last touch time with tickNil. the thread
  1188. // whose transaction succeeds wins the race and will touch the resource.
  1189. // all other threads will leave. this is certainly acceptable as touches
  1190. // that collide are obviously too close together to be of any interest
  1191. const TICK tickLastBI = TICK( AtomicCompareExchange( (long*)&pic->m_tickLast, tickLastBIExpected, tickNil ) );
  1192. if ( tickLastBI == tickLastBIExpected )
  1193. {
  1194. // compute the correlation period of the last series of correlated
  1195. // touches by taking the different between the last touch time and
  1196. // the most recent touch time in our history
  1197. RESMGRAssert( _CmpTick( tickLastBI, pic->m_rgtick[ 1 - 1 ] ) >= 0 );
  1198. const TICK dtickCorrelationPeriod = tickLastBI - pic->m_rgtick[ 1 - 1 ];
  1199. // move all touch times up one slot and advance them by the correlation
  1200. // period and the timeout. this will collapse all correlated touches
  1201. // to a time interval of zero and will make resources that have more
  1202. // uncorrelated touches appear younger in the index
  1203. for ( int K = m_Kmax; K >= 2; K-- )
  1204. {
  1205. pic->m_rgtick[ K - 1 ] = pic->m_rgtick[ ( K - 1 ) - 1 ] +
  1206. dtickCorrelationPeriod +
  1207. m_ctickTimeout;
  1208. }
  1209. // set the most recent historical touch time to the current time
  1210. pic->m_rgtick[ 1 - 1 ] = tickNow;
  1211. // set the last touch time to the current time via AtomicExchange,
  1212. // unlocking this resource to touches by other threads
  1213. AtomicExchange( (long*)&pic->m_tickLast, tickNow );
  1214. }
  1215. }
  1216. }
  1217. // restores the touch history for the specified resource if known
  1218. template< int m_Kmax, class CResource, PfnOffsetOf OffsetOfIC, class CKey >
  1219. inline void CLRUKResourceUtilityManager< m_Kmax, CResource, OffsetOfIC, CKey >::
  1220. _RestoreHistory( const CKey& key, CInvasiveContext* const pic, const TICK tickNow )
  1221. {
  1222. CHistoryTable::CLock lockHist;
  1223. CHistoryEntry he;
  1224. // this resource has history
  1225. m_cHistoryReq++;
  1226. m_KeyHistory.ReadLockKey( key, &lockHist );
  1227. if ( m_KeyHistory.ErrRetrieveEntry( &lockHist, &he ) == CHistoryTable::errSuccess )
  1228. {
  1229. // determine the range of Kth touch times over which retained history
  1230. // is still valid
  1231. const TICK tickKHistoryMax = tickNow + ( m_K - 1 ) * m_ctickTimeout;
  1232. const TICK tickKHistoryMin = tickNow - m_ctickTimeout;
  1233. // the history is still valid
  1234. if ( m_HistoryLRUK.CmpKey( tickKHistoryMin, he.m_phist->m_rgtick[ m_K - 1 ] ) <= 0 &&
  1235. m_HistoryLRUK.CmpKey( he.m_phist->m_rgtick[ m_K - 1 ], tickKHistoryMax ) < 0 )
  1236. {
  1237. // restore the history for this resource
  1238. m_cHistoryHit++;
  1239. pic->m_tickIndex = tickNil;
  1240. for ( int K = 1; K <= m_Kmax; K++ )
  1241. {
  1242. pic->m_rgtick[ K - 1 ] = he.m_phist->m_rgtick[ K - 1 ];
  1243. }
  1244. pic->m_tickLast = he.m_phist->m_rgtick[ 1 - 1 ];
  1245. // touch this resource
  1246. TouchResource( _PresFromPic( pic ), tickNow );
  1247. }
  1248. }
  1249. m_KeyHistory.ReadUnlockKey( &lockHist );
  1250. }
  1251. // stores the touch history for a resource
  1252. template< int m_Kmax, class CResource, PfnOffsetOf OffsetOfIC, class CKey >
  1253. inline void CLRUKResourceUtilityManager< m_Kmax, CResource, OffsetOfIC, CKey >::
  1254. _StoreHistory( const CKey& key, CInvasiveContext* const pic )
  1255. {
  1256. CHistoryLRUK::ERR errHLRUK = CHistoryLRUK::errSuccess;
  1257. CHistory* phist = NULL;
  1258. // keep trying to store our history until we either succeed or fail with
  1259. // out of memory. commonly, this will succeed immediately but in rare cases
  1260. // we have to loop to resolve an errKeyRangeExceeded in the history LRUK
  1261. do
  1262. {
  1263. // determine the range of Kth touch times over which retained history
  1264. // is still valid
  1265. const TICK tickNow = _TickCurrentTime();
  1266. const TICK tickKHistoryMax = tickNow + ( m_K - 1 ) * m_ctickTimeout;
  1267. const TICK tickKHistoryMin = tickNow - m_ctickTimeout;
  1268. // only store the history for this resource if it is not too young or too
  1269. // old. history records that are too young or too old will not help us
  1270. // anyway
  1271. //
  1272. // NOTE: the only time we will ever see a history record that is too young
  1273. // is when a resource is evicted that hasn't been touched for so long that
  1274. // we have wrapped-around
  1275. if ( m_HistoryLRUK.CmpKey( tickKHistoryMin, pic->m_rgtick[ m_K - 1 ] ) <= 0 &&
  1276. m_HistoryLRUK.CmpKey( pic->m_rgtick[ m_K - 1 ], tickKHistoryMax ) < 0 )
  1277. {
  1278. // we allocated a history record
  1279. if ( phist = _PhistAllocHistory() )
  1280. {
  1281. // save the history for this resource
  1282. phist->m_key = key;
  1283. for ( int K = 1; K <= m_Kmax; K++ )
  1284. {
  1285. phist->m_rgtick[ K - 1 ] = pic->m_rgtick[ K - 1 ];
  1286. }
  1287. // we failed to insert the history in the history table
  1288. if ( ( errHLRUK = _ErrInsertHistory( phist ) ) != CHistoryLRUK::errSuccess )
  1289. {
  1290. // free the history record
  1291. delete phist;
  1292. }
  1293. }
  1294. // we failed to allocate a history record
  1295. else
  1296. {
  1297. // set an out of memory condition
  1298. errHLRUK = CHistoryLRUK::errOutOfMemory;
  1299. }
  1300. }
  1301. // we have decided that the history for this resource is not useful
  1302. else
  1303. {
  1304. // claim success but do not store the history
  1305. errHLRUK = CHistoryLRUK::errSuccess;
  1306. }
  1307. }
  1308. while ( errHLRUK == CHistoryLRUK::errKeyRangeExceeded );
  1309. }
  1310. // allocates a new history record to store the touch history for a resource.
  1311. // this history record can either be newly allocated or recycled from the
  1312. // history table
  1313. template< int m_Kmax, class CResource, PfnOffsetOf OffsetOfIC, class CKey >
  1314. inline CLRUKResourceUtilityManager< m_Kmax, CResource, OffsetOfIC, CKey >::CHistory* CLRUKResourceUtilityManager< m_Kmax, CResource, OffsetOfIC, CKey >::
  1315. _PhistAllocHistory()
  1316. {
  1317. CHistoryLRUK::ERR errHLRUK;
  1318. CHistoryLRUK::CLock lockHLRUK;
  1319. CHistory* phist;
  1320. CHistoryTable::ERR errHist;
  1321. CHistoryTable::CLock lockHist;
  1322. // determine the range of Kth touch times over which retained history is
  1323. // still valid
  1324. const TICK tickNow = _TickCurrentTime();
  1325. const TICK tickKHistoryMax = tickNow + ( m_K - 1 ) * m_ctickTimeout;
  1326. const TICK tickKHistoryMin = tickNow - m_ctickTimeout;
  1327. // get the first history in the history LRUK, if any
  1328. m_HistoryLRUK.MoveBeforeFirst( &lockHLRUK );
  1329. if ( ( errHLRUK = m_HistoryLRUK.ErrMoveNext( &lockHLRUK ) ) == CHistoryLRUK::errSuccess )
  1330. {
  1331. // if this history is no longer valid, recycle it
  1332. errHLRUK = m_HistoryLRUK.ErrRetrieveEntry( &lockHLRUK, &phist );
  1333. RESMGRAssert( errHLRUK == CHistoryLRUK::errSuccess );
  1334. if ( m_HistoryLRUK.CmpKey( phist->m_rgtick[ m_K - 1 ], tickKHistoryMin ) < 0 ||
  1335. m_HistoryLRUK.CmpKey( tickKHistoryMax, phist->m_rgtick[ m_K - 1 ] ) <= 0 )
  1336. {
  1337. // remove this history from the history LRUK
  1338. errHLRUK = m_HistoryLRUK.ErrDeleteEntry( &lockHLRUK );
  1339. RESMGRAssert( errHLRUK == CHistoryLRUK::errSuccess );
  1340. AtomicDecrement( (long*)&m_cHistoryRecord );
  1341. m_HistoryLRUK.UnlockKeyPtr( &lockHLRUK );
  1342. // try to remove this history from the history table, but don't
  1343. // worry about it if it is not there
  1344. //
  1345. // NOTE: the history record must exist until we unlock its key
  1346. // in the history table after we delete its entry so that some
  1347. // other thread can't touch a deleted history record
  1348. m_KeyHistory.WriteLockKey( phist->m_key, &lockHist );
  1349. errHist = m_KeyHistory.ErrDeleteEntry( &lockHist );
  1350. RESMGRAssert( errHist == CHistoryTable::errSuccess ||
  1351. errHist == CHistoryTable::errNoCurrentEntry );
  1352. m_KeyHistory.WriteUnlockKey( &lockHist );
  1353. // return this history
  1354. return phist;
  1355. }
  1356. }
  1357. RESMGRAssert( errHLRUK == CHistoryLRUK::errSuccess ||
  1358. errHLRUK == CHistoryLRUK::errNoCurrentEntry );
  1359. m_HistoryLRUK.UnlockKeyPtr( &lockHLRUK );
  1360. // get the last history in the history LRUK, if any
  1361. m_HistoryLRUK.MoveAfterLast( &lockHLRUK );
  1362. if ( ( errHLRUK = m_HistoryLRUK.ErrMovePrev( &lockHLRUK ) ) == CHistoryLRUK::errSuccess )
  1363. {
  1364. // if this history is no longer valid, recycle it
  1365. errHLRUK = m_HistoryLRUK.ErrRetrieveEntry( &lockHLRUK, &phist );
  1366. RESMGRAssert( errHLRUK == CHistoryLRUK::errSuccess );
  1367. if ( m_HistoryLRUK.CmpKey( phist->m_rgtick[ m_K - 1 ], tickKHistoryMin ) < 0 ||
  1368. m_HistoryLRUK.CmpKey( tickKHistoryMax, phist->m_rgtick[ m_K - 1 ] ) <= 0 )
  1369. {
  1370. // remove this history from the history LRUK
  1371. errHLRUK = m_HistoryLRUK.ErrDeleteEntry( &lockHLRUK );
  1372. RESMGRAssert( errHLRUK == CHistoryLRUK::errSuccess );
  1373. AtomicDecrement( (long*)&m_cHistoryRecord );
  1374. m_HistoryLRUK.UnlockKeyPtr( &lockHLRUK );
  1375. // try to remove this history from the history table, but don't
  1376. // worry about it if it is not there
  1377. //
  1378. // NOTE: the history record must exist until we unlock its key
  1379. // in the history table after we delete its entry so that some
  1380. // other thread can't touch a deleted history record
  1381. m_KeyHistory.WriteLockKey( phist->m_key, &lockHist );
  1382. errHist = m_KeyHistory.ErrDeleteEntry( &lockHist );
  1383. RESMGRAssert( errHist == CHistoryTable::errSuccess ||
  1384. errHist == CHistoryTable::errNoCurrentEntry );
  1385. m_KeyHistory.WriteUnlockKey( &lockHist );
  1386. // return this history
  1387. return phist;
  1388. }
  1389. }
  1390. RESMGRAssert( errHLRUK == CHistoryLRUK::errSuccess ||
  1391. errHLRUK == CHistoryLRUK::errNoCurrentEntry );
  1392. m_HistoryLRUK.UnlockKeyPtr( &lockHLRUK );
  1393. // allocate and return a new history because one couldn't be recycled
  1394. return new CHistory;
  1395. }
  1396. // inserts the history record containing the touch history for a resource into
  1397. // the history table
  1398. template< int m_Kmax, class CResource, PfnOffsetOf OffsetOfIC, class CKey >
  1399. inline CLRUKResourceUtilityManager< m_Kmax, CResource, OffsetOfIC, CKey >::CHistoryLRUK::ERR CLRUKResourceUtilityManager< m_Kmax, CResource, OffsetOfIC, CKey >::
  1400. _ErrInsertHistory( CHistory* const phist )
  1401. {
  1402. CHistoryEntry he;
  1403. CHistoryTable::ERR errHist;
  1404. CHistoryTable::CLock lockHist;
  1405. CHistoryLRUK::ERR errHLRUK;
  1406. CHistoryLRUK::CLock lockHLRUK;
  1407. // try to insert our history into the history table
  1408. he.m_KeySignature = CHistoryTable::CKeyEntry::Hash( phist->m_key );
  1409. he.m_phist = phist;
  1410. m_KeyHistory.WriteLockKey( phist->m_key, &lockHist );
  1411. if ( ( errHist = m_KeyHistory.ErrInsertEntry( &lockHist, he ) ) != CHistoryTable::errSuccess )
  1412. {
  1413. // there is already an entry in the history table for this resource
  1414. if ( errHist == CHistoryTable::errKeyDuplicate )
  1415. {
  1416. // replace this entry with our entry. note that this will make the
  1417. // old history that this entry pointed to invisible to the history
  1418. // table. this is OK because we will eventually clean it up via the
  1419. // history LRUK
  1420. errHist = m_KeyHistory.ErrReplaceEntry( &lockHist, he );
  1421. RESMGRAssert( errHist == CHistoryTable::errSuccess );
  1422. }
  1423. // some other error occurred while inserting this entry in the history table
  1424. else
  1425. {
  1426. RESMGRAssert( errHist == CHistoryTable::errOutOfMemory );
  1427. m_KeyHistory.WriteUnlockKey( &lockHist );
  1428. return CHistoryLRUK::errOutOfMemory;
  1429. }
  1430. }
  1431. m_KeyHistory.WriteUnlockKey( &lockHist );
  1432. // try to insert our history into the history LRUK
  1433. m_HistoryLRUK.LockKeyPtr( phist->m_rgtick[ m_K - 1 ], phist, &lockHLRUK );
  1434. if ( ( errHLRUK = m_HistoryLRUK.ErrInsertEntry( &lockHLRUK, phist ) ) != CHistoryLRUK::errSuccess )
  1435. {
  1436. RESMGRAssert( errHLRUK == CHistoryLRUK::errOutOfMemory ||
  1437. errHLRUK == CHistoryLRUK::errKeyRangeExceeded );
  1438. // we failed to insert our history into the history LRUK
  1439. m_HistoryLRUK.UnlockKeyPtr( &lockHLRUK );
  1440. m_KeyHistory.WriteLockKey( phist->m_key, &lockHist );
  1441. errHist = m_KeyHistory.ErrDeleteEntry( &lockHist );
  1442. RESMGRAssert( errHist == CHistoryTable::errSuccess );
  1443. m_KeyHistory.WriteUnlockKey( &lockHist );
  1444. return errHLRUK;
  1445. }
  1446. m_HistoryLRUK.UnlockKeyPtr( &lockHLRUK );
  1447. // we have successfully stored our history
  1448. AtomicIncrement( (long*)&m_cHistoryRecord );
  1449. return CHistoryLRUK::errSuccess;
  1450. }
  1451. // converts a pointer to the invasive context to a pointer to a resource
  1452. template< int m_Kmax, class CResource, PfnOffsetOf OffsetOfIC, class CKey >
  1453. inline CResource* CLRUKResourceUtilityManager< m_Kmax, CResource, OffsetOfIC, CKey >::
  1454. _PresFromPic( CInvasiveContext* const pic ) const
  1455. {
  1456. return (CResource*)( (BYTE*)pic - OffsetOfIC() );
  1457. }
  1458. // converts a pointer to a resource to a pointer to the invasive context
  1459. template< int m_Kmax, class CResource, PfnOffsetOf OffsetOfIC, class CKey >
  1460. inline CLRUKResourceUtilityManager< m_Kmax, CResource, OffsetOfIC, CKey >::CInvasiveContext* CLRUKResourceUtilityManager< m_Kmax, CResource, OffsetOfIC, CKey >::
  1461. _PicFromPres( CResource* const pres ) const
  1462. {
  1463. return (CInvasiveContext*)( (BYTE*)pres + OffsetOfIC() );
  1464. }
  1465. // returns the current tick count in msec resolution updated every 2 msec with
  1466. // the special lock value 0xFFFFFFFF reserved
  1467. template< int m_Kmax, class CResource, PfnOffsetOf OffsetOfIC, class CKey >
  1468. inline CLRUKResourceUtilityManager< m_Kmax, CResource, OffsetOfIC, CKey >::TICK CLRUKResourceUtilityManager< m_Kmax, CResource, OffsetOfIC, CKey >::
  1469. _TickCurrentTime()
  1470. {
  1471. return TICK( DWORD( DwOSTimeGetTickCount() ) & tickMask );
  1472. }
  1473. // performs a wrap-around insensitive comparison of two TICKs
  1474. template< int m_Kmax, class CResource, PfnOffsetOf OffsetOfIC, class CKey >
  1475. inline long CLRUKResourceUtilityManager< m_Kmax, CResource, OffsetOfIC, CKey >::
  1476. _CmpTick( const TICK tick1, const TICK tick2 ) const
  1477. {
  1478. return long( tick1 - tick2 );
  1479. }
  1480. #define DECLARE_LRUK_RESOURCE_UTILITY_MANAGER( m_Kmax, CResource, OffsetOfIC, CKey, Typedef ) \
  1481. \
  1482. typedef CLRUKResourceUtilityManager< m_Kmax, CResource, OffsetOfIC, CKey > Typedef; \
  1483. \
  1484. DECLARE_APPROXIMATE_INDEX( Typedef::TICK, CResource, Typedef::CInvasiveContext::OffsetOfAIIC, Typedef##__m_aiResourceLRUK ); \
  1485. \
  1486. DECLARE_APPROXIMATE_INDEX( Typedef::TICK, Typedef::CHistory, Typedef::CHistory::OffsetOfAIIC, Typedef##__m_aiHistoryLRU ); \
  1487. \
  1488. inline ULONG_PTR Typedef::CHistoryTable::CKeyEntry:: \
  1489. Hash() const \
  1490. { \
  1491. return ULONG_PTR( m_entry.m_KeySignature ); \
  1492. } \
  1493. \
  1494. inline BOOL Typedef::CHistoryTable::CKeyEntry:: \
  1495. FEntryMatchesKey( const CKey& key ) const \
  1496. { \
  1497. return Hash() == Hash( key ) && m_entry.m_phist->m_key == key; \
  1498. } \
  1499. \
  1500. inline void Typedef::CHistoryTable::CKeyEntry:: \
  1501. SetEntry( const Typedef::CHistoryEntry& he ) \
  1502. { \
  1503. m_entry = he; \
  1504. } \
  1505. \
  1506. inline void Typedef::CHistoryTable::CKeyEntry:: \
  1507. GetEntry( Typedef::CHistoryEntry* const phe ) const \
  1508. { \
  1509. *phe = m_entry; \
  1510. }
  1511. }; // namespace RESMGR
  1512. using namespace RESMGR;
  1513. #endif // _RESMGR_HXX_INCLUDED