/*
 * $Id: udqueue_err.c,v 1.1 2000/08/07 23:15:04 emmerson Exp $
 */


#include "udposix.h"	/* for voidp */

#include <stddef.h>	/* for size_t */
#include <assert.h>
#include <stdio.h>	/* needed by some non-conforming <assert.h>s */
#include <stdlib.h>	/* for malloc() & memcpy() */
#include <pthread.h>	/* for POSIX threads */

#include "udfifo.h"	/* for FIFO API */
#include "udqueue.h"	/* for my public API */


/*
 * Destroy a queue.
 */
    int
udqueue_destroy(queue)
    Udqueue	*queue;
{
    int		status;

    if (queue == NULL) {
	status	= UDQUEUE_EINVAL;
    } else {
	status	= UDQUEUE_ESUCCESS;

	if (!queue->mutex_created) {
	    status	= UDQUEUE_EINVAL;
	} else {
	    (void) pthread_mutex_destroy(&queue->mutex);
	}

	if (!queue->cond_created) {
	    status	= UDQUEUE_EINVAL;
	} else {
	    (void) pthread_cond_destroy(&queue->cond);
	}

	status	= udfifo_destroy(&queue->fifo);
    }

    return status;
}


/*
 * Initialize a new queue.
 */
    int
udqueue_init(queue, eltsize, numelts, getmode, putmode)
    Udqueue	*queue;
    size_t	eltsize;
    int		numelts;
    int		getmode;
    int		putmode;
{
    int		status;

    if (queue == NULL
	    || eltsize <= 0
	    || numelts <= 0
	    || (getmode != UDQUEUE_WAIT && getmode != UDQUEUE_NOWAIT)
	    || (putmode != UDQUEUE_WAIT && putmode != UDQUEUE_NOWAIT
		&& putmode != UDQUEUE_FORCE)) {

	status	= UDQUEUE_EINVAL;

    } else {

	/*
	 * Create a FIFO.
	 */
	if ((status = udfifo_init(&queue->fifo, eltsize, numelts, 
				  putmode == UDQUEUE_FORCE
					? UDFIFO_FORCE
					: 0)) == UDQUEUE_ESUCCESS) {

	    queue->putmode	= putmode;
	    queue->getmode	= getmode;

	    /*
	     * Create a fast, non-recursive mutex to manage
	     * asynchronous access to the FIFO.
	     */
	    if (!(queue->mutex_created = pthread_mutex_init(&queue->mutex, 
		    pthread_mutexattr_default) == 0)) {

		status	= UDQUEUE_ETHREAD;

	    } else {

		/*
		 * Create a condition variable to wait upon if necessary.
		 */
		if (putmode != UDQUEUE_WAIT && getmode != UDQUEUE_WAIT) {
		    status	= UDQUEUE_ESUCCESS;
		} else {
		    status	= (queue->cond_created = 
					pthread_cond_init(&queue->cond, 
					    pthread_condattr_default) == 0)
					? UDQUEUE_ESUCCESS
					: UDQUEUE_ETHREAD;
		}
	    }
	}
	if (status != UDQUEUE_ESUCCESS)
	    (void) udqueue_delete(queue);
    }

    return status;
}


/*
 * Add to a queue.
 */
    int
udqueue_put(queue, new, old)
    Udqueue	*queue;
    const void	*new;
    void	*old;
{
    int		status;

    if (queue == NULL) {

	status	= UDQUEUE_EINVAL;

    } else {

	if (pthread_mutex_lock(&queue->mutex) != 0) {

	    status	= UDQUEUE_ETHREAD;

	} else {

	    status	= UDQUEUE_ESUCCESS;

	    if (queue->putmode == UDQUEUE_WAIT)
		/*
		 * Because we're not forcing stuff into the FIFO, wait until 
		 * there's room.
		 */
		while (udfifo_space(&queue->fifo) == 0)
		    if (pthread_cond_wait(&queue->cond, &queue->mutex) != 0) {
			status	= UDQUEUE_ETHREAD;
			break;
		    }

	    if (status == UDQUEUE_ESUCCESS)
		if ((status = udfifo_put(&queue->fifo, new, old)) == 0)
		    (void) pthread_cond_signal(&queue->cond);

	    (void) pthread_mutex_unlock(&queue->mutex);
	}
    }

    return status;
}


/*
 * Remove the oldest element from a queue.
 */
    int
udqueue_get(queue, old)
    Udqueue	*queue;
    void	*old;
{
    int		status;

    if (queue == NULL) {

	status	= UDQUEUE_EINVAL;

    } else {

	if (pthread_mutex_lock(&queue->mutex) != 0) {

	    status	= UDQUEUE_ETHREAD;

	} else {

	    status	= UDQUEUE_ESUCCESS;

	    if (queue->getmode == UDQUEUE_WAIT)
		/*
		 * Wait until there's something in the FIFO.
		 */
		while (udfifo_count(&queue->fifo) == 0)
		    if (pthread_cond_wait(&queue->cond, &queue->mutex) != 0) {
			status	= UDQUEUE_ETHREAD;
			break;
		    }

	    if (status == UDQUEUE_ESUCCESS)
		if ((status = udfifo_get(&queue->fifo, old)) == 0)
		    (void) pthread_cond_signal(&queue->cond);

	    (void) pthread_mutex_unlock(&queue->mutex);
	}
    }

    return status;
}


/*
 * Return the number of elements in a queue.
 */
    int
udqueue_count(queue)
    Udqueue	*queue;
{
    int		status;

    if (queue == NULL) {
	status	= UDQUEUE_EINVAL;
    } else {
	if (pthread_mutex_lock(&queue->mutex) != 0) {
	    status	= UDQUEUE_ETHREAD;
	} else {
	    status	= udfifo_count(&queue->fifo);
	    (void) pthread_mutex_unlock(&queue->mutex);
	}
    }

    return status;
}


/*
 * Return the number of empty spaces in a queue in terms of elements.
 */
    int
udqueue_space(queue)
    Udqueue	*queue;
{
    int		status;

    if (queue == NULL) {
	status	= UDQUEUE_EINVAL;
    } else {
	if (pthread_mutex_lock(&queue->mutex) != 0) {
	    status	= UDQUEUE_ETHREAD;
	} else {
	    status	= udfifo_space(&queue->fifo);
	    (void) pthread_mutex_unlock(&queue->mutex);
	}
    }

    return status;
}
