/*<html><pre>*/
#include <../source/TAL/objbase.h>
#include <../source/PAL/dataman.h>

/*
 *	Read-write lockable fifo's.
 *	Last update: 04.06.2001
 */

typedef struct rwlocking_fifo_s
{
	libv_object_t base;
	u8_fifo_t	fifo;
	mutex_t		lock;
	cond_t		in_flag;
	u32_t		in_request;
	void		(*push_proc)(struct rwlocking_fifo_s*,void*,u32_t);
	u32_t		(*pop_proc)(struct rwlocking_fifo_s*,void*,u32_t);
} rwlocking_fifo_t, *rwlocking_fifo_p;

#ifdef	CHECKED_BUILD
#define	RWL_MAGIC		0x52574C46		/* RWLF */
V_LOCAL char			*rwl_object = "lockable fifo";
#endif	/* CHECKED_BUILD */

V_LOCAL	u32_t	V_CALL
rwl_destroy(vhandle_t handle)
{
	rwlocking_fifo_p fifo=(rwlocking_fifo_p)handle;
	UNREF_EXIT(fifo);
	mutexLock(fifo->lock);
	if(fifo->in_request)
	{
		/* Unblock pending request; it will fail ... */
		mutexUnlock(fifo->lock);
		rwlBlockMode(fifo,false);
		mutexLock(fifo->lock);
	}
	u8fClear(&fifo->fifo);
	mutexUnlock(fifo->lock);
	mutexDestroy(fifo->lock);
	condDestroy(fifo->in_flag);
	memFreeObject(handle);
	return 0;
}

V_LOCAL	void
rwlPushBlocking(rwlocking_fifo_t *fifo,void *buf,u32_t len)
{
	mutexLock(fifo->lock);
	u8fPushBuffer(&fifo->fifo,(u8_t*)buf,len);
	if(fifo->in_request<=u8fCount(&fifo->fifo))
	{
		fifo->in_request=0;
		condSet(fifo->in_flag);
	}
	mutexUnlock(fifo->lock);
}

V_LOCAL	void
rwlPushNonblocking(rwlocking_fifo_t *fifo,void *buf,u32_t len)
{
	mutexLock(fifo->lock);
	u8fPushBuffer(&fifo->fifo,(u8_t*)buf,len);
	mutexUnlock(fifo->lock);
}

/*
 *	Will block caller if not enought data in FIFO buffer.
 */
V_LOCAL	u32_t
rwlPopBlocking(rwlocking_fifo_t *fifo,void *buf,u32_t len)
{
	u32_t count;
	mutexLock(fifo->lock);
	if(u8fCount(&fifo->fifo)<len)
	{
		fifo->in_request=len;
		mutexUnlock(fifo->lock);
		condWait(fifo->in_flag);
		mutexLock(fifo->lock);
		/* If wait failed, fifo->in_request == len again */
		/* TODO: print('fifo destroyed from another thread !') */
	}
	count=u8fPopBuffer(&fifo->fifo,(u8_t*)buf,len);
	fifo->in_request=0; /* clear request flag anyway */
	mutexUnlock(fifo->lock);
	return count;
}

V_LOCAL	u32_t
rwlPopNonblocking(rwlocking_fifo_t *fifo,void *buf,u32_t len)
{
	u32_t count;
	mutexLock(fifo->lock);
	count=u8fPopBuffer(&fifo->fifo,(u8_t*)buf,len);
	mutexUnlock(fifo->lock);
	return count;
}

void	V_GLOBAL
rwlBlockMode(rwl_fifo_t rwl,vboolean blockable)
{
	rwlocking_fifo_t *fifo=(rwlocking_fifo_t*)
		libvIsHandle(rwl,"rwlBlockMode",rwl_object,RWL_MAGIC);
	if(!fifo)
		return;
	mutexLock(fifo->lock);
	if(blockable)
	{
		/* There can't be pending request */
		fifo->in_request=0;
		fifo->push_proc=rwlPushBlocking;
		fifo->pop_proc=rwlPopBlocking;
	}
	else
	{
		/* Check for pending input request */
		if(fifo->in_request)
		{
#ifdef		CHECKED_BUILD
			conlPuts(LOG_WARNING,"rwlBlockMode(true): unblocked "
				"input request will fail.\n");
#endif		/* CHECKED_BUILD */
		}
		condSet(fifo->in_flag);
		/* Thread which is waiting in rwlPopBlocking() will be released
		 * immediately after unlocking fifo->lock; we wait here for 
		 * completion of rwlPopBlocking() */
		mutexUnlock(fifo->lock);
		schedYield();
		mutexLock(fifo->lock);		/* We wait here ... */
		/* Other thread exited from rwlPopBlocking() */
		fifo->push_proc=rwlPushNonblocking;
		fifo->pop_proc=rwlPopNonblocking;
	}
	mutexUnlock(fifo->lock);
}

rwl_fifo_t V_GLOBAL
rwlCreate()
{
	rwlocking_fifo_t *fifo;
	ALLOC_OBJECT(fifo,rwlocking_fifo_t,0,RWL_MAGIC);
	fifo->base.destroy=rwl_destroy;
	u8fInit(&fifo->fifo);
	fifo->lock=mutexCreate();
	fifo->in_flag=condCreate(true); /* auto-reset conditional */
	fifo->in_request=0;
	rwlBlockMode((rwl_fifo_t*)fifo,true);
	return (rwl_fifo_t*)fifo;
}

void	V_GLOBAL
rwlDestroy(rwl_fifo_t rwl)
{
	libvCloseHandle(rwl,"rwlDestroy",rwl_object,RWL_MAGIC);
}

#ifdef	CHECKED_BUILD
V_LOCAL	vboolean
rwlCheckIOArgs(char *caller,void *buf,u32_t len)
{
	if(!buf || !len)
	{
		conlPrintf(LOG_ERROR,"%s([FIFO],%X,%d): incorrect parameter(s).\n",
			caller,buf,len);
		return false;
	}
	return true;
}
#endif	/* !CHECKED_BUILD */

void	V_GLOBAL
rwlPushBuffer(rwl_fifo_t rwl,void *buf,u32_t len)
{
	rwlocking_fifo_t *fifo;
#ifdef	CHECKED_BUILD
	fifo=(rwlocking_fifo_t*)
		libvIsHandle(rwl,"rwlPushBuffer",rwl_object,RWL_MAGIC);
	if(!fifo)
		return;
	if(!rwlCheckIOArgs("rwlPushBuffer",buf,len))
		return;
#else	/* !CHECKED_BUILD */
	fifo=(rwlocking_fifo_t*)rwl;
#endif	/* !CHECKED_BUILD */
	fifo->push_proc(fifo,buf,len);
}

u32_t	V_GLOBAL
rwlPopBuffer(rwl_fifo_t rwl,void *buf,u32_t len)
{
	rwlocking_fifo_t *fifo;
#ifdef	CHECKED_BUILD
	fifo=(rwlocking_fifo_t*)
		libvIsHandle(rwl,"rwlPopBuffer",rwl_object,RWL_MAGIC);
	if(!fifo)
		return 0;
	if(!rwlCheckIOArgs("rwlPopBuffer",buf,len))
		return 0;
#else	/* !CHECKED_BUILD */
	fifo=(rwlocking_fifo_t*)rwl;
#endif	/* !CHECKED_BUILD */
	return fifo->pop_proc(fifo,buf,len);
}

u32_t	V_GLOBAL
rwlCount(rwl_fifo_t rwl)
{
	u32_t count;
	rwlocking_fifo_t *fifo;
#ifdef	CHECKED_BUILD
	fifo=(rwlocking_fifo_t*)
		libvIsHandle(rwl,"rwlCount",rwl_object,RWL_MAGIC);
	if(!fifo)
		return 0;
#else	/* !CHECKED_BUILD */
	fifo=(rwlocking_fifo_t*)rwl;
#endif	/* !CHECKED_BUILD */
	mutexLock(fifo->lock);
	count=u8fCount(&fifo->fifo);
	mutexUnlock(fifo->lock);
	return count;
}

/*</pre></html>*/
/*EOF*/
