/*==========================================================================
 *
 *  Copyright (C) 1995 - 2000 Microsoft Corporation.  All Rights Reserved.
 *
 *  File:       ContextCFPM.h
 *  Content:	fixed pool manager for classes that takes into account contexts
 *
 *  History:
 *   Date		By		Reason
 *   ======		==		======
 *  12-18-97	aarono	Original
 *	11-06-98	ejs		Add custom handler for Release function
 *	04-12-99	jtk		Trimmed unused functions and parameters, added size assert
 *	01-31-2000	jtk		Added code to check for items already being in the pool on Release().
 *	02-08-2000	jtk		Derived from ClassFPM.h
***************************************************************************/

#ifndef __CONTEXT_CLASS_FPM_H__
#define __CONTEXT_CLASS_FPM_H__

#undef DPF_SUBCOMP
#define DPF_SUBCOMP DN_SUBCOMP_COMMON

//**********************************************************************
// Constant definitions
//**********************************************************************

#ifdef	_WIN64
#define	BLANK_NODE_VALUE	0xAA55817E6D5C4B3A
#else	// _WIN64
#define	BLANK_NODE_VALUE	0xAA55817E
#endif	// _WIN64

#define	CHECK_FOR_DUPLICATE_LOCKEDCCFPM_RELEASE

//**********************************************************************
// Macro definitions
//**********************************************************************

#ifndef	OFFSETOF
// Macro to compute the offset of an element inside of a larger structure (copied from MSDEV's STDLIB.H)
#define OFFSETOF(s,m)	( (INT_PTR) &(((s *)0)->m) )
#define	__LOCAL_OFFSETOF_DEFINED__
#endif	// OFFSETOF

//**********************************************************************
// Structure definitions
//**********************************************************************

//**********************************************************************
// Variable definitions
//**********************************************************************

//**********************************************************************
// Function prototypes
//**********************************************************************

//**********************************************************************
// Class definitions
//**********************************************************************

// class to act as a link in the pool
template< class T >
class	CLockedContextClassFPMPoolNode
{
	public:
		CLockedContextClassFPMPoolNode() { m_pNext = NULL; }
		~CLockedContextClassFPMPoolNode() {};

		CLockedContextClassFPMPoolNode	*m_pNext;
		void	*m_pContext;
		T		m_Item;

	protected:
	private:
};

// class to manage the pool
template< class T >
class	CLockedContextClassFixedPool
{
	public:
		CLockedContextClassFixedPool();
		~CLockedContextClassFixedPool();


typedef BOOL (T::*PBOOLCALLBACK)( void *const pContext );
typedef void (T::*PVOIDCALLBACK)( void *const pContext );

		BOOL	Initialize( PBOOLCALLBACK pAllocFunction,
							PBOOLCALLBACK pInitFunction,
							PVOIDCALLBACK pReleaseFunction,
							PVOIDCALLBACK pDeallocFunction );

		void	Deinitialize( void );

		T		*Get( void *const pContext );
		void	Release( T *const pItem );

	protected:

	private:
		DNCRITICAL_SECTION	m_Lock;

		PBOOLCALLBACK	m_pAllocFunction;
		PBOOLCALLBACK	m_pInitFunction;
		PVOIDCALLBACK	m_pReleaseFunction;
		PVOIDCALLBACK	m_pDeallocFunction;

		CLockedContextClassFPMPoolNode< T >	*volatile m_pPool;		// pointer to list of available elements

		BOOL			m_fInitialized;					// Initialized ?

		DEBUG_ONLY( LONG	volatile m_lOutstandingItemCount );
};

//**********************************************************************
// Class function definitions
//**********************************************************************


//**********************************************************************
// ------------------------------
// CLockedContextClassFixedPool::CLockedContextClassFixedPool - constructor
//
// Entry:		Nothing
//
// Exit:		Nothing
// ------------------------------
template< class T >
CLockedContextClassFixedPool< T >::CLockedContextClassFixedPool():
	m_pAllocFunction( NULL ),
	m_pInitFunction( NULL ),
	m_pReleaseFunction( NULL ),
	m_pDeallocFunction( NULL ),
	m_pPool( NULL ),
	m_fInitialized( FALSE )
{
	DEBUG_ONLY( m_lOutstandingItemCount = 0 );
}
//**********************************************************************


//**********************************************************************
// ------------------------------
// CLockedContextClassFixedPool::~CLockedContextClassFixedPool - destructor
//
// Entry:		Nothing
//
// Exit:		Nothing
// ------------------------------
#undef DPF_MODNAME
#define DPF_MODNAME "CCFPM::CCFPM"

template< class T >
CLockedContextClassFixedPool< T >::~CLockedContextClassFixedPool()
{
	DEBUG_ONLY( DNASSERT( m_lOutstandingItemCount == 0 ) );
	DEBUG_ONLY( DNASSERT( m_fInitialized == FALSE ) );
}
//**********************************************************************


//**********************************************************************
// ------------------------------
// CLockedContextClassFixedPool::Initialize - initialize pool
//
// Entry:		Pointer to function to call when a new entry is allocated
//				Pointer to function to call when a new entry is removed from the pool
//				Pointer to function to call when an entry is returned to the pool
//				Pointer to function to call when an entry is deallocated
//
// Exit:		Nothing
// ------------------------------
#undef DPF_MODNAME
#define DPF_MODNAME "CCFPM::Initialize"

template< class T >
BOOL	CLockedContextClassFixedPool< T >::Initialize( PBOOLCALLBACK pAllocFunction, PBOOLCALLBACK pInitFunction, PVOIDCALLBACK pReleaseFunction, PVOIDCALLBACK pDeallocFunction )
{
	BOOL	fReturn;

	DNASSERT( m_fInitialized == FALSE );

	DNASSERT( pAllocFunction != NULL );
	DNASSERT( pInitFunction != NULL );
	DNASSERT( pReleaseFunction != NULL );
	DNASSERT( pDeallocFunction != NULL );

	fReturn = TRUE;

	if ( DNInitializeCriticalSection( &m_Lock ) == FALSE )
	{
		fReturn = FALSE;
		goto Exit;
	}

	m_pAllocFunction = pAllocFunction;
	m_pInitFunction = pInitFunction;
	m_pReleaseFunction = pReleaseFunction;
	m_pDeallocFunction = pDeallocFunction;

	m_fInitialized = TRUE;

Exit:
	return	fReturn;
}
//**********************************************************************


//**********************************************************************
// ------------------------------
// CLockedContextClassFixedPool::Deinitialize - deinitialize pool
//
// Entry:		Nothing
//
// Exit:		Nothing
// ------------------------------
#undef DPF_MODNAME
#define DPF_MODNAME "CCFPM::Deinitialize"

template< class T >
void	CLockedContextClassFixedPool< T >::Deinitialize( void )
{
	DNASSERT( m_fInitialized == TRUE );

	DNEnterCriticalSection(&m_Lock);
	DEBUG_ONLY( DNASSERT( m_lOutstandingItemCount == 0 ) );
	while ( m_pPool != NULL )
	{
		CLockedContextClassFPMPoolNode< T >	*pNode;

		pNode = m_pPool;
		m_pPool = m_pPool->m_pNext;
		(pNode->m_Item.*this->m_pDeallocFunction)( pNode->m_pContext );
		delete	pNode;
	}
	DNLeaveCriticalSection(&m_Lock);

	DNDeleteCriticalSection(&m_Lock);

	m_fInitialized = FALSE;
}
//**********************************************************************


//**********************************************************************
// ------------------------------
// CLockedContextClassFixedPool::Get - get an item from the pool
//
// Entry:		Pointer to user context
//
// Exit:		Pointer to item
//				NULL = out of memory
// ------------------------------
#undef DPF_MODNAME
#define DPF_MODNAME "CCFPM::Get"

template< class T >
T	*CLockedContextClassFixedPool< T >::Get( void *const pContext )
{
	CLockedContextClassFPMPoolNode< T >	*pNode;
	T	*pReturn;


	DEBUG_ONLY( DNASSERT( m_fInitialized != FALSE ) );

	//
	// initialize
	//
	pReturn = NULL;

	DNEnterCriticalSection(&m_Lock);

	//
	// if the pool is empty, try to allocate a new entry, otherwise grab
	// the first item from the pool
	//
	if ( m_pPool == NULL )
	{
		DNLeaveCriticalSection(&m_Lock);
		pNode = new CLockedContextClassFPMPoolNode< T >;
		if ( pNode != NULL )
		{
			if ( (pNode->m_Item.*this->m_pAllocFunction)( pContext ) == FALSE )
			{
				delete pNode;
				pNode = NULL;
			}
		}
	}
	else
	{
		pNode = m_pPool;
		m_pPool = m_pPool->m_pNext;
		DNLeaveCriticalSection(&m_Lock);
	}


	//
	// if we have an entry (it was freshly created, or removed from the pool),
	// attempt to initialize it before passing it to the user
	//
	if ( pNode != NULL )
	{
		if ( (pNode->m_Item.*this->m_pInitFunction)( pContext ) == FALSE )
		{
			DNEnterCriticalSection(&m_Lock);

			pNode->m_pNext = m_pPool;
			m_pPool = pNode;

			DNLeaveCriticalSection(&m_Lock);

			pNode = NULL;
		}
		else
		{
			pNode->m_pContext = pContext;
			pReturn = &pNode->m_Item;

			DEBUG_ONLY( pNode->m_pNext = (CLockedContextClassFPMPoolNode<T>*) BLANK_NODE_VALUE );
			DEBUG_ONLY( InterlockedIncrement(const_cast<LONG*>(&m_lOutstandingItemCount)) );
		}
	}

	return	 pReturn;
}
//**********************************************************************


//**********************************************************************
// ------------------------------
// CLockedContextClassFixedPool::Release - return item to pool
//
// Entry:		Pointer to item
//
// Exit:		Nothing
// ------------------------------
#undef DPF_MODNAME
#define DPF_MODNAME "CCFPM::Release"

template< class T >
void	CLockedContextClassFixedPool< T >::Release( T *const pItem )
{
	CLockedContextClassFPMPoolNode< T >	*pNode;


	DEBUG_ONLY( DNASSERT( m_fInitialized != FALSE ) );
	DNASSERT( pItem != NULL );
	DBG_CASSERT( sizeof( BYTE* ) == sizeof( pItem ) );
	DBG_CASSERT( sizeof( CLockedContextClassFPMPoolNode< T >* ) == sizeof( BYTE* ) );
	pNode = reinterpret_cast<CLockedContextClassFPMPoolNode< T >*>( &reinterpret_cast<BYTE*>( pItem )[ -OFFSETOF( CLockedContextClassFPMPoolNode< T >, m_Item ) ] );

	DEBUG_ONLY( DNASSERT( pNode->m_pNext == (CLockedContextClassFPMPoolNode< T >*)BLANK_NODE_VALUE ) );
	(pNode->m_Item.*this->m_pReleaseFunction)( pNode->m_pContext );
	DNEnterCriticalSection(&m_Lock);

#if defined(CHECK_FOR_DUPLICATE_LOCKEDCCFPM_RELEASE) && defined(DEBUG)
	{
		CLockedContextClassFPMPoolNode< T >	*pTemp;


		pTemp = m_pPool;
		while ( pTemp != NULL )
		{
			DNASSERT( pTemp != pNode );
			pTemp = pTemp->m_pNext;
		}
	}
#endif	// CHECK_FOR_DUPLICATE_LOCKEDCCFPM_RELEASE

#ifdef NO_POOLS
	(pNode->m_Item.*this->m_pDeallocFunction)( pNode->m_pContext );
	delete pNode;
#else
	pNode->m_pNext = m_pPool;
	m_pPool = pNode;
#endif

	DEBUG_ONLY( InterlockedDecrement(const_cast<LONG*>(&m_lOutstandingItemCount)) );
	DNLeaveCriticalSection(&m_Lock);
}
//**********************************************************************

#ifdef	__LOCAL_OFFSETOF_DEFINED__
#undef	__LOCAL_OFFSETOF_DEFINED__
#undef	OFFSETOF
#endif	// __LOCAL_OFFSETOF_DEFINED__

#undef DPF_MODNAME
#undef DPF_SUBCOMP

#endif	// __CONTEXT_CLASS_FPM_H__