/* -*- mode: c ; c-file-style: "canonware-c-style" -*-
 ******************************************************************************
 *
 * Copyright (C) 1996-2004 Jason Evans <jasone@canonware.com>.
 * All rights reserved.
 * 
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice(s), this list of conditions and the following disclaimer
 *    unmodified other than the allowable addition of one or more
 *    copyright notices.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice(s), this list of conditions and the following disclaimer in
 *    the documentation and/or other materials provided with the
 *    distribution.
 * 
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDER(S) ``AS IS'' AND ANY
 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
 * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE COPYRIGHT HOLDER(S) BE
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
 * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
 * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
 * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 ******************************************************************************
 *
 * Version: Onyx 5.1.1
 *
 ******************************************************************************/

#include "../include/libonyx/libonyx.h"

#include <sys/time.h>
#include <errno.h>

void
cnd_new(cw_cnd_t *a_cnd)
{
#ifdef CW_PTHREADS
    int error;
#endif

    cw_check_ptr(a_cnd);

#ifdef CW_PTH
    if (pth_cond_init(&a_cnd->condition) == false)
    {
	fprintf(stderr, "%s:%d:%s(): Error in pth_cond_init(): %s\n",
		__FILE__, __LINE__, __func__, strerror(errno));
	abort();
    }
#endif
#ifdef CW_PTHREADS
    error = pthread_cond_init(&a_cnd->condition, NULL);
    if (error)
    {
	fprintf(stderr, "%s:%d:%s(): Error in pthread_cond_init(): %s\n",
		__FILE__, __LINE__, __func__, strerror(error));
	abort();
    }
#endif
}

void
cnd_delete(cw_cnd_t *a_cnd)
{
#ifdef CW_PTHREADS
    int error;
#endif

    cw_check_ptr(a_cnd);

#ifdef CW_PTHREADS
    error = pthread_cond_destroy(&a_cnd->condition);
    if (error)
    {
	fprintf(stderr, "%s:%d:%s(): Error in pthread_cond_destroy(): %s\n",
		__FILE__, __LINE__, __func__, strerror(error));
	abort();
    }
#endif
}

void
cnd_signal(cw_cnd_t *a_cnd)
{
#ifdef CW_PTHREADS
    int error;
#endif

    cw_check_ptr(a_cnd);

#ifdef CW_PTH
    if (pth_cond_notify(&a_cnd->condition, false) == false)
    {
	fprintf(stderr, "%s:%d:%s(): Error in pth_cond_notify(): %s\n",
		__FILE__, __LINE__, __func__, strerror(errno));
	abort();
    }
#endif
#ifdef CW_PTHREADS
    error = pthread_cond_signal(&a_cnd->condition);
    if (error) {
	fprintf(stderr, "%s:%d:%s(): Error in pthread_cond_signal(): %s\n",
		__FILE__, __LINE__, __func__, strerror(error));
	abort();
    }
#endif
}

void
cnd_broadcast(cw_cnd_t *a_cnd)
{
#ifdef CW_PTHREADS
    int error;
#endif

    cw_check_ptr(a_cnd);

#ifdef CW_PTH
    if (pth_cond_notify(&a_cnd->condition, true) == false)
    {
	fprintf(stderr, "%s:%d:%s(): Error in pth_cond_notify(): %s\n",
		__FILE__, __LINE__, __func__, strerror(errno));
	abort();
    }
#endif
#ifdef CW_PTHREADS
    error = pthread_cond_broadcast(&a_cnd->condition);
    if (error)
    {
	fprintf(stderr, "%s:%d:%s(): Error in pthread_cond_broadcast(): %s\n",
		__FILE__, __LINE__, __func__, strerror(error));
	abort();
    }
#endif
}

bool
cnd_timedwait(cw_cnd_t *a_cnd, cw_mtx_t *a_mtx,
	      const struct timespec *a_timeout)
{
    bool retval;
#ifdef CW_PTH
    pth_event_t event;
#endif
#ifdef CW_PTHREADS
    int error;
#endif
    struct timeval now;
    struct timespec timeout;
    struct timezone tz;

    cw_check_ptr(a_cnd);
    cw_check_ptr(a_mtx);
    cw_check_ptr(a_timeout);

    /* Set timeout. */
    memset(&tz, 0, sizeof(struct timezone));
    gettimeofday(&now, &tz);
    timeout.tv_nsec = now.tv_usec * 1000 + a_timeout->tv_nsec;
    timeout.tv_sec = (now.tv_sec + a_timeout->tv_sec
		      + (timeout.tv_nsec / 1000000000)); /* Carry if nanoseconds
							  * overflowed. */
    /* Chop off the number of nanoseconds to be less than one second. */
    timeout.tv_nsec %= 1000000000;

#ifdef CW_PTH
    event = pth_event(PTH_EVENT_TIME,
		      pth_time(timeout.tv_sec, timeout.tv_nsec / 1000));

    if (pth_cond_await(&a_cnd->condition, &a_mtx->mutex, event) == false)
    {
	fprintf(stderr, "%s:%d:%s(): Error in pth_cond_await(): %s\n",
		__FILE__, __LINE__, __func__, strerror(errno));
	abort();
    }

    if (pth_event_occurred(event) == false)
    {
	retval = false;
    }
    else
    {
	retval = true;
    }
#endif
#ifdef CW_PTHREADS
    error = pthread_cond_timedwait(&a_cnd->condition, &a_mtx->mutex, &timeout);
    if (error == 0)
    {
	retval = false;
    }
    else if (error == ETIMEDOUT)
    {
	retval = true;
    }
    else
    {
	fprintf(stderr, "%s:%d:%s(): Error in pthread_cond_timedwait(): %s\n",
		__FILE__, __LINE__, __func__, strerror(error));
	abort();
    }
#endif

    return retval;
}

void
cnd_wait(cw_cnd_t *a_cnd, cw_mtx_t *a_mtx)
{
#ifdef CW_PTHREADS
    int error;
#endif

    cw_check_ptr(a_cnd);
    cw_check_ptr(a_mtx);

#ifdef CW_PTH
    if (pth_cond_await(&a_cnd->condition, &a_mtx->mutex, NULL) == false)
    {
	fprintf(stderr, "%s:%d:%s(): Error in pth_cond_wait: %s\n",
		__FILE__, __LINE__, __func__, strerror(errno));
	abort();
    }
#endif
#ifdef CW_PTHREADS
    error = pthread_cond_wait(&a_cnd->condition, &a_mtx->mutex);
    if (error)
    {
	fprintf(stderr, "%s:%d:%s(): Error in pthread_cond_wait: %s\n",
		__FILE__, __LINE__, __func__, strerror(error));
	abort();
    }
#endif
}
