/***************************************************************************

    supportware
    Copyright (C) 2001-2008 by c.holzheuer
    c.holzheuer@sourceworx.org

    This program is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation; either version 2 of the License, or
    (at your option) any later version.

***************************************************************************/


#include <ddthreadevent.h>

int ddthreadevent::debug = 0;


/*
 * Initialize a event. An event can be of two types:
 * DM1_EVENT_SINGLE - supports signalling and waking one waiting thread
 * DM1_EVENT_BROADCAST - supports signalling and waking up multiple threads.
 */
 
ddthreadevent::ddthreadevent(ddthreadeventType atype)
{
	signaled = false;
	waitersCount = 0;
	this->type = atype;
	int rc = pthread_cond_init(&cond, 0);
	if (rc != 0) {
		//fprintf(stderr, "ddthreadevent.ctor: Error creating ddthreadevent object: errcode = %d\n", rc);
		//throw ddthreadexception(__FILE__, __LINE__, DM1_ERR_EVENT_CREATE, rc);
	}
	
}

/*
 * Reset an event to non-signalled state.
 */

void ddthreadevent::reset()
{
	mutex.lock();
	signaled = false;
	mutex.unlock();
}

/*
 * Wait for a event to be signalled.
 */
void ddthreadevent::wait()
{
	int rc = 0;
	mutex.lock();
	if (signaled) {
		signaled = false;
	}
	else {
		waitersCount++;
		rc = pthread_cond_wait(&cond, mutex.get_real_mutex() );
		waitersCount--;
	}
	mutex.unlock();
	if (rc != 0) {
		//fprintf(stderr, "ddthreadevent.wait: Failed to wait on ddthreadevent: errcode = %d\n", rc);
		//throw ddthreadexception(__FILE__, __LINE__, DM1_ERR_EVENT_WAIT, rc);
	}
}

/**
	Wait for a event to be signalled.
*/

int ddthreadevent::wait(unsigned secs)
{
	struct timespec ts;
	int rc = 0;

	mutex.lock();
	if (signaled) {
		signaled = false;
	}
	else {
		ts.tv_sec = time(0) + secs;					/* Is this portable ? */
		ts.tv_nsec = 0;
		waitersCount++;
		rc = pthread_cond_timedwait(&cond, mutex.get_real_mutex(), &ts);
		waitersCount--;
	}

	mutex.unlock();
	if (rc != 0 && rc != ETIMEDOUT) {
		//fprintf(stderr, "ddthreadevent.wait: Failed to wait on ddthreadevent: errcode = %d\n", rc);
		//throw ddthreadexception(__FILE__, __LINE__, DM1_ERR_EVENT_WAIT, rc);
	}
	return rc;
}

/**
 * Signal a event variable, waking up any waiting thread(s).
 */
void ddthreadevent::notify()
{
	int rc = 0;
	mutex.lock();
	switch (type) {

	case DM1_EVENT_SINGLE:
		if (waitersCount == 0) {
			signaled = true;
		}
		else {
			rc = pthread_cond_signal(&cond);
		}
		break;

	case DM1_EVENT_BROADCAST:
		rc = pthread_cond_broadcast(&cond);
		break;

	}
	mutex.unlock();
	if (rc != 0) {
		fprintf(stderr, "ddthreadevent.notify: Failed to notify ddthreadevent: errcode = %d\n", rc);
		//throw ddthreadexception(__FILE__, __LINE__, DM1_ERR_EVENT_NOTIFY, rc);
	}

}

/*
 * Destroy a event.
 */
ddthreadevent::~ddthreadevent()
{
	if (debug)
		fprintf(stdout, "ddthreadevent.dtor: ddthreadevent destroyed\n");
	int rc;
	while ((rc = pthread_cond_destroy(&cond)) == EBUSY)
		notify();
	if (rc != 0) {
		fprintf(stderr, "ddthreadevent.dtor: ddthreadevent being destroyed is BUSY\n");
	}
}
