#define _REENTRANT

#include <gpk_channel.h>
#include <stdio.h>
#include <assert.h>

generic_channel::generic_channel()
: sz(0), data(0)
{
 mutex_init(&mutex, USYNC_THREAD, 0);
 cond_init(&data_available, USYNC_THREAD, 0);
 cond_init(&data_empty, USYNC_THREAD, 0);
}

generic_channel::~generic_channel()
{
 /* Try to shut down all users. */
 /* this will catch any that are waiting,
	but will miss threads that are off doing something
	else, and begin to use the channel after things are
	destroyed. */

 int pass = 0;
 int pri = 0;
 thr_getprio(thr_self(), &pri);
 thr_setprio(thr_self(), 0);
 while(sz!=(-1) || pass++<10)	{
	mutex_lock(&mutex);
 	sz = -1;
 	cond_broadcast(&data_empty);
 	cond_broadcast(&data_available);
	mutex_unlock(&mutex);
	thr_yield();
	}
 thr_setprio(thr_self(), pri);
 mutex_destroy(&mutex);
 cond_destroy(&data_available);
 cond_destroy(&data_empty);
}

void generic_channel::read(void *p, int sz_)
{
 (void) mutex_lock(&mutex);
 while(!data && sz!=(-1))
	(void)cond_wait(&data_available, &mutex);
 if(sz == -1)	{
	(void)mutex_unlock(&mutex);
	fprintf(stderr, "ERR: read on closed channel\n");
	thr_exit(0);
	}
 assert(sz == sz_);
 memcpy(p, data, sz);
 p = 0;
 sz = 0;
 cond_signal(&data_empty);
 (void)mutex_unlock(&mutex);
}

void generic_channel::write(const void *p, int sz_)
{
 (void) mutex_lock(&mutex);
 while(data && sz!=(-1))
	(void)cond_wait(&data_empty, &mutex);
 if(sz == -1)	{
	(void)mutex_unlock(&mutex);
	fprintf(stderr, "ERR: write on closed channel\n");
	thr_exit(0);
	}
 sz = sz_;
 data = p;
 cond_signal(&data_available);
 (void)mutex_unlock(&mutex);
}
