/*++

Cacheint.h

	This file defines all of the classes required to support
	the generic cache manager, who's interface is defined in
	cache.h



--*/

#ifndef	_CACHEINT_H_
#define	_CACHEINT_H_

#include	<windows.h>
#include	"dbgtrace.h"
#include	"smartptr.h"
//#include	"refptr2.h"
#include	"fhashex.h"

#ifdef	_USE_RWNH_	
#include	"rwnew.h"
typedef	CShareLockNH	_CACHELOCK ;
#else
#include	"rw.h"
typedef	CShareLock		_CACHELOCK ;
#endif

//
//	This class defines all the information we keep about 
//	objects that we are holding in our cache.
//
//
class	CacheState	{
protected : 

	//
	//	Doubly linked list of CacheEntry BLOCKS !
	//
	class	CacheState*	m_pPrev ;
	class	CacheState*	m_pNext ;

	//
	//	Cacge State Information - Time To Live and Orphanage status !
	//
	long				m_TTL ;
	BOOL				m_fOrphan ;

	//
	//	Insert us into the list after the specified element !
	//
	void
	InsertAfter( 
			class	CacheState*	pPrevious 
			) ;

	//
	//	Insert us into the circular list before the specified element !
	//
	void
	InsertBefore( 
			class	CacheState*	pNext 
			)	;

	//
	//	Remove this element from the circular list !
	//
	void
	Remove() ;

	//
	//	The CacheList object maintains a list of these objects and ages them 
	//	out !
	//
	friend	class	CacheList ;

public : 

	//
	//	Default Constructor NULLS everything out !
	//
	CacheState( DWORD	ttl = 0 )	: 
	   m_pPrev( 0 ), m_pNext( 0 ), m_TTL( ttl ), m_fOrphan( FALSE ) { 
#ifdef	DEBUG
		InterlockedIncrement( &s_cCreated ) ;
#endif
	}

	//
	//	Copy Constructor relinks linked list ! we place ourselves in the linked list
	//	after the rhs element.
	//
	CacheState( CacheState& rhs ) : m_pPrev( 0 ), 
			m_pNext( 0 ), 
			m_TTL( rhs.m_TTL ), 
			m_fOrphan( rhs.m_fOrphan )  {

		if( rhs.m_pPrev != 0 ) 
			InsertAfter( &rhs ) ;
#ifdef	DEBUG
		InterlockedIncrement( &s_cCreated ) ;
#endif
	}

	//
	//	If we are in a linked list - the default destructor needs to remove us
	//	from that list.
	//
	~CacheState() ;

	//
	//	Need a virtual function which we use to figure out when we can 
	//	safely remove an entry from the Cache !!
	//
	virtual	BOOL	fDeletable() {	return	TRUE ; } 	

#ifdef	DEBUG

	static	long	s_cCreated ;

#endif

} ;

//
//	This class defines a couple of virtual functions 
//	which are to be called from CacheList objects.
//
class	CacheTable	{
protected :

	friend	class	CacheList ;

	//
	//	Remove an entry from the Cache - return TRUE if 
	//	successful and pEntry destroyed.
	//
	virtual	BOOL	RemoveEntry( 
							CacheState*	pEntry 
							) ;

	//
	//	Called to determine whether we want to remove a 
	//	specified entry from the cache.
	//
	virtual	BOOL	QueryRemoveEntry(	
							CacheState*	pEntry 
							) ;

} ;

class	CacheList	{
private : 

	//
	//	Keep this element around so we can keep a doubly linked list
	//	of the elements in the Cache List.
	//
	CacheState	m_Head ;

	//
	//	Make this private - nobody gets to copy us !
	//
	CacheList( CacheList& ) ;	

protected : 


	//
	//	Number of Elements in the Cache !
	//
	long		m_cEntries ;

	//	
	//	Number of elements (approximately) ready to be removed from 
	//	the cache !
	//
	long		m_cReadyToDie ;

public : 

	//
	//	Maximum number of Elements in the List !
	//
	long		m_cMax ;

	//
	// Stop hint function to be called during long shutdown loops.
	//
	PSTOPHINT_FN m_pfnStopHint;

	//
	//	Initialize the Cache list !
	//
	CacheList(	long	Max = 128 ) ;

#ifdef	DEBUG
	//
	//	In debug builds the destructor helps track how many of these
	//	are created !!
	//
	~CacheList() ;
#endif

	//
	//	Append an Entry into the CacheList.  We Append as this element 
	//	should have the largest Age and most likely to be the last element expired !
	//
	//	Returns TRUE if the number of entries is larger than our pre-ordained Maximum !
	//
	BOOL
	Append(		const	CacheState*		pEntry ) ;


	//
	//	Remove an arbitrary Entry from the CacheList !
	//
	//	Returns TRUE if the number of remaining entries is larger than our pre-ordained Maximum !
	//
	BOOL
	Remove(		const	CacheState*		pEntry ) ;

	//
	//	Walk the list and Expire any entries in there !
	//	This means we decrement TTL's and count up the number of 
	//	entries in the list we could drop right now !
	//
	BOOL
	Expire(	DWORD&	cReady ) ;

	//
	//	Go find those elements who's TTL has dropped below 0 and 
	//	get rid of them from the cache !!
	//
	//	NOTE - if fDoCheap is set to TRUE then we use m_cReadyToDie
	//	to figure out if there's any work we can do !
	//
	BOOL
	Expunge(	CacheTable*	ptable,
				BOOL		fDoCheap = FALSE,	
				const	CacheState*	pProtected = 0 
				) ;

	//
	//	Go remove a single element who's TTL haven't dropped below 0
	//	and get rid of it from the Cache !
	//
	BOOL
	ForceExpunge(	CacheTable*	ptable, 
					const	CacheState*	pProtected = 0
					) ;

	//
	//	Go find those elements who's TTL has dropped below 0 and 
	//	get rid of them from the cache !!
	//
	BOOL
	ExpungeSpecific(	CacheTable*	ptable,	
						BOOL fForced  
						) ;

	//
	//	Bump the life time of the specified object up, 
	//	as somebody is re-using it from the cache !
	//
	void
	LiveLonger(		CacheState*	pEntry, 
					long		ttl
					) ;

#ifdef	DEBUG

	static	long	s_cCreated ;

#endif

} ;

	



//
//	This template class adds one member to the 'CacheState' class, 
//	which is the Reference counting pointer to the block of data we
//	are keeping in the Cache !  
//	In addition - we let the user specify whether Cache Blocks must be
//	atomic.  An atomic Cache block means we must never remove an element
//	from the Cache for which somebody external has a reference !
//	(ie  if fAtomic == TRUE then we only remove elements from the cache
//	when the reference count on the Data is zero meaning that we have the 
//	last reference !!!
//
//
template<	class	Data,	class	Key,	class   KEYREF, BOOL	fAtomic = TRUE >	
class	CacheEntry : public	CacheState		{
public : 

	CRefPtr< Data >		m_pData ;

	CacheEntry*		m_pNext ;
	//
	//	Default constructor just passes on TTL data to CacheState object
	//
	CacheEntry( DWORD ttl=0 ) : CacheState( ttl ), m_pNext(0) {}

	//
	//	We do not declare a copy constructor as the default 
	//	compiler provided copy constructor which invokes 
	//	base class and member variable copy constructors works fine !
	//
	CacheEntry( CacheEntry& rhs ) :
        m_pData( rhs.m_pData ),
        m_pNext( rhs.m_pNext ),
        CacheState( *((CacheState *) &rhs) ) {
    }

	//
	//	We do not declare a destructor as the default compiler 
	//	provided destructor works fine !
	//
	//	~CacheEntry() ;

	//
	//	Return a reference to the Key data from the data portion !
	//
	KEYREF	GetKey()	{
		KEYREF  key;
        m_pData->GetKey(&key) ;
        return key;
	}

	//
	//	Return 0 if the provided Key matches that contained in the 
	//	data block !
	//
	int		MatchKey( KEYREF	key	)	{
		return	m_pData->MatchKey( key ) ;
	}

	//
	//	Determine whether we can remove the Data represented by this 
	//	entry from the Cache.  'fAtomic' == TRUE means that we should
	//	only release entries from the cache when the cache is the 
	//	only component holding a reference to the entry.
	//	This ensures that two pieces of 'Data' which are located 
	//	with the same 'Key' never exist at the same time.
	//
	BOOL	fDeleteable()	{
		if( !fAtomic ) {
			return	TRUE ;
		}
		return	m_pData->m_refs == 0 ;
	}

} ;



//
//	This is a base class for those objects which wish to be
//	called on a regularily scheduled basis.
//
//	The constructors for this class will automatically
//	put the object in a doubly linked list walked by a 
//	background thread which periodically executes a virtual function.
//
//
class	CScheduleThread {
private : 

	//
	//	Special constructor for the 'Head' element of the 
	//	doubly linked list this class maintains.
	//
	CScheduleThread( BOOL	fSpecial ) ;
	
protected : 

	//
	//	Has the scheduler been initialized ? 
	//
	static	BOOL			s_fInitialized ;

	//
	//	Crit sect protecting doubly linked list.
	//
	static	CRITICAL_SECTION	s_critScheduleList ;

	//
	//	Handle to event used to terminate background thread.
	//
	static	HANDLE			s_hShutdown ;

	//
	//	Handle to background thread.
	//
	static	HANDLE			s_hThread ;

	//
	//	The head element of the doubly linked list.
	//
	static	CScheduleThread		s_Head ;

	//
	//	The thread which calls our virtual functions
	//
	static	DWORD	WINAPI	ScheduleThread(	LPVOID	lpv ) ;

	//
	//	Previous and Next pointers which maintain doubly linked
	//	list of scheduled itesm.
	//
	class	CScheduleThread*	m_pPrev ;
	class	CScheduleThread*	m_pNext ;

protected : 

	//
	//	Derived classes should override this function - 
	//	it will be called on a regular basis by the scheduler thread.
	//
	virtual	void	Schedule( void ) {}

	//
	//	Constructor and Destructor automagically manage
	//	insertion into doubly linked list of other scheduled items.
	//	These are protected as we want people to buid only 
	//	derived objects which use this.
	//
	CScheduleThread() ; 

	//
	//	Member functions which put us into the regular schedule !
	//
	void	AddToSchedule() ;
	void	RemoveFromSchedule() ;
		
public : 

	//
	//	Initialize the class - don't construct
	//	any derived objects before this is called.
	//
	static	BOOL	Init() ;

	//
	//	Terminate class and background thread !
	//
	static	void	Term() ;

	//
	//	Global which tells clients how frequently they
	//	will be called !	
	//
	static	DWORD	dwNotificationSeconds ;

	//
	//	Destructor is protected - we should only be invoked
	//	by derived class constructors
	//
	~CScheduleThread() ;

} ;
		




#endif