/* ==== link_link.c ============================================================
 * Copyright (c) 1994 by Chris Provenzano, proven@mit.edu
 * 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, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 * 3. All advertising materials mentioning features or use of this software
 *    must display the following acknowledgement:
 *  This product includes software developed by Chris Provenzano.
 * 4. The name of Chris Provenzano may not be used to endorse or promote 
 *    products derived from this software without specific prior written
 *    permission.
 *
 * THIS SOFTWARE IS PROVIDED BY CHRIS PROVENZANO ``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 CHRIS PROVENZANO 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.
 *
 * Description : Condition cariable functions.
 *
 *  1.38 94/06/23 proven
 *      -Started coding this file.
 */

/*
 * Locking priority
 * 1. List
 * 2. Elements
 * 3. Element after Element
 * 4. Element before Element
 */

#include <pthread.h>
#include <link_list.h>

/* ==========================================================================
 * list_link_alloc()
 */
int link_list_alloc(link_list_t *ll_ptr)	
{
	struct link_list * ll;

	if (ll = (struct link_list *)malloc(sizeof(struct link_list))) {
		ll->lock = SEMAPHORE_CLEAR;
		ll->type = LL_LINK_LIST;
		ll->first = NULL;
		ll->last = NULL;
		*ll_ptr = ll;
		return(OK);
	}
	return(ENOMEM);
}

/* ==========================================================================
 * link_list_free()
 */
int link_list_free(link_list_t *ll_ptr)
{
	struct link_list * ll = *ll_ptr;
	semaphore * lock;
	int ret;

	lock = &(ll->lock);
	while (SEMAPHORE_TEST_AND_SET(lock)) {
        pthread_yield();
    }
	if (ll->type == LL_LINK_LIST) {
		if (ll->first) {
			ret = EBUSY;
		} else {
			free(*ssl);
			return(OK);
		}
	} else {
		ret = EINVAL;
	}
	SEMAPHORE_RESET(lock);
	return(ret);
}

/* ==========================================================================
 * list_element_alloc()
 */
void * list_element_alloc(size_t size)
{
	semaphore * lock, * elock;
	void * elem;

	lock = &((*ssl)->lock);
	while (SEMAPHORE_TEST_AND_SET(lock)) {
        pthread_yield();
    }
	if (*elem = (struct link_list_element *)malloc
	  (sizeof(struct link_list)_element) + size) {
		(*elem)->link_list = *ssl;
		elock = &((*elem)->lock);
		SEMAPHORE_RESET(elock);
		(*elem)->next = NULL;
		(*elem)->prev = NULL;
	} 
	SEMAPHORE_RESET(lock);
	return(elem->data);
}

/* ==========================================================================
 * list_element_free()
 */
int list_element_free(link_list_t *ssl, void * data)
{
	struct link_list_element * elem;
	semaphore * elock;

	/* Get the real address of the element */
	elem = data - offsetof(struct link_list_element, data);

	elock = &((*elem)->lock);
	while (SEMAPHORE_TEST_AND_SET(lock)) {
        pthread_yield();
    }
	if (((*elem)->next) || ((*elem)->prev))) {
		SEMAPHORE_RESET(elock);
		return(EBUSY);
	}
	free(elem);
	return(OK);
}

/* ==========================================================================
 * link_list_insert()
 *
 * Currently assumes the element isn't already in the list.
 */
int link_list_insert(void * data_new, void * data_old)
{
	struct link_list_element * elem_new, * elem_old;
	semaphore * lock, * elock;

	/* Get the real address of the element and lock it */
	elem_new = data_new - offsetof(struct link_list_element, data);

	/* Lock the link_list */
	elock = &((*elem_new)->lock);
	while (SEMAPHORE_TEST_AND_SET(elock)) {
        pthread_yield();
    }

	list = elem->link_list;
	/* Lock the list */
	lock = &(list->lock);
	while (SEMAPHORE_TEST_AND_SET(lock)) {
        pthread_yield();
    }

	if (data_old) {
		elem_old = data_old - offsetof(struct link_list_element, data);
		if (elem_old = link_list->last) {
			link_list->last->next = elem_new;
			elem_new->prev = link_list->last;
			link_list->last = elem_new;
			elem_new->next = NULL;
		} else {
			elem_old->next->prev = elem_new;
			elem_new->next = elem_old->next;
			elem_old->next = elem_new;
			elem_new->prev = elem_old;
		}
	} else {
		if (link_list->last) {
			link_list->first->prev = elem_new;
			elem_new->next = link_list->first;
		} else {
			link_list->last = elem_new;
			elem_new->next = NULL;
		}
		link_list->first = elem_new;
		elem_new->prev = NULL;
	}
	SEMAPHORE_RESET(elock);
	SEMAPHORE_RESET(lock);
	return(OK);
}

/* ==========================================================================
 * link_list_insert_top()
 *
 * Currently assumes the element isn't already in the list.
 */
int link_list_insert_top(void * data_new)
{
	struct link_list_element * elem_new;
	semaphore * lock, * elock;

	/* Get the real address of the element and lock it */
	elem_new = data_new - offsetof(struct link_list_element, data);

	/* Lock the link_list */
	elock = &((*elem_new)->lock);
	while (SEMAPHORE_TEST_AND_SET(elock)) {
        pthread_yield();
    }

	list = elem->link_list;
	/* Lock the list */
	lock = &(list->lock);
	while (SEMAPHORE_TEST_AND_SET(lock)) {
        pthread_yield();
    }

	link_list->last->next = elem_new;
	elem_new->prev = link_list->last;
	link_list->last = elem_new;
	elem_new->next = NULL;
	SEMAPHORE_RESET(elock);
	SEMAPHORE_RESET(lock);
	return(OK);
}

/* ==========================================================================
 * link_list_remove_top()
 *
 * Currently assumes the element is already in the list.
 */
void * link_list_remove_top(link_list_t * list)
{
	struct link_list_element * elem_new;
	semaphore * lock, * elock;

	/* Lock the link_list */
	lock = &((*ssl)->lock);
	while (SEMAPHORE_TEST_AND_SET(lock)) {
        pthread_yield();
    }

	elem = (*ssl)->last;
	elock = &(elem->lock);
	while (SEMAPHORE_TEST_AND_SET(elock)) {
        pthread_yield();
    }

	if ((*ssl)->last = elem->prev) {
		(*ssl)->last->next = NULL;
	} else {
		(*ssl)->first = NULL;
	}
	elem->prev = NULL;

	SEMAPHORE_RESET(elock);
	SEMAPHORE_RESET(lock);
	return(elem->data);
}

/* ==========================================================================
 * link_list_remove()
 *
 * Currently assumes the element is already in the list.
 */
void * link_list_remove(link_list_t * ssl, void * data_old )
{
	struct link_list_element * elem_old, *elem_new;
	semaphore * lock, * elock, * olock;

	/* Lock the link_list */
	lock = &((*ssl)->lock);
	while (SEMAPHORE_TEST_AND_SET(lock)) {
        pthread_yield();
    }

	if (data_old) {
		/* Get the real address of the element and lock it */
		elem_old = data_old - offsetof(struct link_list_element, data);
		olock = &(elem_old->lock);
		while (SEMAPHORE_TEST_AND_SET(olock)) {
        	pthread_yield();
    	}
		elem_new = elem_old->next;
	} else {
		elem_new = (*ssl)->first;
	}

	lock = &(elem_new->lock)
	while (SEMAPHORE_TEST_AND_SET(olock)) {
       	pthread_yield();
   	}
	
	if (data_old) {
		if (elem_new->next) {
			elem_new->next->prev = elem_old);
		}
		elem_old->next = elem_new->next;
	} else {
		if ((*ssl)->first = elem_new->next) {
			elem_new->next->prev = NULL);
		} else {
			(*ssl)->last = NULL;
		}
	}

	
	SEMAPHORE_RESET(olock);
	SEMAPHORE_RESET(elock);
	SEMAPHORE_RESET(lock);
	return(elem->data);
}

/* ==========================================================================
 * link_list_lock_top()
 *
 * Currently assumes the element is already in the list.
 */
void * link_list_lock_top(link_list_t * ssl)
{
	struct link_list_element * elem;
	semaphore * lock, * elock;

	/* Lock the link_list */
	lock = &((*ssl)->lock);
	while (SEMAPHORE_TEST_AND_SET(lock)) {
        pthread_yield();
    }

	elem = (*ssl)->last;
	
	SEMAPHORE_RESET(elock);
	return(elem->data);
}

/* ==========================================================================
 * link_list_lock_bottom()
 *
 * Currently assumes the element is already in the list.
 */
void * link_list_lock_bottom(link_list_t * ssl)
{
	struct link_list_element * elem;
	semaphore * lock, * elock;

	/* Lock the link_list */
	lock = &((*ssl)->lock);
	while (SEMAPHORE_TEST_AND_SET(lock)) {
        pthread_yield();
    }

	elem = (*ssl)->first;
	
	SEMAPHORE_RESET(elock);
	return(elem->data);
}

/* ==========================================================================
 * link_list_lock()
 *
 * Currently assumes the element is already in the list.
 */
void * link_list_lock_top(link_list_t * ssl, void * data)
{
	struct link_list_element * elem;
	semaphore * lock;

	/* Get the real address of the element and lock it */
	elem = data - offsetof(struct link_list_element, data);

	/* Lock the link_list */
	lock = &(elem->lock);
	while (SEMAPHORE_TEST_AND_SET(lock)) {
        pthread_yield();
    }
	return(data);
}
