Leaked source code of windows server 2003
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.

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