#ifndef _RESMGR_HXX_INCLUDED
#define _RESMGR_HXX_INCLUDED


//  asserts
//
//  #define RESMGRAssert to point to your favorite assert function per #include

#ifdef RESMGRAssert
#else  //  !RESMGRAssert
#define RESMGRAssert Assert
#endif  //  RESMGRAssert

#ifdef COLLAssert
#else  //  !COLLAssert
#define COLLAssert RESMGRAssert
#endif  //  COLLAssert


#include "collection.hxx"

#include <math.h>


namespace RESMGR {


//////////////////////////////////////////////////////////////////////////////////////////
//  CDBAResourceAllocationManager
//
//  Implements a class used to manage how much memory a resource pool should use as a
//  function of the real-time usage characteristics of that pool and the entire system.
//  Naturally, the managed resource would have to be of a type where retention in memory
//  is optional, at least to some extent.
//
//  The resource pool's size is managed by the Dynamic Buffer Allocation (DBA) algorithm.
//  DBA was originally invented by Andrew E. Goodsell in 1997 to manage the size of the
//  multiple database page caches in Exchange Server 5.5.  DBA is not patented and is a
//  trade secret of Microsoft Corporation.
//  
//  The class is written to be platform independent.  As such, there are several pure
//  virtual functions that must be implemented to provide the data about the OS that the
//  resource manager needs to make decisions.  To provide these functions, derive the
//  class for the desired platform and then define these functions:
//
//    TotalPhysicalMemoryPages()
//
//        Returns the total number of pageable physical memory pages in the system.
//
//    AvailablePhysicalMemoryPages()
//
//        Returns the number of available pageable physical memory pages in the system.
//
//    PhysicalMemoryPageSize()
//
//        Returns the size of a single physical memory page.
//
//    TotalPhysicalMemoryPageEvictions()
//
//        Returns the total number of physical page evictions that have occurred.
//
//  There are also several pure virtual functions that must be implemented to provide
//  resource specific data.  To provide these functions, derive the class for each
//  resource to manage and then define these functions:
//
//    TotalResources()
//
//        Returns the total number of resources in the resource pool.
//
//    ResourceSize()
//
//        Returns the size of a single resource.
//
//    TotalResourceEvictions()
//
//        Returns the total number of resource evictions that have occurred.
//
//  There is one final pure virtual function that must be implemented to provide the
//  resource pool with the size recommendation issued by the resource manager:
//
//    SetOptimalResourcePoolSize( size_t cResource )
//
//        Delivers the recommendation of the resource manager for the total number of
//        resources that should be in the resource pool at this time.  Note that this
//        size is merely a suggestion that can be followed to maximize overall system
//        performance.  It is not mandatory to follow this suggestion exactly or
//        instantaneously.
//
//  This recommendation can be queried at any time via OptimalResourcePoolSize().
//
//  Finally, UpdateStatistics() must be called once per second to sample this data and
//  and to update the current resource pool size recommendation.  The statistics need to
//  be reset before use via ResetStatistics().  This function can be called at any time
//  to reset the manager's statistics for whatever reason.

class CDBAResourceAllocationManager
	{
	public:

		CDBAResourceAllocationManager();
		virtual ~CDBAResourceAllocationManager();

		virtual void	ResetStatistics();
		virtual void	UpdateStatistics();
		
		virtual size_t	OptimalResourcePoolSize();

	protected:

		virtual size_t	TotalPhysicalMemoryPages()						= 0;
		virtual size_t	AvailablePhysicalMemoryPages()					= 0;
		virtual size_t	PhysicalMemoryPageSize()						= 0;
		virtual long	TotalPhysicalMemoryPageEvictions()				= 0;

		virtual size_t	TotalResources()								= 0;
		virtual size_t	ResourceSize()									= 0;
		virtual long	TotalResourceEvictions()						= 0;

		virtual void	SetOptimalResourcePoolSize( size_t cResource )	= 0;

	private:

		enum { m_cRollingAvgDepth	= 3 };

		size_t	m_cTotalPage[ m_cRollingAvgDepth ];
		int		m_icTotalPageOldest;
		double	m_cTotalPageSum;
		double	m_cTotalPageAvg;

		size_t	m_cAvailPage[ m_cRollingAvgDepth ];
		int		m_icAvailPageOldest;
		double	m_cAvailPageSum;
		double	m_cAvailPageAvg;

		size_t	m_cbPage[ m_cRollingAvgDepth ];
		int		m_icbPageOldest;
		double	m_cbPageSum;
		double	m_cbPageAvg;
		
		long	m_cPageEvict2;
		long	m_cPageEvict1;
		long	m_dcPageEvict[ m_cRollingAvgDepth ];
		int		m_idcPageEvictOldest;
		double	m_dcPageEvictSum;
		double	m_dcPageEvictAvg;
		
		size_t	m_cbResource[ m_cRollingAvgDepth ];
		int		m_icbResourceOldest;
		double	m_cbResourceSum;
		double	m_cbResourceAvg;
		
		size_t	m_cTotalResource[ m_cRollingAvgDepth ];
		int		m_icTotalResourceOldest;
		double	m_cTotalResourceSum;
		double	m_cTotalResourceAvg;
		
		long	m_cResourceEvict2;
		long	m_cResourceEvict1;
		long	m_dcResourceEvict[ m_cRollingAvgDepth ];
		int		m_idcResourceEvictOldest;
		double	m_dcResourceEvictSum;
		double	m_dcResourceEvictAvg;

		size_t	m_cResourceMin;
		size_t	m_cResourceMax;
	};

//  ctor

inline CDBAResourceAllocationManager::CDBAResourceAllocationManager()
	{
	//  nothing to do
	}

//  virtual dtor

inline CDBAResourceAllocationManager::~CDBAResourceAllocationManager()
	{
	//  nothing to do
	}

//  resets the observed statistics for the resource pool and the system such
//  that the current state appears to have been the steady state for as long as
//  past behavioral data is retained

inline void CDBAResourceAllocationManager::ResetStatistics()
	{
	//  load all statistics

	size_t cTotalPageInit = TotalPhysicalMemoryPages();
	for ( m_icTotalPageOldest = m_cRollingAvgDepth - 1; m_icTotalPageOldest >= 0; m_icTotalPageOldest-- )
		{
		m_cTotalPage[ m_icTotalPageOldest ] = cTotalPageInit;
		}
	m_icTotalPageOldest = 0;
	m_cTotalPageSum = m_cRollingAvgDepth * (double)cTotalPageInit;
	m_cTotalPageAvg = (double)cTotalPageInit;

	size_t cAvailPageInit = AvailablePhysicalMemoryPages();
	for ( m_icAvailPageOldest = m_cRollingAvgDepth - 1; m_icAvailPageOldest >= 0; m_icAvailPageOldest-- )
		{
		m_cAvailPage[ m_icAvailPageOldest ] = cAvailPageInit;
		}
	m_icAvailPageOldest = 0;
	m_cAvailPageSum = m_cRollingAvgDepth * (double)cAvailPageInit;
	m_cAvailPageAvg = (double)cAvailPageInit;

	size_t cbPageInit = PhysicalMemoryPageSize();
	for ( m_icbPageOldest = m_cRollingAvgDepth - 1; m_icbPageOldest >= 0; m_icbPageOldest-- )
		{
		m_cbPage[ m_icbPageOldest ] = cbPageInit;
		}
	m_icbPageOldest = 0;
	m_cbPageSum = m_cRollingAvgDepth * (double)cbPageInit;
	m_cbPageAvg = (double)cbPageInit;

	long cPageEvictInit = TotalPhysicalMemoryPageEvictions();
	m_cPageEvict2 = cPageEvictInit;
	m_cPageEvict1 = cPageEvictInit;
	for ( m_idcPageEvictOldest = m_cRollingAvgDepth - 1; m_idcPageEvictOldest >= 0; m_idcPageEvictOldest-- )
		{
		m_dcPageEvict[ m_idcPageEvictOldest ] = 0;
		}
	m_idcPageEvictOldest = 0;
	m_dcPageEvictSum = 0;
	m_dcPageEvictAvg = 0;

	size_t cbResourceInit = ResourceSize();
	for ( m_icbResourceOldest = m_cRollingAvgDepth - 1; m_icbResourceOldest >= 0; m_icbResourceOldest-- )
		{
		m_cbResource[ m_icbResourceOldest ] = cbResourceInit;
		}
	m_icbResourceOldest = 0;
	m_cbResourceSum = m_cRollingAvgDepth * (double)cbResourceInit;
	m_cbResourceAvg = (double)cbResourceInit;

	size_t cTotalResourceInit = TotalResources();
	for ( m_icTotalResourceOldest = m_cRollingAvgDepth - 1; m_icTotalResourceOldest >= 0; m_icTotalResourceOldest-- )
		{
		m_cTotalResource[ m_icTotalResourceOldest ] = cTotalResourceInit;
		}
	m_icTotalResourceOldest = 0;
	m_cTotalResourceSum = m_cRollingAvgDepth * (double)cTotalResourceInit;
	m_cTotalResourceAvg = (double)cTotalResourceInit;

	long cResourceEvictInit = TotalResourceEvictions();
	m_cResourceEvict2 = cResourceEvictInit;
	m_cResourceEvict1 = cResourceEvictInit;
	for ( m_idcResourceEvictOldest = m_cRollingAvgDepth - 1; m_idcResourceEvictOldest >= 0; m_idcResourceEvictOldest-- )
		{
		m_dcResourceEvict[ m_idcResourceEvictOldest ] = 0;
		}
	m_idcResourceEvictOldest = 0;
	m_dcResourceEvictSum = 0;
	m_dcResourceEvictAvg = 0;

	m_cResourceMin = 1;
	m_cResourceMax = size_t( m_cTotalPageAvg * m_cbPageAvg / m_cbResourceAvg );
	}

//  updates the observed characteristics of the resource pool and the system as
//  a whole for the purpose of making real-time suggestions as to the size of
//  the resource pool.  this function should be called at approximately 1 Hz

inline void CDBAResourceAllocationManager::UpdateStatistics()
	{
	//  update all statistics

	m_cTotalPageSum -= m_cTotalPage[ m_icTotalPageOldest ];
	m_cTotalPage[ m_icTotalPageOldest ] = TotalPhysicalMemoryPages();
	m_cTotalPageSum += m_cTotalPage[ m_icTotalPageOldest ];
	m_icTotalPageOldest = ( m_icTotalPageOldest + 1 ) % m_cRollingAvgDepth;
	m_cTotalPageAvg = m_cTotalPageSum / m_cRollingAvgDepth;

	m_cAvailPageSum -= m_cAvailPage[ m_icAvailPageOldest ];
	m_cAvailPage[ m_icAvailPageOldest ] = AvailablePhysicalMemoryPages();
	m_cAvailPageSum += m_cAvailPage[ m_icAvailPageOldest ];
	m_icAvailPageOldest = ( m_icAvailPageOldest + 1 ) % m_cRollingAvgDepth;
	m_cAvailPageAvg = m_cAvailPageSum / m_cRollingAvgDepth;

	m_cbPageSum -= m_cbPage[ m_icbPageOldest ];
	m_cbPage[ m_icbPageOldest ] = PhysicalMemoryPageSize();
	m_cbPageSum += m_cbPage[ m_icbPageOldest ];
	m_icbPageOldest = ( m_icbPageOldest + 1 ) % m_cRollingAvgDepth;
	m_cbPageAvg = m_cbPageSum / m_cRollingAvgDepth;

	m_cPageEvict2 = m_cPageEvict1;
	m_cPageEvict1 = TotalPhysicalMemoryPageEvictions();
	m_dcPageEvictSum -= m_dcPageEvict[ m_idcPageEvictOldest ];
	m_dcPageEvict[ m_idcPageEvictOldest ] = m_cPageEvict1 - m_cPageEvict2;
	m_dcPageEvictSum += m_dcPageEvict[ m_idcPageEvictOldest ];
	m_idcPageEvictOldest = ( m_idcPageEvictOldest + 1 ) % m_cRollingAvgDepth;
	m_dcPageEvictAvg = m_dcPageEvictSum / m_cRollingAvgDepth;

	m_cbResourceSum -= m_cbResource[ m_icbResourceOldest ];
	m_cbResource[ m_icbResourceOldest ] = ResourceSize();
	m_cbResourceSum += m_cbResource[ m_icbResourceOldest ];
	m_icbResourceOldest = ( m_icbResourceOldest + 1 ) % m_cRollingAvgDepth;
	m_cbResourceAvg = m_cbResourceSum / m_cRollingAvgDepth;

	m_cTotalResourceSum -= m_cTotalResource[ m_icTotalResourceOldest ];
	m_cTotalResource[ m_icTotalResourceOldest ] = TotalResources();
	m_cTotalResourceSum += m_cTotalResource[ m_icTotalResourceOldest ];
	m_icTotalResourceOldest = ( m_icTotalResourceOldest + 1 ) % m_cRollingAvgDepth;
	m_cTotalResourceAvg = m_cTotalResourceSum / m_cRollingAvgDepth;

	m_cResourceEvict2 = m_cResourceEvict1;
	m_cResourceEvict1 = TotalResourceEvictions();
	m_dcResourceEvictSum -= m_dcResourceEvict[ m_idcResourceEvictOldest ];
	m_dcResourceEvict[ m_idcResourceEvictOldest ] = m_cResourceEvict1 - m_cResourceEvict2;
	m_dcResourceEvictSum += m_dcResourceEvict[ m_idcResourceEvictOldest ];
	m_idcResourceEvictOldest = ( m_idcResourceEvictOldest + 1 ) % m_cRollingAvgDepth;
	m_dcResourceEvictAvg = m_dcResourceEvictSum / m_cRollingAvgDepth;

	m_cResourceMin = 1;
	m_cResourceMax = size_t( m_cTotalPageAvg * m_cbPageAvg / m_cbResourceAvg );

	//  recommend a size for the resource pool

	SetOptimalResourcePoolSize( OptimalResourcePoolSize() );
	}

//  returns the current recommended size for the resource pool according to the
//  observed characteristics of that pool and the system as a whole as sampled
//  via UpdateStatistics().  the recommended size is given in bytes.  note that
//  the recommended size is merely a suggestion that can be used to maximize
//  overall system performance.  it is not mandatory to follow this suggestion
//  exactly or instantaneously

inline size_t CDBAResourceAllocationManager::OptimalResourcePoolSize()
	{
	//  our goal is to equalize the internal memory pressure in our resource
	//  pool with the external memory pressure from the rest of the system.  we
	//  do this using the following differential equation:
	//
	//    dRM/dt = k1 * AM/TM * dRE/dt - k2 * RM/TM * dPE/dt
	//
	//      RM = Total Resource Memory
	//      k1 = fudge factor 1 (1.0 by default)
	//      AM = Available System Memory
	//      TM = Total System Memory
	//      RE = Resource Evictions
	//      k2 = fudge factor 2 (1.0 by default)
	//      PE = System Page Evictions
	//
	//  this equation has two parts:
	//
	//    the first half tries to grow the resource pool proportional to the
	//    internal resource pool memory pressure and to the amount of memory
	//    available in the system
	//
	//    the second half tries to shrink the resource pool proportional to the
	//    external resource pool memory pressure and to the amount of resource
	//    memory we own
	//
	//  in other words, the more available memory and the faster resources are
	//  evicted, the faster the pool grows.  the larger the pool size and the
	//  faster memory pages are evicted, the faster the pool shrinks
	//
	//  the method behind the madness is an approximation of page victimization
	//  by the OS under memory pressure.  the more memory we have then the more
	//  likely it is that one of our pages will be evicted.  to pre-emptively
	//  avoid this, we'll voluntarily reduce our memory usage when we determine
	//  that memory pressure will cause our pages to get evicted.  note that
	//  the reverse of this is also true.  if we make it clear to the OS that
	//  we need this memory by slowly growing while under memory pressure, we
	//  force pages of memory owned by others to be victimized to be used by
	//  our pool.  the more memory any one of those others owns, the more
	//  likely it is that one of their pages will be stolen.  if any of the
	//  others is another resource allocation manager then they can communicate
	//  their memory needs indirectly via the memory pressure they each apply
	//  on the system.  the net result is an equilibrium where each pool gets
	//  the memory it "deserves"

	//  compute the change in the amount of memory that this resource pool
	//  should have

	double	k1						= 1.0;
	double	cbAvailMemory			= m_cAvailPageAvg * m_cbPageAvg;
	double	dcbResourceEvict		= m_dcResourceEvictAvg * m_cbResourceAvg;
	double	k2						= 1.0;
	double	cbResourcePool			= m_cTotalResourceAvg * m_cbResourceAvg;
	double	dcbPageEvict			= m_dcPageEvictAvg * m_cbPageAvg;
	double	cbTotalMemory			= m_cTotalPageAvg * m_cbPageAvg;

	double	dcTotalResource			=	(
											k1 * cbAvailMemory * dcbResourceEvict -
											k2 * cbResourcePool * dcbPageEvict
										) /
										(
											cbTotalMemory * m_cbResourceAvg
										);

	//  round the delta away from zero so that we don't get stuck at the same
	//  number of resources if the delta is commonly less than one resource

	dcTotalResource += dcTotalResource < 0 ? -0.5 : 0.5;
	modf( dcTotalResource, &dcTotalResource );

	//  get the new amount of memory that this resource pool should have

	double cResourceNew = TotalResources() + dcTotalResource;

	//  limit the new resource pool size to the realm of sanity just in case...

	cResourceNew = max( cResourceNew, m_cResourceMin );
	cResourceNew = min( cResourceNew, m_cResourceMax );

	//  return the suggested resource pool size

	return size_t( cResourceNew );
	}


//////////////////////////////////////////////////////////////////////////////////////////
//  CLRUKResourceUtilityManager
//
//  Implements a class used to manage a resource via the LRUK Replacement Policy.  Each
//  resource can be cached, touched, and evicted.  The current pool of resources can
//  also be scanned in order by ascending utility to allow eviction of less useful
//  resouces by a clean procedure.
//
//	m_Kmax			= the maximum K-ness of the LRUK Replacement Policy (the actual
//					  K-ness can be set at init time)
//  CResource		= class representing the managed resource
//  OffsetOfIC		= inline function returning the offset of the CInvasiveContext
//					  contained in each CResource
//  CKey			= class representing the key which uniquely identifies each
//					  instance of a CResource.  CKey must implement a default ctor,
//					  operator=(), and operator==()
//
//  You must use the DECLARE_LRUK_RESOURCE_UTILITY_MANAGER macro to declare this class.
//
//  You must implement the following inline function and pass its hashing characteristics
//  into ErrInit():
//
//  	int CLRUKResourceUtilityManager< m_Kmax, CResource, OffsetOfIC, CKey >::CHistoryTable::CKeyEntry::Hash( const CKey& key );

//  CONSIDER:  add code to special case LRU
//  CONSIDER:  add a fast allocator for CHistorys

template< int m_Kmax, class CResource, PfnOffsetOf OffsetOfIC, class CKey >
class CLRUKResourceUtilityManager
	{
	public:

		//  timestamp used for resource touch times

		typedef DWORD TICK;

		enum { tickNil	= ~TICK( 0 ) };
		enum { tickMask	= ~TICK( 0 ) ^ TICK( 1 ) };
		
		//  class containing context needed per CResource

		class CInvasiveContext
			{
			public:

				CInvasiveContext() {}
				~CInvasiveContext() {}

				static SIZE_T OffsetOfILE() { return OffsetOfIC() + OffsetOf( CInvasiveContext, m_aiic ); }
				static SIZE_T OffsetOfAIIC() { return OffsetOfIC() + OffsetOf( CInvasiveContext, m_aiic ); }

			private:

				friend class CLRUKResourceUtilityManager< m_Kmax, CResource, OffsetOfIC, CKey >;

				CApproximateIndex< TICK, CResource, OffsetOfAIIC >::CInvasiveContext	m_aiic;
				TICK																	m_tickLast;
				TICK																	m_rgtick[ m_Kmax ];
				TICK																	m_tickIndex;
			};

		//  API Error Codes

		enum ERR
			{
			errSuccess,
			errInvalidParameter,
			errOutOfMemory,
			errResourceNotCached,
			errNoCurrentResource,
			};

		//  API Lock Context

		class CLock;

	public:

		//  ctor / dtor

		CLRUKResourceUtilityManager( const int Rank );
		~CLRUKResourceUtilityManager();

		//  API

		ERR ErrInit(	const int		K,
						const double	csecCorrelatedTouch,
						const double	csecTimeout,
						const double	csecUncertainty,
						const double	dblHashLoadFactor,
						const double	dblHashUniformity,
						const double	dblSpeedSizeTradeoff );
		void Term();

		ERR ErrCacheResource( const CKey& key, CResource* const pres, const BOOL fUseHistory = fTrue );
		void TouchResource( CResource* const pres, const TICK tickNow = _TickCurrentTime() );
		BOOL FHotResource( CResource* const pres );
		ERR ErrEvictResource( const CKey& key, CResource* const pres, const BOOL fKeepHistory = fTrue );

		void LockResourceForEvict( CResource* const pres, CLock* const plock );
		void UnlockResourceForEvict( CLock* const plock );

		void BeginResourceScan( CLock* const plock );
		ERR ErrGetCurrentResource( CLock* const plock, CResource** const ppres );
		ERR ErrGetNextResource( CLock* const plock, CResource** const ppres );
		ERR ErrEvictCurrentResource( CLock* const plock, const CKey& key, const BOOL fKeepHistory = fTrue );
		void EndResourceScan( CLock* const plock );

		DWORD CHistoryRecord();
		DWORD CHistoryHit();
		DWORD CHistoryRequest();

		DWORD CResourceScanned();
		DWORD CResourceScannedOutOfOrder();

	public:

		//  class containing the history of an evicted CResource instance

		class CHistory
			{
			public:

				CHistory() {}
				~CHistory() {}

				static SIZE_T OffsetOfAIIC() { return OffsetOf( CHistory, m_aiic ); }

			public:

				CApproximateIndex< TICK, CHistory, OffsetOfAIIC >::CInvasiveContext	m_aiic;
				CKey																m_key;
				TICK																m_rgtick[ m_Kmax ];
			};
			
	private:

		//  index over CResources by LRUK

		typedef CApproximateIndex< TICK, CResource, CInvasiveContext::OffsetOfAIIC > CResourceLRUK;

		//  index over history by LRUK

		typedef CApproximateIndex< TICK, CHistory, CHistory::OffsetOfAIIC > CHistoryLRUK;

		//  list of resources that need to have their positions updated in the
		//  resource LRUK

		typedef CInvasiveList< CResource, CInvasiveContext::OffsetOfILE > CUpdateList;

		//  list of resources that couldn't be placed in their correct positions
		//  in the resource LRUK

		typedef CInvasiveList< CResource, CInvasiveContext::OffsetOfILE > CStuckList;

	public:

		//  API Lock Context

		class CLock
			{
			public:

				CLock() {}
				~CLock() {}

			private:

				friend class CLRUKResourceUtilityManager< m_Kmax, CResource, OffsetOfIC, CKey >;

				CResourceLRUK::CLock	m_lock;
				
				TICK					m_tickIndexCurrent;
				
				CUpdateList				m_UpdateList;
				
				CStuckList				m_StuckList;
				CResource*				m_presStuckList;
				CResource*				m_presStuckListNext;
			};

		//  entry used in the hash table used to map CKeys to CHistorys

		class CHistoryEntry
			{
			public:

				CHistoryEntry() {}
				~CHistoryEntry() {}

				CHistoryEntry& operator=( const CHistoryEntry& he )
					{
					m_KeySignature	= he.m_KeySignature;
					m_phist			= he.m_phist;
					return *this;
					}

			public:

				ULONG_PTR	m_KeySignature;
				CHistory*	m_phist;
			};

	private:

		//  table that maps CKeys to CHistoryEntrys

		typedef CDynamicHashTable< CKey, CHistoryEntry > CHistoryTable;

	private:

		void _TouchResource( CInvasiveContext* const pic, const TICK tickNow, const TICK tickLastBIExpected );
		void _RestoreHistory( const CKey& key, CInvasiveContext* const pic, const TICK tickNow );
		void _StoreHistory( const CKey& key, CInvasiveContext* const pic );
		CHistory* _PhistAllocHistory();
		CHistoryLRUK::ERR _ErrInsertHistory( CHistory* const phist );
		
		CResource* _PresFromPic( CInvasiveContext* const pic ) const;
		CInvasiveContext* _PicFromPres( CResource* const pres ) const;
		static TICK _TickCurrentTime();
		long _CmpTick( const TICK tick1, const TICK tick2 ) const;

	private:

		//  never updated

		BOOL			m_fInitialized;
		DWORD			m_K;
		TICK			m_ctickCorrelatedTouch;
		TICK			m_ctickTimeout;
		TICK			m_dtickUncertainty;
		BYTE			m_rgbReserved1[ 12 ];

		//  rarely updated

		volatile DWORD	m_cHistoryRecord;
		volatile DWORD	m_cHistoryHit;
		volatile DWORD	m_cHistoryReq;
		volatile DWORD	m_cResourceScanned;
		volatile DWORD	m_cResourceScannedOutOfOrder;
		BYTE			m_rgbReserved2[ 12 ];
		
		CHistoryLRUK	m_HistoryLRUK;
//		BYTE			m_rgbReserved3[ 0 ];

		CHistoryTable	m_KeyHistory;
//		BYTE			m_rgbReserved4[ 0 ];

		//  commonly updated

		CResourceLRUK	m_ResourceLRUK;
//		BYTE			m_rgbReserved5[ 0 ];
	};

//  ctor

template< int m_Kmax, class CResource, PfnOffsetOf OffsetOfIC, class CKey >
inline CLRUKResourceUtilityManager< m_Kmax, CResource, OffsetOfIC, CKey >::
CLRUKResourceUtilityManager( const int Rank )
	:	m_fInitialized( fFalse ),
		m_HistoryLRUK( Rank - 1 ),
		m_KeyHistory( Rank - 1 ),
		m_ResourceLRUK( Rank )
	{
	}

//  dtor
	
template< int m_Kmax, class CResource, PfnOffsetOf OffsetOfIC, class CKey >
inline CLRUKResourceUtilityManager< m_Kmax, CResource, OffsetOfIC, CKey >::
~CLRUKResourceUtilityManager()
	{
	}

//  initializes the LRUK resource utility manager using the given parameters.
//  if the RUM cannot be initialized, errOutOfMemory is returned

template< int m_Kmax, class CResource, PfnOffsetOf OffsetOfIC, class CKey >
inline CLRUKResourceUtilityManager< m_Kmax, CResource, OffsetOfIC, CKey >::ERR CLRUKResourceUtilityManager< m_Kmax, CResource, OffsetOfIC, CKey >::
ErrInit(	const int		K,
			const double	csecCorrelatedTouch,
			const double	csecTimeout,
			const double	csecUncertainty,
			const double	dblHashLoadFactor,
			const double	dblHashUniformity,
			const double	dblSpeedSizeTradeoff )
	{
	//  validate all parameters

	if (	K < 1 || K > m_Kmax ||
			csecCorrelatedTouch < 0 ||
			csecTimeout < 0 ||
			dblHashLoadFactor < 0 ||
			dblHashUniformity < 1.0 )
		{
		return errInvalidParameter;
		}

	//  init our parameters

	m_K						= DWORD( K );
	m_ctickCorrelatedTouch	= TICK( csecCorrelatedTouch * 1000.0 );
	m_ctickTimeout			= TICK( csecTimeout * 1000.0 );
	m_dtickUncertainty		= TICK( max( csecUncertainty * 1000.0, 2.0 ) );

	//  init our counters

	m_cHistoryRecord	= 0;
	m_cHistoryHit		= 0;
	m_cHistoryReq		= 0;

	m_cResourceScanned				= 0;
	m_cResourceScannedOutOfOrder	= 0;

	//  init our indexes
	//
	//  NOTE:  the precision for the history LRUK is intentionally low so that
	//  we are forced to purge history records older than twice the timeout
	//  in order to insert new history records.  we would normally only purge
	//  history records on demand which could allow a sudden burst of activity
	//  to permanently allocate an abnormally high number of history records

	if ( m_HistoryLRUK.ErrInit(	4 * m_K * m_ctickTimeout,
								m_dtickUncertainty,
								dblSpeedSizeTradeoff ) != errSuccess )
		{
		Term();
		return errOutOfMemory;
		}

	if ( m_KeyHistory.ErrInit(	dblHashLoadFactor,
								dblHashUniformity ) != errSuccess )
		{
		Term();
		return errOutOfMemory;
		}

#ifdef DEBUG
	const TICK dtickLRUKPrecision = 2 * m_K * m_ctickTimeout;
#else  //  !DEBUG
#ifdef RTM
	const TICK dtickLRUKPrecision = ~TICK( 0 );  //  49.7 days
#else  //  !RTM
	const TICK dtickLRUKPrecision = 2 * 24 * 60 * 60 * 1000;  //  2 days
#endif  //  RTM
#endif  //  DEBUG

	if ( m_ResourceLRUK.ErrInit(	dtickLRUKPrecision,
									m_dtickUncertainty,
									dblSpeedSizeTradeoff ) != errSuccess )
		{
		Term();
		return errOutOfMemory;
		}

	m_fInitialized = fTrue;
	return errSuccess;
	}

//  terminates the LRUK RUM.  this function can be called even if the RUM has
//  never been initialized or is only partially initialized
//
//  NOTE:  any data stored in the RUM at this time will be lost!

template< int m_Kmax, class CResource, PfnOffsetOf OffsetOfIC, class CKey >
inline void CLRUKResourceUtilityManager< m_Kmax, CResource, OffsetOfIC, CKey >::
Term()
	{
	//  purge all history records from the history LRUK.  we must do this from
	//  the history LRUK and not the history table as it is possible for the
	//  history table to lose pointers to history records in the history LRUK.
	//  see ErrEvictCurrentResource for details

	if ( m_fInitialized )
		{
		CHistoryLRUK::CLock lockHLRUK;

		m_HistoryLRUK.MoveBeforeFirst( &lockHLRUK );
		while ( m_HistoryLRUK.ErrMoveNext( &lockHLRUK ) == errSuccess )
			{
			CHistory* phist;
			CHistoryLRUK::ERR errHLRUK = m_HistoryLRUK.ErrRetrieveEntry( &lockHLRUK, &phist );
			RESMGRAssert( errHLRUK == CHistoryLRUK::errSuccess );

			errHLRUK = m_HistoryLRUK.ErrDeleteEntry( &lockHLRUK );
			RESMGRAssert( errHLRUK == CHistoryLRUK::errSuccess );

			delete phist;
			}
		m_HistoryLRUK.UnlockKeyPtr( &lockHLRUK );
		}
		
	//  terminate our indexes

	m_ResourceLRUK.Term();
	m_KeyHistory.Term();
	m_HistoryLRUK.Term();

	m_fInitialized = fFalse;
	}

//  starts management of the specified resource optionally using any previous
//  knowledge the RUM has about this resource.  the resource must currently be
//  evicted from the RUM.  the resource will be touched by this call.  if we
//  cannot start management of this resource, errOutOfMemory will be returned

template< int m_Kmax, class CResource, PfnOffsetOf OffsetOfIC, class CKey >
inline CLRUKResourceUtilityManager< m_Kmax, CResource, OffsetOfIC, CKey >::ERR CLRUKResourceUtilityManager< m_Kmax, CResource, OffsetOfIC, CKey >::
ErrCacheResource( const CKey& key, CResource* const pres, const BOOL fUseHistory )
	{
	ERR						err = errSuccess;
	CLock					lock;
	CResourceLRUK::ERR		errLRUK;

	CInvasiveContext* const pic = _PicFromPres( pres );

	//  initialize this resource to be touched once by setting all of its
	//  touch times to the current time

	const TICK tickNow = _TickCurrentTime();

	for ( int K = 1; K <= m_Kmax; K++ )
		{
		pic->m_rgtick[ K - 1 ] = tickNow;
		}
	pic->m_tickLast = tickNow;

	//  we are supposed to use the history for this resource if available

	if ( fUseHistory )
		{
		//  restore the history for this resource

		_RestoreHistory( key, pic, tickNow );
		}

	//  insert this resource into the resource LRUK at its Kth touch time or as
	//  close as we can get to that Kth touch time

	do
		{
		const TICK tickK = pic->m_rgtick[ m_K - 1 ];
		m_ResourceLRUK.LockKeyPtr( tickK, pres, &lock.m_lock );

		pic->m_tickIndex = tickK;
		errLRUK = m_ResourceLRUK.ErrInsertEntry( &lock.m_lock, pres, fTrue );

		if ( errLRUK != CResourceLRUK::errSuccess )
			{
			pic->m_tickIndex = tickNil;
			m_ResourceLRUK.UnlockKeyPtr( &lock.m_lock );

			const TICK tickYoungest = m_ResourceLRUK.KeyInsertMost();
			m_ResourceLRUK.LockKeyPtr( tickYoungest, pres, &lock.m_lock );

			pic->m_tickIndex = tickYoungest;
			errLRUK = m_ResourceLRUK.ErrInsertEntry( &lock.m_lock, pres, fTrue );

			if ( errLRUK != CResourceLRUK::errSuccess )
				{
				pic->m_tickIndex = tickNil;
				}
			}

		m_ResourceLRUK.UnlockKeyPtr( &lock.m_lock );
		}
	while ( errLRUK == CResourceLRUK::errKeyRangeExceeded );
	RESMGRAssert(	errLRUK == CResourceLRUK::errSuccess ||
			errLRUK == CResourceLRUK::errOutOfMemory );

	return errLRUK == CResourceLRUK::errSuccess ? errSuccess : errOutOfMemory;
	}

//  touches the specifed resource according to the LRUK Replacement Policy
//
//  NOTE:  only the touch times are updated here.  the actual update of this
//  resource's position in the LRUK index is amortized to ErrGetNextResource().
//  this scheme minimizes updates to the index and localizes those updates
//  to the thread(s) that perform cleanup of the resources.  this scheme also
//  makes it impossible to block while touching a resource

template< int m_Kmax, class CResource, PfnOffsetOf OffsetOfIC, class CKey >
inline void CLRUKResourceUtilityManager< m_Kmax, CResource, OffsetOfIC, CKey >::
TouchResource( CResource* const pres, const TICK tickNow )
	{
	CInvasiveContext* const pic = _PicFromPres( pres );

	//  get the current last touch time of the resource.  we will use this time
	//  to lock the resource for touching.  if the last touch time ever changes
	//  to not match this touch time then we can no longer touch the resource
	//
	//  NOTE:  munge the last tick time so that it will never match tickNil

	const TICK tickLastBIExpected = pic->m_tickLast & tickMask;

	//  if the current time is equal to the last touch time then there is no
	//  need to touch the resource
	//
	//  NOTE:  also handle the degenerate case where the current time is less
	//  than the last touch time

	if ( _CmpTick( tickNow, tickLastBIExpected ) <= 0 )
		{
		//  do not touch the resource
		}

	//  if the current time is not equal to the last touch time then we need
	//  to update the touch times of the resource

	else
		{
		_TouchResource( pic, tickNow, tickLastBIExpected );
		}
	}

//  returns fTrue if the specified resource is touched frequently

template< int m_Kmax, class CResource, PfnOffsetOf OffsetOfIC, class CKey >
inline BOOL CLRUKResourceUtilityManager< m_Kmax, CResource, OffsetOfIC, CKey >::
FHotResource( CResource* const pres )
	{
	CInvasiveContext* const pic = _PicFromPres( pres );

	//  a resource is hot if it's Kth touch time is within the Kth time region
	//  in the index

	const TICK	tickNow			= _TickCurrentTime();
	const TICK	tickKTimeout	= ( m_K - 2 ) * m_ctickTimeout;
	const TICK	tickKHot		= tickNow + tickKTimeout;

	return _CmpTick( pic->m_rgtick[ m_K - 1 ], tickKHot ) > 0;
	}

//  stops management of the specified resource optionally saving any current
//  knowledge the RUM has about this resource.  if the resource is not currently
//  cached by the RUM, errResourceNotCached will be returned
	
template< int m_Kmax, class CResource, PfnOffsetOf OffsetOfIC, class CKey >
inline CLRUKResourceUtilityManager< m_Kmax, CResource, OffsetOfIC, CKey >::ERR CLRUKResourceUtilityManager< m_Kmax, CResource, OffsetOfIC, CKey >::
ErrEvictResource( const CKey& key, CResource* const pres, const BOOL fKeepHistory )
	{
	ERR		err = errSuccess;
	CLock	lock;

	//  lock and evict this resource from the resource LRUK by setting up our
	//  currency to look like we navigated to this resource normally and then
	//  calling our normal eviction routine

	LockResourceForEvict( pres, &lock );
	
	if ( ( err = ErrEvictCurrentResource( &lock, key, fKeepHistory ) ) != errSuccess )
		{
		RESMGRAssert( err == errNoCurrentResource );

		err = errResourceNotCached;
		}

	UnlockResourceForEvict( &lock );
	return err;
	}

//  sets up the specified lock context in preparation for evicting the given
//  resource

template< int m_Kmax, class CResource, PfnOffsetOf OffsetOfIC, class CKey >
inline void CLRUKResourceUtilityManager< m_Kmax, CResource, OffsetOfIC, CKey >::
LockResourceForEvict( CResource* const pres, CLock* const plock )
	{
	//  get the current index touch time for this resource
	
	CInvasiveContext* const pic = _PicFromPres( pres );

	const TICK tickIndex = pic->m_tickIndex;

	//  lock this resource for this Kth touch time in the LRUK

	m_ResourceLRUK.LockKeyPtr( tickIndex, pres, &plock->m_lock );

	//  the index touch time has changed for this resource or it is tickNil

	if ( pic->m_tickIndex != tickIndex || tickIndex == tickNil )
		{
		//  release our lock and lock tickNil for this resource.  we know that
		//  we will never find a resource in this bucket so we will have the
		//  desired errNoCurrentResource from ErrEvictCurrentResource

		m_ResourceLRUK.UnlockKeyPtr( &plock->m_lock );
		m_ResourceLRUK.MoveBeforeKeyPtr( tickNil, NULL, &plock->m_lock );
		}
	}

//  releases the lock acquired with LockResourceForEvict()

template< int m_Kmax, class CResource, PfnOffsetOf OffsetOfIC, class CKey >
inline void CLRUKResourceUtilityManager< m_Kmax, CResource, OffsetOfIC, CKey >::
UnlockResourceForEvict( CLock* const plock )
	{
	m_ResourceLRUK.UnlockKeyPtr( &plock->m_lock );
	}

//  sets up the specified lock context in preparation for scanning all resources
//  managed by the RUM by ascending utility
//
//  NOTE:  this function will acquire a lock that must eventually be released
//  via EndResourceScan()

template< int m_Kmax, class CResource, PfnOffsetOf OffsetOfIC, class CKey >
inline void CLRUKResourceUtilityManager< m_Kmax, CResource, OffsetOfIC, CKey >::
BeginResourceScan( CLock* const plock )
	{
	//  move before the first entry in the resource LRUK

	m_ResourceLRUK.MoveBeforeFirst( &plock->m_lock );
	}

//  retrieves the current resource managed by the RUM by ascending utility
//  locked by the specified lock context.  if there is no current resource, 
//  errNoCurrentResource is returned

template< int m_Kmax, class CResource, PfnOffsetOf OffsetOfIC, class CKey >
inline CLRUKResourceUtilityManager< m_Kmax, CResource, OffsetOfIC, CKey >::ERR CLRUKResourceUtilityManager< m_Kmax, CResource, OffsetOfIC, CKey >::
ErrGetCurrentResource( CLock* const plock, CResource** const ppres )
	{
	//  we are currently walking the resource LRUK

	if ( plock->m_StuckList.FEmpty() )
		{
		CResourceLRUK::ERR errLRUK;
		
		//  get the current resource in the resource LRUK

		errLRUK = m_ResourceLRUK.ErrRetrieveEntry( &plock->m_lock, ppres );

		//  return the status of our currency

		return errLRUK == CResourceLRUK::errSuccess ? errSuccess : errNoCurrentResource;
		}

	//  we are currently walking the list of resources that couldn't be
	//  moved

	else
		{
		//  return the status of our currency on the stuck list

		*ppres = plock->m_presStuckList;
		return *ppres ? errSuccess : errNoCurrentResource;
		}
	}

//  retrieves the next resource managed by the RUM by ascending utility locked
//  by the specified lock context.  if there are no more resources to be
//  scanned, errNoCurrentResource is returned

template< int m_Kmax, class CResource, PfnOffsetOf OffsetOfIC, class CKey >
inline CLRUKResourceUtilityManager< m_Kmax, CResource, OffsetOfIC, CKey >::ERR CLRUKResourceUtilityManager< m_Kmax, CResource, OffsetOfIC, CKey >::
ErrGetNextResource( CLock* const plock, CResource** const ppres )
	{
	CResourceLRUK::ERR errLRUK;
	
	//  keep scanning until we establish currency on a resource or reach the
	//  end of the resource LRUK

	m_cResourceScanned++;

	*ppres	= NULL;
	errLRUK	= CResourceLRUK::errSuccess;
	do
		{
		//  we are currently walking the resource LRUK

		if ( plock->m_StuckList.FEmpty() )
			{
			//  scan forward in the index until we find the least useful
			//  resource that is in the correct place in the index or we reach
			//  the end of the index

			while ( ( errLRUK = m_ResourceLRUK.ErrMoveNext( &plock->m_lock ) ) == CResourceLRUK::errSuccess )
				{
				CResourceLRUK::ERR	errLRUK2 = CResourceLRUK::errSuccess;
				CResource*			pres;
				
				errLRUK2 = m_ResourceLRUK.ErrRetrieveEntry( &plock->m_lock, &pres );
				RESMGRAssert( errLRUK2 == CResourceLRUK::errSuccess );

				CInvasiveContext* const pic = _PicFromPres( pres );

				//  if our update list contains resources and the tickIndex for
				//  those resources doesn't match the tickIndex for this
				//  resource then we need to stop and empty the update list

				if (	!plock->m_UpdateList.FEmpty() &&
						m_ResourceLRUK.CmpKey( pic->m_tickIndex, plock->m_tickIndexCurrent ) )
					{
					break;
					}

				//  if the Kth touch time of this resource matches the tickIndex
				//  of the resource then we can return this resource as the
				//  current resource because it is in the correct place in the
				//  index

				if ( !m_ResourceLRUK.CmpKey( pic->m_tickIndex, pic->m_rgtick[ m_K - 1 ] ) )
					{
					*ppres = pres;
					break;
					}

				//  if the Kth touch time of this resource doesn't match the
				//  tickIndex of the resource then add the resource to the
				//  update list so that we can move it to the correct place
				//  later.  leave space reserved for this resource here so
				//  that we can always move it back in case of an error

				errLRUK2 = m_ResourceLRUK.ErrReserveEntry( &plock->m_lock );
				RESMGRAssert( errLRUK2 == CResourceLRUK::errSuccess );

				errLRUK2 = m_ResourceLRUK.ErrDeleteEntry( &plock->m_lock );
				RESMGRAssert( errLRUK2 == CResourceLRUK::errSuccess );

				plock->m_tickIndexCurrent	= pic->m_tickIndex;
				pic->m_tickIndex			= tickNil;
				plock->m_UpdateList.InsertAsNextMost( pres );
				}
			RESMGRAssert( errLRUK == CResourceLRUK::errSuccess ||
					errLRUK == CResourceLRUK::errNoCurrentEntry );

			//  we still do not have a current resource

			if ( *ppres == NULL )
				{
				//  release our lock on the resource LRUK

				m_ResourceLRUK.UnlockKeyPtr( &plock->m_lock );
				
				//  try to update the positions of any resources we have in our
				//  update list.  remove the reservation for any resource we
				//  can move and put any resource we can't move in the stuck
				//  list

				while ( !plock->m_UpdateList.FEmpty() )
					{
					CResource* const		pres	= plock->m_UpdateList.PrevMost();
					CInvasiveContext* const	pic		= _PicFromPres( pres );
					
					plock->m_UpdateList.Remove( pres );

					CResourceLRUK::ERR errLRUK2;
					do
						{
						const TICK tickK = pic->m_rgtick[ m_K - 1 ];
						m_ResourceLRUK.LockKeyPtr( tickK, pres, &plock->m_lock );

						pic->m_tickIndex = tickK;
						errLRUK2 = m_ResourceLRUK.ErrInsertEntry( &plock->m_lock, pres, fTrue );

						if ( errLRUK2 != CResourceLRUK::errSuccess )
							{
							pic->m_tickIndex = tickNil;
							m_ResourceLRUK.UnlockKeyPtr( &plock->m_lock );

							const TICK tickYoungest = m_ResourceLRUK.KeyInsertMost();
							m_ResourceLRUK.LockKeyPtr( tickYoungest, pres, &plock->m_lock );

							pic->m_tickIndex = tickYoungest;
							if ( m_ResourceLRUK.CmpKey( pic->m_tickIndex, plock->m_tickIndexCurrent ) )
								{
								errLRUK2 = m_ResourceLRUK.ErrInsertEntry( &plock->m_lock, pres, fTrue );
								}
							else
								{
								errLRUK2 = CResourceLRUK::errOutOfMemory;
								}

							if ( errLRUK2 != CResourceLRUK::errSuccess )
								{
								pic->m_tickIndex = tickNil;
								}
							}

						m_ResourceLRUK.UnlockKeyPtr( &plock->m_lock );
						}
					while ( errLRUK2 == CResourceLRUK::errKeyRangeExceeded );

					if ( errLRUK2 == CResourceLRUK::errSuccess )
						{
						m_ResourceLRUK.LockKeyPtr( plock->m_tickIndexCurrent, pres, &plock->m_lock );
						m_ResourceLRUK.UnreserveEntry( &plock->m_lock );
						m_ResourceLRUK.UnlockKeyPtr( &plock->m_lock );

						errLRUK = CResourceLRUK::errSuccess;
						}
					else
						{
						plock->m_StuckList.InsertAsNextMost( pres );
						}
					}

				//  set our currency so that if we have any resources in the
				//  stuck list then we will walk them as if they are in the
				//  correct place in the resource LRUK

				if ( plock->m_StuckList.FEmpty() )
					{
					m_ResourceLRUK.MoveAfterKeyPtr( plock->m_tickIndexCurrent, (CResource*)~NULL, &plock->m_lock );
					}
				else
					{
					plock->m_presStuckList		= NULL;
					plock->m_presStuckListNext	= plock->m_StuckList.PrevMost();
					}
				}
			}

		//  we are currently walking the list of resources that couldn't be
		//  moved

		if ( !plock->m_StuckList.FEmpty() )
			{
			//  move to the next resource in the list

			plock->m_presStuckList		= (	plock->m_presStuckList ?
												plock->m_StuckList.Next( plock->m_presStuckList ) :
												plock->m_presStuckListNext );
			plock->m_presStuckListNext	= NULL;

			//  we have a current resource

			if ( plock->m_presStuckList )
				{
				//  return the current resource

				m_cResourceScannedOutOfOrder++;

				*ppres = plock->m_presStuckList;
				}

			//  we still do not have a current resource

			else
				{
				//  we have walked off of the end of the list of resources that
				//  couldn't be moved so we need to put them back into their
				//  source buckets before we move on to the next entry in the
				//  resource LRUK.  we are guaranteed to be able to put them
				//  back because we reserved space for them

				while ( !plock->m_StuckList.FEmpty() )
					{
					CResource*			pres		= plock->m_StuckList.PrevMost();
					CResourceLRUK::ERR	errLRUK2	= CResourceLRUK::errSuccess;

					plock->m_StuckList.Remove( pres );
					
					m_ResourceLRUK.LockKeyPtr( plock->m_tickIndexCurrent, pres, &plock->m_lock );
					
					m_ResourceLRUK.UnreserveEntry( &plock->m_lock );

					_PicFromPres( pres )->m_tickIndex = plock->m_tickIndexCurrent;
					errLRUK2 = m_ResourceLRUK.ErrInsertEntry( &plock->m_lock, pres, fTrue );
					RESMGRAssert( errLRUK2 == CResourceLRUK::errSuccess );
					
					m_ResourceLRUK.UnlockKeyPtr( &plock->m_lock );
					}

				//  we are no longer walking the stuck list so restore our
				//  currency on the resource LRUK

				m_ResourceLRUK.MoveAfterKeyPtr( plock->m_tickIndexCurrent, (CResource*)~NULL, &plock->m_lock );
				}
			}
		}
	while ( *ppres == NULL && errLRUK != CResourceLRUK::errNoCurrentEntry );

	//  return the state of our currency

	return *ppres ? errSuccess : errNoCurrentResource;
	}

//  stops management of the current resource optionally saving any current
//  knowledge the RUM has about this resource.  if there is no current
//  resource, errNoCurrentResource will be returned
	
template< int m_Kmax, class CResource, PfnOffsetOf OffsetOfIC, class CKey >
inline CLRUKResourceUtilityManager< m_Kmax, CResource, OffsetOfIC, CKey >::ERR CLRUKResourceUtilityManager< m_Kmax, CResource, OffsetOfIC, CKey >::
ErrEvictCurrentResource( CLock* const plock, const CKey& key, const BOOL fKeepHistory )
	{
	ERR						err;
	CResourceLRUK::ERR		errLRUK;
	CResource*				pres;

	//  get the current resource in the resource LRUK, if any

	if ( ( err = ErrGetCurrentResource( plock, &pres ) ) != errSuccess )
		{
		return err;
		}
	
	//  we are currently walking the resource LRUK

	if ( plock->m_StuckList.FEmpty() )
		{
		//  delete the current resource from the resource LRUK

		errLRUK = m_ResourceLRUK.ErrDeleteEntry( &plock->m_lock );
		RESMGRAssert( errLRUK == CResourceLRUK::errSuccess );
		}

	//  we are currently walking the list of resources that couldn't be
	//  moved

	else
		{
		//  delete the current resource from the stuck list and remove its
		//  reservation from the resource LRUK

		plock->m_presStuckListNext = plock->m_StuckList.Next( pres );
		plock->m_StuckList.Remove( pres );
		plock->m_presStuckList = NULL;
		
		m_ResourceLRUK.LockKeyPtr( plock->m_tickIndexCurrent, pres, &plock->m_lock );
		m_ResourceLRUK.UnreserveEntry( &plock->m_lock );
		m_ResourceLRUK.UnlockKeyPtr( &plock->m_lock );

		//  if we are no longer walking the stuck list then restore our currency
		//  on the resource LRUK

		if ( plock->m_StuckList.FEmpty() )
			{
			m_ResourceLRUK.MoveAfterKeyPtr( plock->m_tickIndexCurrent, (CResource*)~NULL, &plock->m_lock );
			}
		}

	//  we are to keep history for this resource

	if ( fKeepHistory )
		{
		//  store the history for this resource

		_StoreHistory( key, _PicFromPres( pres ) );
		}
	
	return errSuccess;
	}

//  ends the scan of all resources managed by the RUM by ascending utility
//  associated with the specified lock context and releases all locks held

template< int m_Kmax, class CResource, PfnOffsetOf OffsetOfIC, class CKey >
inline void CLRUKResourceUtilityManager< m_Kmax, CResource, OffsetOfIC, CKey >::
EndResourceScan( CLock* const plock )
	{
	//  we are currently walking the resource LRUK

	if ( plock->m_StuckList.FEmpty() )
		{
		//  release our lock on the resource LRUK

		m_ResourceLRUK.UnlockKeyPtr( &plock->m_lock );
		
		//  try to update the positions of any resources we have in our
		//  update list.  remove the reservation for any resource we
		//  can move and put any resource we can't move in the stuck
		//  list

		while ( !plock->m_UpdateList.FEmpty() )
			{
			CResource* const		pres	= plock->m_UpdateList.PrevMost();
			CInvasiveContext* const	pic		= _PicFromPres( pres );
			
			plock->m_UpdateList.Remove( pres );

			CResourceLRUK::ERR errLRUK;
			do
				{
				const TICK tickK = pic->m_rgtick[ m_K - 1 ];
				m_ResourceLRUK.LockKeyPtr( tickK, pres, &plock->m_lock );

				pic->m_tickIndex = tickK;
				errLRUK = m_ResourceLRUK.ErrInsertEntry( &plock->m_lock, pres, fTrue );

				if ( errLRUK != CResourceLRUK::errSuccess )
					{
					pic->m_tickIndex = tickNil;
					m_ResourceLRUK.UnlockKeyPtr( &plock->m_lock );

					const TICK tickYoungest = m_ResourceLRUK.KeyInsertMost();
					m_ResourceLRUK.LockKeyPtr( tickYoungest, pres, &plock->m_lock );

					pic->m_tickIndex = tickYoungest;
					errLRUK = m_ResourceLRUK.ErrInsertEntry( &plock->m_lock, pres, fTrue );

					if ( errLRUK != CResourceLRUK::errSuccess )
						{
						pic->m_tickIndex = tickNil;
						}
					}

				m_ResourceLRUK.UnlockKeyPtr( &plock->m_lock );
				}
			while ( errLRUK == CResourceLRUK::errKeyRangeExceeded );

			if ( errLRUK == CResourceLRUK::errSuccess )
				{
				m_ResourceLRUK.LockKeyPtr( plock->m_tickIndexCurrent, pres, &plock->m_lock );
				m_ResourceLRUK.UnreserveEntry( &plock->m_lock );
				m_ResourceLRUK.UnlockKeyPtr( &plock->m_lock );
				}
			else
				{
				plock->m_StuckList.InsertAsNextMost( pres );
				}
			}
		}

	//  put all the resources we were not able to move back into their
	//  source buckets.  we know that we will succeed because we reserved
	//  space for them
	
	while ( !plock->m_StuckList.FEmpty() )
		{
		CResource*			pres	= plock->m_StuckList.PrevMost();
		CResourceLRUK::ERR	errLRUK	= CResourceLRUK::errSuccess;

		plock->m_StuckList.Remove( pres );
		
		m_ResourceLRUK.LockKeyPtr( plock->m_tickIndexCurrent, pres, &plock->m_lock );
		
		m_ResourceLRUK.UnreserveEntry( &plock->m_lock );

		_PicFromPres( pres )->m_tickIndex = plock->m_tickIndexCurrent;
		errLRUK = m_ResourceLRUK.ErrInsertEntry( &plock->m_lock, pres, fTrue );
		RESMGRAssert( errLRUK == CResourceLRUK::errSuccess );
		
		m_ResourceLRUK.UnlockKeyPtr( &plock->m_lock );
		}
	}

//  returns the current number of resource history records retained for
//  supporting the LRUK replacement policy

template< int m_Kmax, class CResource, PfnOffsetOf OffsetOfIC, class CKey >
inline DWORD CLRUKResourceUtilityManager< m_Kmax, CResource, OffsetOfIC, CKey >::
CHistoryRecord()
	{
	return m_cHistoryRecord;
	}

//  returns the cumulative number of successful resource history record loads
//  when caching a resource

template< int m_Kmax, class CResource, PfnOffsetOf OffsetOfIC, class CKey >
inline DWORD CLRUKResourceUtilityManager< m_Kmax, CResource, OffsetOfIC, CKey >::
CHistoryHit()
	{
	return m_cHistoryHit;
	}

//  returns the cumulative number of attempted resource history record loads
//  when caching a resource

template< int m_Kmax, class CResource, PfnOffsetOf OffsetOfIC, class CKey >
inline DWORD CLRUKResourceUtilityManager< m_Kmax, CResource, OffsetOfIC, CKey >::
CHistoryRequest()
	{
	return m_cHistoryReq;
	}

//  returns the cumulative number of resources scanned in the LRUK

template< int m_Kmax, class CResource, PfnOffsetOf OffsetOfIC, class CKey >
inline DWORD CLRUKResourceUtilityManager< m_Kmax, CResource, OffsetOfIC, CKey >::
CResourceScanned()
	{
	return m_cResourceScanned;
	}

//  returns the cumulative number of resources scanned in the LRUK that were
//  returned out of order

template< int m_Kmax, class CResource, PfnOffsetOf OffsetOfIC, class CKey >
inline DWORD CLRUKResourceUtilityManager< m_Kmax, CResource, OffsetOfIC, CKey >::
CResourceScannedOutOfOrder()
	{
	return m_cResourceScannedOutOfOrder;
	}

//  updates the touch times of a given resource

template< int m_Kmax, class CResource, PfnOffsetOf OffsetOfIC, class CKey >
inline void CLRUKResourceUtilityManager< m_Kmax, CResource, OffsetOfIC, CKey >::
_TouchResource( CInvasiveContext* const pic, const TICK tickNow, const TICK tickLastBIExpected )
	{
	RESMGRAssert(	tickLastBIExpected != tickLastBIExpected ||
			_CmpTick( tickLastBIExpected, tickNow ) < 0 );
				
	//  if the current time and the last historical touch time are closer than
	//  the correlation interval then this is a correlated touch

	if ( tickNow - pic->m_rgtick[ 1 - 1 ] <= m_ctickCorrelatedTouch )
		{
		//  update the last tick time to the current time iff the last touch time
		//  has not changed and is not tickNil

		AtomicCompareExchange( (long*)&pic->m_tickLast, tickLastBIExpected, tickNow );
		}

	//  if the current time and the last historical touch time are not closer
	//  than the correlation interval then this is not a correlated touch
		
	else
		{
		//  to determine who will get to actually touch the resource, we will do an
		//  AtomicCompareExchange on the last touch time with tickNil.  the thread
		//  whose transaction succeeds wins the race and will touch the resource.
		//  all other threads will leave.  this is certainly acceptable as touches
		//  that collide are obviously too close together to be of any interest

		const TICK tickLastBI = TICK( AtomicCompareExchange( (long*)&pic->m_tickLast, tickLastBIExpected, tickNil ) );

		if ( tickLastBI == tickLastBIExpected )
			{
			//  compute the correlation period of the last series of correlated
			//  touches by taking the different between the last touch time and
			//  the most recent touch time in our history

			RESMGRAssert( _CmpTick( tickLastBI, pic->m_rgtick[ 1 - 1 ] ) >= 0 );

			const TICK dtickCorrelationPeriod = tickLastBI - pic->m_rgtick[ 1 - 1 ];
			
			//  move all touch times up one slot and advance them by the correlation
			//  period and the timeout.  this will collapse all correlated touches
			//  to a time interval of zero and will make resources that have more
			//  uncorrelated touches appear younger in the index
			
			for ( int K = m_Kmax; K >= 2; K-- )
				{
				pic->m_rgtick[ K - 1 ] =	pic->m_rgtick[ ( K - 1 ) - 1 ] +
											dtickCorrelationPeriod +
											m_ctickTimeout;
				}

			//  set the most recent historical touch time to the current time
			
			pic->m_rgtick[ 1 - 1 ] = tickNow;

			//  set the last touch time to the current time via AtomicExchange,
			//  unlocking this resource to touches by other threads

			AtomicExchange( (long*)&pic->m_tickLast, tickNow );
			}
		}
	}

//  restores the touch history for the specified resource if known

template< int m_Kmax, class CResource, PfnOffsetOf OffsetOfIC, class CKey >
inline void CLRUKResourceUtilityManager< m_Kmax, CResource, OffsetOfIC, CKey >::
_RestoreHistory( const CKey& key, CInvasiveContext* const pic, const TICK tickNow )
	{
	CHistoryTable::CLock	lockHist;
	CHistoryEntry			he;

	//  this resource has history

	m_cHistoryReq++;

	m_KeyHistory.ReadLockKey( key, &lockHist );
	if ( m_KeyHistory.ErrRetrieveEntry( &lockHist, &he ) == CHistoryTable::errSuccess )
		{
		//  determine the range of Kth touch times over which retained history
		//  is still valid

		const TICK	tickKHistoryMax	= tickNow + ( m_K - 1 ) * m_ctickTimeout;
		const TICK	tickKHistoryMin	= tickNow - m_ctickTimeout;
		
		//  the history is still valid

		if (	m_HistoryLRUK.CmpKey( tickKHistoryMin, he.m_phist->m_rgtick[ m_K - 1 ] ) <= 0 &&
				m_HistoryLRUK.CmpKey( he.m_phist->m_rgtick[ m_K - 1 ], tickKHistoryMax ) < 0 )
			{
			//  restore the history for this resource

			m_cHistoryHit++;

			pic->m_tickIndex = tickNil;
			for ( int K = 1; K <= m_Kmax; K++ )
				{
				pic->m_rgtick[ K - 1 ] = he.m_phist->m_rgtick[ K - 1 ];
				}
			pic->m_tickLast = he.m_phist->m_rgtick[ 1 - 1 ];

			//  touch this resource

			TouchResource( _PresFromPic( pic ), tickNow );
			}
		}
	m_KeyHistory.ReadUnlockKey( &lockHist );
	}

//  stores the touch history for a resource

template< int m_Kmax, class CResource, PfnOffsetOf OffsetOfIC, class CKey >
inline void CLRUKResourceUtilityManager< m_Kmax, CResource, OffsetOfIC, CKey >::
_StoreHistory( const CKey& key, CInvasiveContext* const pic )
	{
	CHistoryLRUK::ERR	errHLRUK	= CHistoryLRUK::errSuccess;
	CHistory*			phist		= NULL;

	//  keep trying to store our history until we either succeed or fail with
	//  out of memory.  commonly, this will succeed immediately but in rare cases
	//  we have to loop to resolve an errKeyRangeExceeded in the history LRUK

	do
		{
		//  determine the range of Kth touch times over which retained history
		//  is still valid

		const TICK	tickNow			= _TickCurrentTime();
		const TICK	tickKHistoryMax	= tickNow + ( m_K - 1 ) * m_ctickTimeout;
		const TICK	tickKHistoryMin	= tickNow - m_ctickTimeout;
		
		//  only store the history for this resource if it is not too young or too
		//  old.  history records that are too young or too old will not help us
		//  anyway
		//
		//  NOTE:  the only time we will ever see a history record that is too young
		//  is when a resource is evicted that hasn't been touched for so long that
		//  we have wrapped-around
		
		if (	m_HistoryLRUK.CmpKey( tickKHistoryMin, pic->m_rgtick[ m_K - 1 ] ) <= 0 &&
				m_HistoryLRUK.CmpKey( pic->m_rgtick[ m_K - 1 ], tickKHistoryMax ) < 0 )
			{
			//  we allocated a history record

			if ( phist = _PhistAllocHistory() )
				{
				//  save the history for this resource

				phist->m_key = key;
				
				for ( int K = 1; K <= m_Kmax; K++ )
					{
					phist->m_rgtick[ K - 1 ] = pic->m_rgtick[ K - 1 ];
					}

				//  we failed to insert the history in the history table

				if ( ( errHLRUK = _ErrInsertHistory( phist ) ) != CHistoryLRUK::errSuccess )
					{
					//  free the history record

					delete phist;
					}
				}

			//  we failed to allocate a history record

			else
				{
				//  set an out of memory condition

				errHLRUK = CHistoryLRUK::errOutOfMemory;
				}
			}

		//  we have decided that the history for this resource is not useful

		else
			{
			//  claim success but do not store the history

			errHLRUK = CHistoryLRUK::errSuccess;
			}
		}
	while (	errHLRUK == CHistoryLRUK::errKeyRangeExceeded );
	}

//  allocates a new history record to store the touch history for a resource.
//  this history record can either be newly allocated or recycled from the
//  history table

template< int m_Kmax, class CResource, PfnOffsetOf OffsetOfIC, class CKey >
inline CLRUKResourceUtilityManager< m_Kmax, CResource, OffsetOfIC, CKey >::CHistory* CLRUKResourceUtilityManager< m_Kmax, CResource, OffsetOfIC, CKey >::
_PhistAllocHistory()
	{
	CHistoryLRUK::ERR		errHLRUK;
	CHistoryLRUK::CLock		lockHLRUK;

	CHistory*				phist;
	
	CHistoryTable::ERR		errHist;
	CHistoryTable::CLock	lockHist;

	//  determine the range of Kth touch times over which retained history is
	//  still valid

	const TICK	tickNow			= _TickCurrentTime();
	const TICK	tickKHistoryMax	= tickNow + ( m_K - 1 ) * m_ctickTimeout;
	const TICK	tickKHistoryMin	= tickNow - m_ctickTimeout;
	
	//  get the first history in the history LRUK, if any
	
	m_HistoryLRUK.MoveBeforeFirst( &lockHLRUK );
	if ( ( errHLRUK = m_HistoryLRUK.ErrMoveNext( &lockHLRUK ) ) == CHistoryLRUK::errSuccess )
		{
		//  if this history is no longer valid, recycle it
		
		errHLRUK = m_HistoryLRUK.ErrRetrieveEntry( &lockHLRUK, &phist );
		RESMGRAssert( errHLRUK == CHistoryLRUK::errSuccess );

		if (	m_HistoryLRUK.CmpKey( phist->m_rgtick[ m_K - 1 ], tickKHistoryMin ) < 0 ||
				m_HistoryLRUK.CmpKey( tickKHistoryMax, phist->m_rgtick[ m_K - 1 ] ) <= 0 )
			{
			//  remove this history from the history LRUK

			errHLRUK = m_HistoryLRUK.ErrDeleteEntry( &lockHLRUK );
			RESMGRAssert( errHLRUK == CHistoryLRUK::errSuccess );

			AtomicDecrement( (long*)&m_cHistoryRecord );

			m_HistoryLRUK.UnlockKeyPtr( &lockHLRUK );

			//  try to remove this history from the history table, but don't
			//  worry about it if it is not there
			//
			//  NOTE:  the history record must exist until we unlock its key
			//  in the history table after we delete its entry so that some
			//  other thread can't touch a deleted history record

			m_KeyHistory.WriteLockKey( phist->m_key, &lockHist );
			
			errHist = m_KeyHistory.ErrDeleteEntry( &lockHist );
			RESMGRAssert(	errHist == CHistoryTable::errSuccess ||
					errHist == CHistoryTable::errNoCurrentEntry );

			m_KeyHistory.WriteUnlockKey( &lockHist );

			//  return this history

			return phist;
			}
		}
	RESMGRAssert(	errHLRUK == CHistoryLRUK::errSuccess ||
			errHLRUK == CHistoryLRUK::errNoCurrentEntry );
	m_HistoryLRUK.UnlockKeyPtr( &lockHLRUK );
	
	//  get the last history in the history LRUK, if any
	
	m_HistoryLRUK.MoveAfterLast( &lockHLRUK );
	if ( ( errHLRUK = m_HistoryLRUK.ErrMovePrev( &lockHLRUK ) ) == CHistoryLRUK::errSuccess )
		{
		//  if this history is no longer valid, recycle it
		
		errHLRUK = m_HistoryLRUK.ErrRetrieveEntry( &lockHLRUK, &phist );
		RESMGRAssert( errHLRUK == CHistoryLRUK::errSuccess );

		if (	m_HistoryLRUK.CmpKey( phist->m_rgtick[ m_K - 1 ], tickKHistoryMin ) < 0 ||
				m_HistoryLRUK.CmpKey( tickKHistoryMax, phist->m_rgtick[ m_K - 1 ] ) <= 0 )
			{
			//  remove this history from the history LRUK

			errHLRUK = m_HistoryLRUK.ErrDeleteEntry( &lockHLRUK );
			RESMGRAssert( errHLRUK == CHistoryLRUK::errSuccess );

			AtomicDecrement( (long*)&m_cHistoryRecord );

			m_HistoryLRUK.UnlockKeyPtr( &lockHLRUK );

			//  try to remove this history from the history table, but don't
			//  worry about it if it is not there
			//
			//  NOTE:  the history record must exist until we unlock its key
			//  in the history table after we delete its entry so that some
			//  other thread can't touch a deleted history record

			m_KeyHistory.WriteLockKey( phist->m_key, &lockHist );
			
			errHist = m_KeyHistory.ErrDeleteEntry( &lockHist );
			RESMGRAssert(	errHist == CHistoryTable::errSuccess ||
					errHist == CHistoryTable::errNoCurrentEntry );

			m_KeyHistory.WriteUnlockKey( &lockHist );

			//  return this history

			return phist;
			}
		}
	RESMGRAssert(	errHLRUK == CHistoryLRUK::errSuccess ||
			errHLRUK == CHistoryLRUK::errNoCurrentEntry );
	m_HistoryLRUK.UnlockKeyPtr( &lockHLRUK );

	//  allocate and return a new history because one couldn't be recycled

	return new CHistory;
	}

//  inserts the history record containing the touch history for a resource into
//  the history table

template< int m_Kmax, class CResource, PfnOffsetOf OffsetOfIC, class CKey >
inline CLRUKResourceUtilityManager< m_Kmax, CResource, OffsetOfIC, CKey >::CHistoryLRUK::ERR CLRUKResourceUtilityManager< m_Kmax, CResource, OffsetOfIC, CKey >::
_ErrInsertHistory( CHistory* const phist )
	{
	CHistoryEntry			he;

	CHistoryTable::ERR		errHist;
	CHistoryTable::CLock	lockHist;

	CHistoryLRUK::ERR		errHLRUK;
	CHistoryLRUK::CLock		lockHLRUK;

	//  try to insert our history into the history table

	he.m_KeySignature	= CHistoryTable::CKeyEntry::Hash( phist->m_key );
	he.m_phist			= phist;

	m_KeyHistory.WriteLockKey( phist->m_key, &lockHist );
	if ( ( errHist = m_KeyHistory.ErrInsertEntry( &lockHist, he ) ) != CHistoryTable::errSuccess )
		{
		//  there is already an entry in the history table for this resource

		if ( errHist == CHistoryTable::errKeyDuplicate )
			{
			//  replace this entry with our entry.  note that this will make the
			//  old history that this entry pointed to invisible to the history
			//  table.  this is OK because we will eventually clean it up via the
			//  history LRUK

			errHist = m_KeyHistory.ErrReplaceEntry( &lockHist, he );
			RESMGRAssert( errHist == CHistoryTable::errSuccess );
			}

		//  some other error occurred while inserting this entry in the history table

		else
			{
			RESMGRAssert( errHist == CHistoryTable::errOutOfMemory );
			
			m_KeyHistory.WriteUnlockKey( &lockHist );
			return CHistoryLRUK::errOutOfMemory;
			}
		}
	m_KeyHistory.WriteUnlockKey( &lockHist );

	//  try to insert our history into the history LRUK

	m_HistoryLRUK.LockKeyPtr( phist->m_rgtick[ m_K - 1 ], phist, &lockHLRUK );
	if ( ( errHLRUK = m_HistoryLRUK.ErrInsertEntry( &lockHLRUK, phist ) ) != CHistoryLRUK::errSuccess )
		{
		RESMGRAssert(	errHLRUK == CHistoryLRUK::errOutOfMemory ||
				errHLRUK == CHistoryLRUK::errKeyRangeExceeded );
		
		//  we failed to insert our history into the history LRUK

		m_HistoryLRUK.UnlockKeyPtr( &lockHLRUK );

		m_KeyHistory.WriteLockKey( phist->m_key, &lockHist );
		
		errHist = m_KeyHistory.ErrDeleteEntry( &lockHist );
		RESMGRAssert( errHist == CHistoryTable::errSuccess );

		m_KeyHistory.WriteUnlockKey( &lockHist );
		return errHLRUK;
		}
	m_HistoryLRUK.UnlockKeyPtr( &lockHLRUK );

	//  we have successfully stored our history

	AtomicIncrement( (long*)&m_cHistoryRecord );
	return CHistoryLRUK::errSuccess;
	}

//  converts a pointer to the invasive context to a pointer to a resource

template< int m_Kmax, class CResource, PfnOffsetOf OffsetOfIC, class CKey >
inline CResource* CLRUKResourceUtilityManager< m_Kmax, CResource, OffsetOfIC, CKey >::
_PresFromPic( CInvasiveContext* const pic ) const
	{
	return (CResource*)( (BYTE*)pic - OffsetOfIC() );
	}

//  converts a pointer to a resource to a pointer to the invasive context

template< int m_Kmax, class CResource, PfnOffsetOf OffsetOfIC, class CKey >
inline CLRUKResourceUtilityManager< m_Kmax, CResource, OffsetOfIC, CKey >::CInvasiveContext* CLRUKResourceUtilityManager< m_Kmax, CResource, OffsetOfIC, CKey >::
_PicFromPres( CResource* const pres ) const
	{
	return (CInvasiveContext*)( (BYTE*)pres + OffsetOfIC() );
	}

//  returns the current tick count in msec resolution updated every 2 msec with
//  the special lock value 0xFFFFFFFF reserved
	
template< int m_Kmax, class CResource, PfnOffsetOf OffsetOfIC, class CKey >
inline CLRUKResourceUtilityManager< m_Kmax, CResource, OffsetOfIC, CKey >::TICK CLRUKResourceUtilityManager< m_Kmax, CResource, OffsetOfIC, CKey >::
_TickCurrentTime()
	{
	return TICK( DWORD( DwOSTimeGetTickCount() ) & tickMask );
	}

//  performs a wrap-around insensitive comparison of two TICKs

template< int m_Kmax, class CResource, PfnOffsetOf OffsetOfIC, class CKey >
inline long CLRUKResourceUtilityManager< m_Kmax, CResource, OffsetOfIC, CKey >::
_CmpTick( const TICK tick1, const TICK tick2 ) const
	{
	return long( tick1 - tick2 );
	}


#define DECLARE_LRUK_RESOURCE_UTILITY_MANAGER( m_Kmax, CResource, OffsetOfIC, CKey, Typedef )									\
																																\
typedef CLRUKResourceUtilityManager< m_Kmax, CResource, OffsetOfIC, CKey > Typedef;												\
																																\
DECLARE_APPROXIMATE_INDEX( Typedef::TICK, CResource, Typedef::CInvasiveContext::OffsetOfAIIC, Typedef##__m_aiResourceLRUK );	\
																																\
DECLARE_APPROXIMATE_INDEX( Typedef::TICK, Typedef::CHistory, Typedef::CHistory::OffsetOfAIIC, Typedef##__m_aiHistoryLRU );		\
																																\
inline ULONG_PTR Typedef::CHistoryTable::CKeyEntry::																			\
Hash() const																													\
	{																															\
	return ULONG_PTR( m_entry.m_KeySignature );																					\
	}																															\
																																\
inline BOOL Typedef::CHistoryTable::CKeyEntry::																					\
FEntryMatchesKey( const CKey& key ) const																						\
	{																															\
	return Hash() == Hash( key ) && m_entry.m_phist->m_key == key;																\
	}																															\
																																\
inline void Typedef::CHistoryTable::CKeyEntry::																					\
SetEntry( const Typedef::CHistoryEntry& he )																					\
	{																															\
	m_entry = he;																												\
	}																															\
																																\
inline void Typedef::CHistoryTable::CKeyEntry::																					\
GetEntry( Typedef::CHistoryEntry* const phe ) const																				\
	{																															\
	*phe = m_entry;																												\
	}


};  //  namespace RESMGR


using namespace RESMGR;


#endif  //  _RESMGR_HXX_INCLUDED