 /*==========================================================================
 *
 *  Copyright (C) 1995 - 1997 Microsoft Corporation.  All Rights Reserved.
 *
 *  File:       fpm.c
 *  Content:	fixed size pool manager
 *
 *  History:
 *   Date		By		Reason
 *   ======		==		======
 *  12-18-97  aarono    Original
 ***************************************************************************/

#include "windows.h"
#include "dpsp.h"
#include "fpm.h"

#ifdef SENDEX

BOOL FN_BOOL_DUMMY(void *pvItem)
{
	return TRUE;
}

VOID FN_VOID_DUMMY(void *pvItem)
{
	return;
}

void * FPM_Get(LPFPOOL this)
{
	void * pvItem;

	EnterCriticalSection(&this->cs);
	
	if(!this->pPool){
	
		LeaveCriticalSection(&this->cs);
		pvItem = GlobalAlloc(GPTR, this->cbItemSize);

		if((pvItem) && !(*this->fnBlockInitAlloc)(pvItem) ){
			GlobalFree(pvItem);
			pvItem=NULL;
		}

		EnterCriticalSection(&this->cs);

		if(pvItem){	
			this->nAllocated++;
		}
		
	} else {
		pvItem=this->pPool;
		this->pPool=*((void **)pvItem);
	}

	if(pvItem){
	
		(*this->fnBlockInit)(pvItem);
		
		this->nInUse++;
		if(this->nInUse > this->nMaxInUse){
			this->nMaxInUse = this->nInUse;
		}
	}

	LeaveCriticalSection(&this->cs);

	return pvItem;
}

#ifdef DEBUG
void DebugCheckList(void *pvList, void *pvItem)
{
	void *pvWalker;
	DWORD n=0;
	pvWalker=pvList;

	while(pvWalker){
		if(pvWalker==pvItem){
			DPF(0,"ERROR: Found Item %x in List %x, item # %d\n",pvList,pvItem,n);
			DEBUG_BREAK();
		}
		n++;
		pvWalker=*((void **)pvWalker);
	}
}
#else
#define DebugCheckList()
#endif

void FPM_Release(LPFPOOL this, void *pvItem)
{
	EnterCriticalSection(&this->cs);
	DebugCheckList(this->pPool, pvItem); //BUGBUG: debug only.
	this->nInUse--;
	*((void**)pvItem)=this->pPool;
	this->pPool=pvItem;
	LeaveCriticalSection(&this->cs);
	
}

void FPM_Scale(LPFPOOL this)
{
	void * pvItem;

	if(!InterlockedExchange(&this->bInScale,1)){

		EnterCriticalSection(&this->cs);

		while((this->nAllocated > this->nMaxInUse) && this->pPool){
			pvItem = this->pPool;
			this->pPool=*((void **)pvItem);
			LeaveCriticalSection(&this->cs);
			(*this->fnBlockFini)(pvItem);
			GlobalFree(pvItem);
			EnterCriticalSection(&this->cs);
			this->nAllocated--;
		}
		
		this->nMaxInUse=this->nInUse;

		LeaveCriticalSection(&this->cs);

		InterlockedExchange(&this->bInScale,0);
	}
}

VOID FPM_Fini(LPFPOOL this, int bFORCE)
{
	void *pvItem;

	while(this->pPool){
		pvItem = this->pPool;
		this->pPool=*((void **)pvItem);
		(*this->fnBlockFini)(pvItem);
		GlobalFree(pvItem);
		this->nAllocated--;
	}
	if(this->nAllocated){
		DPF(0,"WSOCK: Exiting with unfreed FPM pool items\n");
	}
	DeleteCriticalSection(&this->cs);
	GlobalFree(this);
}

LPFPOOL FPM_Init(
	unsigned int size, 
	FN_BLOCKINITALLOC fnBlockInitAlloc,
	FN_BLOCKINIT      fnBlockInit, 
	FN_BLOCKFINI      fnBlockFini)
{
	LPFPOOL pPool;
	
	if(!(pPool=(LPFPOOL)GlobalAlloc(GPTR,sizeof(FPOOL))))
	{
	  return NULL;
	}

	InitializeCriticalSection(&pPool->cs);
	
	// by zero init.
	//pPool.pPool      = NULL;
	//pPool.nAllocated = 0;
	//pPool.nInUse     = 0;
	//pPool.nMaxInUse  = 0;
	//pPool.bInScale   = FALSE;

	if(fnBlockInitAlloc){
		pPool->fnBlockInitAlloc = fnBlockInitAlloc;
	} else {
		pPool->fnBlockInitAlloc = FN_BOOL_DUMMY;
	}
	if(fnBlockInit){
		pPool->fnBlockInit      = fnBlockInit;
	} else {
		pPool->fnBlockInit      = FN_VOID_DUMMY;
	}
	if(fnBlockFini){
		pPool->fnBlockFini      = fnBlockFini;
	} else {
		pPool->fnBlockFini      = FN_VOID_DUMMY;
	}

	pPool->Get    = FPM_Get;
	pPool->Release= FPM_Release;
	pPool->Scale  = FPM_Scale;
	pPool->Fini   = FPM_Fini;

	pPool->cbItemSize = size;
	
	return pPool;
}

#endif
