/*
 * Copyright 2010
 * Princeton University. 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.
 *
 * THIS SOFTWARE IS PROVIDED BY PRINCETON UNIVERSITY ``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 PRINCETON UNIVERSITY OR
 * CONTRIBUTORS 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.
 *
 * The views and conclusions contained in the software and documentation are those of the
 * authors and should not be interpreted as representing official policies, either expressed
 * or implied, of Princeton University.
 *
 */

/* 
 *	@(#)host_serv.h	(Princeton) 11/11/2010
 *  
 */


#include <stdlib.h>
#include <unistd.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <malloc.h>
#include <pthread.h>
#include <stdint.h>
#include<sys/mman.h>

#include <sys/socket.h>
#include <sys/types.h>
#include <linux/types.h>
#include <linux/in.h>
#include <linux/if_ether.h>
#include <net/if.h>
//#include <linux/filter.h>
#include <linux/ip.h>
#include <linux/tcp.h>
#include <sys/ioctl.h>

#include <sys/bsd_lock.h>

#include "host_serv.h"

#define	LC_SLEEPLOCK	0x00000001	/* Sleep lock. */
#define	LC_SPINLOCK	0x00000002	/* Spin lock. */
#define	LC_SLEEPABLE	0x00000004	/* Sleeping allowed with this lock. */
#define	LC_RECURSABLE	0x00000008	/* Locks of this type may recurse. */
#define	LC_UPGRADABLE	0x00000010	/* Upgrades and downgrades permitted. */

#define	LO_CLASSFLAGS	0x0000ffff	/* Class specific flags. */
#define	LO_INITIALIZED	0x00010000	/* Lock has been initialized. */
#define	LO_WITNESS	0x00020000	/* Should witness monitor this lock. */
#define	LO_QUIET	0x00040000	/* Don't log locking operations. */
#define	LO_RECURSABLE	0x00080000	/* Lock may recurse. */
#define	LO_SLEEPABLE	0x00100000	/* Lock may be held while sleeping. */
#define	LO_UPGRADABLE	0x00200000	/* Lock may be upgraded/downgraded. */
#define	LO_DUPOK	0x00400000	/* Don't check for duplicate acquires */
#define	LO_CLASSMASK	0x0f000000	/* Class index bitmask. */
#define LO_NOPROFILE    0x10000000      /* Don't profile this lock */


/*
 * CPU_Set
 */
long		max_host_cores;
cpu_set_t 	cpuset_exclusive_s;
cpu_set_t 	cpuset_exclusive_c;
cpu_set_t	cpuset_shared;


/*
 * Declare some variables
 */

static pthread_mutexattr_t	 mutexattr;

#ifdef UPTCP_CLIENT
static struct sockaddr_in   server_addr;
#else
static struct sockaddr_in   client_addr;
#endif

/*
 * Thread related
 */ 

thread_wrapper	thread_list[THREAD_LIST_SIZE];
unsigned char	thread_index[THREAD_INDEX_SIZE]; 

int		uptcpip_thread_count = 0;
pthread_mutex_t uptcpip_thread_count_mutex  = PTHREAD_MUTEX_INITIALIZER;

extern struct timeval global_roi_start_tv;

int		in_tcpip_roi = 0;

/*
 * Function Implementation
 */ 


int host_setup_cpuset()
{
#ifndef FOR_SIMULATION
	int i, s, c;
	max_host_cores = sysconf(_SC_NPROCESSORS_ONLN);
#if 0
	if(max_host_cores >= 2){
		s = max_host_cores/2 - 1;
		c = max_host_cores/2;

		CPU_ZERO(&cpuset_exclusive_s);
		CPU_SET(s, &cpuset_exclusive_s);

		CPU_ZERO(&cpuset_exclusive_c);
		CPU_SET(c, &cpuset_exclusive_c);

		CPU_ZERO(&cpuset_shared);
		for(i = 0; i < max_host_cores; i ++)
			if(i != s && i != c)
				CPU_SET(i, &cpuset_shared);

	} else {
		CPU_ZERO(&cpuset_exclusive_s);
		CPU_SET(0, &cpuset_exclusive_s);

		CPU_ZERO(&cpuset_exclusive_c);
		CPU_SET(0, &cpuset_exclusive_c);

		CPU_ZERO(&cpuset_shared);
		CPU_SET(0, &cpuset_shared);
	}
	

        if (sched_setaffinity(0, sizeof(cpu_set_t),&cpuset_shared) <0) {
                perror("pthread_setaffinity_np");
        }
#endif //0
#endif
	return 0;
}

void enter_tcpip_roi()
{
	int i;
	for(i = 0; i < uptcpip_thread_count; i++)
		thread_list[i].last_active_time = global_roi_start_tv;
	in_tcpip_roi = 1;
}

void exit_tcpip_roi()
{
	//NOP
	in_tcpip_roi = 0;
}


int pthread_create_wrapper(pthread_t *thread, const pthread_attr_t *attr, void *(*start_routine)(void*), void *arg, int type)
{
        host_setup_cpuset();
	thread_wrapper * tw_ptr;

        int res = pthread_create(thread, attr, start_routine, arg);	
	
	/* initialize thread_wrapper for the created thread */
	if(res == 0){
		pthread_mutex_lock(&uptcpip_thread_count_mutex);

		tw_ptr = &thread_list[uptcpip_thread_count];
		tw_ptr->ptr = *thread;
		tw_ptr->id = uptcpip_thread_count; //start from "1"
		tw_ptr->type = type;
		gettimeofday(&(tw_ptr->last_active_time), NULL);
		
		int hash = (unsigned long)(*thread) % THREAD_INDEX_SIZE;
		while(thread_index[hash] != 0xff)
			hash = (hash + 1) % THREAD_INDEX_SIZE;
		thread_index[hash] = tw_ptr->id;
		
		uptcpip_thread_count ++;

		pthread_mutex_unlock(&uptcpip_thread_count_mutex);

#ifndef FOR_SIMULATION
		cpu_set_t  cpuset;
		CPU_ZERO(&cpuset);

#ifdef UPTCP_CLIENT //client
		switch(type){
			case THREAD_TYPE_TX_LINK:  CPU_SET(1 % max_host_cores, &cpuset);	break;			
			case THREAD_TYPE_RX_TCPIP: CPU_SET(2 % max_host_cores, &cpuset);	break;			
			case THREAD_TYPE_APP:      CPU_SET(3 % max_host_cores, &cpuset);	break;			
			default: 	           CPU_SET(0 % max_host_cores, &cpuset);	break;			
		}
#else //server
		switch(type){
			case THREAD_TYPE_RX_LINK:  CPU_SET((max_host_cores-2) % max_host_cores, &cpuset);	break;			
			case THREAD_TYPE_RX_TCPIP: CPU_SET((max_host_cores-1) % max_host_cores, &cpuset);	break;			
			case THREAD_TYPE_APP:      CPU_SET(max_host_cores, &cpuset);	break;			
			default: 	           CPU_SET((max_host_cores-3) % max_host_cores, &cpuset);	break;			
		}
#endif		
		if (pthread_setaffinity_np(*thread, sizeof(cpu_set_t),&cpuset) <0) {
                        fprintf(stderr, "pthread_setaffinity_np failed\n");
                }

#endif

	}
	
	return res;
}


int host_thread_get_uptcp_tid()
{
	pthread_t self = pthread_self();
	
	int hash = (unsigned long)self % THREAD_INDEX_SIZE;
	while(thread_index[hash] == 0xff || self != thread_list[thread_index[hash]].ptr)
		hash = (hash + 1) % THREAD_INDEX_SIZE;

	return thread_index[hash];
}


void host_thread_enter_wait_region()
{
	if(!in_tcpip_roi)
		return;

	struct timeval cur_time;
	pthread_t self = pthread_self();
	
	int hash = (unsigned long)self % THREAD_INDEX_SIZE;
	while(thread_index[hash] == 0xff || self != thread_list[thread_index[hash]].ptr)
		hash = (hash + 1) % THREAD_INDEX_SIZE;

	thread_wrapper  *ptr = &(thread_list[thread_index[hash]]);

	/* enter wait region*/
	gettimeofday(&(cur_time), NULL);

	unsigned long long  interval = (cur_time.tv_sec * 1000000 + cur_time.tv_usec) - (ptr->last_active_time.tv_sec * 1000000 + ptr->last_active_time.tv_usec);
	ptr->active_time += interval;
}


void host_thread_exit_wait_region()
{
	if(!in_tcpip_roi)
		return;

	pthread_t self = pthread_self();
	
	int hash = (unsigned long)self % THREAD_INDEX_SIZE;
	while(thread_index[hash] == 0xff || self != thread_list[thread_index[hash]].ptr)
		hash = (hash + 1) % THREAD_INDEX_SIZE;

	thread_wrapper  *ptr = &(thread_list[thread_index[hash]]);

	/* exit wait region*/
	gettimeofday(&(ptr->last_active_time), NULL);
}


int host_thread_enter_tcpip_region(int type)
{
	if(!in_tcpip_roi)
		return 0;

	pthread_t self = pthread_self();
	
	int hash = (unsigned long)self % THREAD_INDEX_SIZE;
	while(thread_index[hash] == 0xff || self != thread_list[thread_index[hash]].ptr)
		hash = (hash + 1) % THREAD_INDEX_SIZE;

	thread_wrapper  *ptr = &(thread_list[thread_index[hash]]);
	if(ptr->type == THREAD_TYPE_APP)
		ptr->type  = type;

	/* enter tcpip region*/
	gettimeofday(&(ptr->last_tcpip_time), NULL);
	
	return ptr->id;
}


int host_thread_exit_tcpip_region(int thread_id)
{
	if(!in_tcpip_roi)
		return 0;

	struct timeval tv;
	thread_wrapper *ptr = &(thread_list[thread_id]);

	gettimeofday(&tv, NULL);
	
	unsigned long long  interval = (tv.tv_sec * 1000000 + tv.tv_usec)- (ptr->last_tcpip_time.tv_sec * 1000000 + ptr->last_tcpip_time.tv_usec);

	ptr->intcpip_time += interval;
	
	return 0;

}


int host_thread_create(void* (*routine)(void*), void* t_args, int type)
{
	pthread_t *pthread;
	
	pthread = (pthread_t*) malloc(sizeof(pthread_t));
	if(!pthread){
		printf("hif_alloc_ring_and_thread(): malloc() for pthread failed\n");
		return -1;
	}
	memset(pthread, 0, sizeof(pthread_t));
		
	if(pthread_create_wrapper(pthread, NULL, routine, t_args, type) < 0){
		printf("hif_alloc_ring_and_thread(): pthread_create() failed\n");
		free(pthread);
		return -1;
	}
	
	return 0;
}


int host_usleep(int num)
{
	usleep(num);
	return 0;
}




int host_setup_link(int port) 
{
	int link_fd;
	
	/* Step.2 bind & listen  */	
        struct sockaddr_in      sin;

	if((link_fd = socket(AF_INET, SOCK_DGRAM, 0)) < 0){
		printf("host_setup_link(): socket() fail, errno = %d\n", errno);
		return -1;
	}

        memset(&sin, 0, sizeof(sin));
        sin.sin_family = PF_INET;
        sin.sin_addr.s_addr = INADDR_ANY;
        sin.sin_port = htons(port);	

       if (bind(link_fd, (struct sockaddr *) &sin, sizeof(sin)) == -1) { 
		printf("host_setup_link(): bind() fail\n");
		return -1; 
        } 
	
	return link_fd;
}


int  host_connect_link(int port) 
{
	int 	link_fd;

	/* Step.0 create a socket  */	
	if((link_fd = socket(AF_INET, SOCK_DGRAM, 0)) < 0){
		printf("host_connect_link(): socket() fail, errno = %d\n", errno);
		return -1;
	}

	/* Step.1 connect to server  */	
#ifdef UPTCP_CLIENT
        memset(&server_addr, 0, sizeof(server_addr));
        server_addr.sin_family = AF_INET;
        server_addr.sin_addr.s_addr = htonl(0x7f000001);
        server_addr.sin_port = htons(port);	
#else
        memset(&client_addr, 0, sizeof(client_addr));
        client_addr.sin_family = AF_INET;
        client_addr.sin_addr.s_addr = htonl(0x0a0a0a01);
        client_addr.sin_port = htons(port);	
#endif

	return link_fd;
}

int host_link_send(int link_fd, const void * buf, size_t len)
{
	int res = 0;
#ifdef UPTCP_CLIENT
	res = sendto(link_fd, buf, len, 0, (struct sockaddr*)&server_addr, sizeof(server_addr));
#else
	res = sendto(link_fd, buf, len, 0, (struct sockaddr*)&client_addr, sizeof(client_addr));
#endif
	if(res < 0)
		printf("host_link_send: errno = %d\n", errno);
	return res;
}

int host_link_recv(int link_fd,void *buf, size_t len)
{
	int res;
	
	res = recvfrom(link_fd, buf, len, 0, NULL, NULL);

	if(res < 0)
		printf("host_link_recv(): res = %d, errno = %d\n", res, errno);
	return res;
}


/* host memory management */
void* host_malloc(size_t size, int align)
{
	if(align == -1)
		return malloc(size);
	else return memalign(align, size);
}

void host_free(void * ptr)
{
	free(ptr);
}


/*
 * mutex
 */
void host_pthread_mutexattr_init()
{
	pthread_mutexattr_init(&mutexattr);
	pthread_mutexattr_settype(&mutexattr, PTHREAD_MUTEX_RECURSIVE);	
}


void* host_pthread_mutex_init(int flag)
{
	pthread_mutex_t  *p_mutex;

	p_mutex = (pthread_mutex_t*)malloc(sizeof(pthread_mutex_t));
	if((flag & LO_RECURSABLE) != 0)
		pthread_mutex_init(p_mutex, &mutexattr);
	else 	pthread_mutex_init(p_mutex, NULL);

	return p_mutex;
}

void host_pthread_mutex_destory(void* lock)
{
	struct lock_object *p_lo = (struct lock_object *)lock;
	
	pthread_mutex_t *p_mutex = (pthread_mutex_t*)p_lo->ext_lock;

	pthread_mutex_destroy(p_mutex);

	free(p_mutex);
}

void host_pthread_mutex_lock(void* lock)
{
	int error;
	struct lock_object *p_lo = (struct lock_object *)lock;
	
	pthread_mutex_t *p_mutex = (pthread_mutex_t*)p_lo->ext_lock;

	if(p_lo->lo_flags & LO_RECURSABLE){
		error = pthread_mutex_lock(p_mutex);
		p_lo->lo_data ++;		
		p_lo->lo_owner = (unsigned long int)pthread_self();
		if(error){
			printf("panic\n");
			exit(-1);
		}
	}else{
		pthread_mutex_lock(p_mutex);
		if(p_lo->lo_data != 0){
			printf("panic\n");
			exit(-1);
		}		
		p_lo->lo_data ++;		
		p_lo->lo_owner = (unsigned long int)pthread_self();
	}

	
}


int host_pthread_mutex_trylock(void* lock)
{
	struct lock_object *p_lo = (struct lock_object *)lock;
	
	pthread_mutex_t *p_mutex = (pthread_mutex_t*)p_lo->ext_lock;
	
	int error, retval;

	if(p_lo->lo_flags & LO_RECURSABLE){
		error = pthread_mutex_trylock(p_mutex);
		if(error == 0){
			p_lo->lo_data ++;			
			p_lo->lo_owner = (unsigned long int)pthread_self();
			retval = 1;
		}else{
			retval = 0;
		}
		
	} else {
		error = pthread_mutex_trylock(p_mutex);
		if(error == 0){
			if(p_lo->lo_data != 0){
				printf("panic\n");
				exit(-1);
			}			
			p_lo->lo_data ++;						
			p_lo->lo_owner = (unsigned long int)pthread_self();
			retval = 1;
		}else{ 
			retval = 0;
		}
	}
	return retval;
}


void host_pthread_mutex_unlock(void* lock)
{
	int	error;
	struct lock_object *p_lo = (struct lock_object *)lock;
	
	pthread_mutex_t *p_mutex = (pthread_mutex_t*)p_lo->ext_lock;
	
	if(p_lo->lo_flags & LO_RECURSABLE){
		p_lo->lo_data --;
		p_lo->lo_owner = (unsigned long int)pthread_self();
		error = pthread_mutex_unlock(p_mutex);
		if(error){
			printf("panic\n");
			exit(-1);
		}
	}else{
		if(p_lo->lo_data != 1){
			printf("panic\n");
			exit(-1);
		}
		p_lo->lo_data --;
		p_lo->lo_owner = (unsigned long int)pthread_self();
		pthread_mutex_unlock(p_mutex);
	}
}



void* host_pthread_rwlock_init()
{
	pthread_rwlock_t  *p_rwlock;

	p_rwlock = (pthread_rwlock_t*)malloc(sizeof(pthread_rwlock_t));
	pthread_rwlock_init(p_rwlock, NULL);

	return p_rwlock;	
}


int host_pthread_rwlock_destory(void* lock)
{
	struct lock_object *p_lo = (struct lock_object *)lock;
	
	pthread_rwlock_t *p_rwlock = (pthread_rwlock_t*)p_lo->ext_lock;
	
	int error = pthread_rwlock_destroy(p_rwlock);

	free(p_rwlock);

	return (error == 0) ? 1 : 0;
}


void host_pthread_rwlock_wlock(void* lock)
{
	struct lock_object *p_lo = (struct lock_object *)lock;
	
	pthread_rwlock_t *p_rwlock = (pthread_rwlock_t*)p_lo->ext_lock;
	
	pthread_t  tid;

	if(p_lo->lo_flags & LO_RECURSABLE){
		tid = pthread_self();

		if(p_lo->lo_owner == tid && p_lo->lo_data != 0){
			p_lo->lo_data ++; 
		} else { /*!= tid*/
			pthread_rwlock_wrlock(p_rwlock);			
			p_lo->lo_owner = tid;
			p_lo->lo_data = 1;
		}

	} else {
		pthread_rwlock_wrlock(p_rwlock);
		if(p_lo->lo_data != 0){
			printf("panic\n");
			exit(-1);
		}		
		p_lo->lo_data ++;		
		p_lo->lo_owner = (unsigned long int)pthread_self();
	}
}


void host_pthread_rwlock_wunlock(void* lock)
{
	struct lock_object *p_lo = (struct lock_object *)lock;
	
	pthread_rwlock_t *p_rwlock = (pthread_rwlock_t*)p_lo->ext_lock;

	pthread_t tid;

	if(p_lo->lo_flags & LO_RECURSABLE){
		tid = pthread_self();
	
		if(p_lo->lo_owner == tid){
			if(p_lo->lo_data == 1){
				p_lo->lo_owner = 0;
				p_lo->lo_data = 0;
				pthread_rwlock_unlock(p_rwlock);
			} else {
				p_lo->lo_data --;
			}
		} else { /*error, lock can be unlock by owner*/
			printf("host_pthread_rwlock_wunlck(): ERROR, try to unlock an unacquired lock\n");
		}
		
	} else {
		if(p_lo->lo_data != 1){
			printf("panic\n");
			exit(-1);
		}
		p_lo->lo_data --;	
		p_lo->lo_owner = 0;
		pthread_rwlock_unlock(p_rwlock);
	}
	
}


void host_pthread_rwlock_rlock(void* lock)
{
	struct lock_object *p_lo = (struct lock_object *)lock;
	
	pthread_rwlock_t *p_rwlock = (pthread_rwlock_t*)p_lo->ext_lock;

	int error;
	
	if(p_lo->lo_flags & LO_RECURSABLE){
		error = pthread_rwlock_rdlock(p_rwlock);
		if(error){
			printf("panic\n");
			exit(-1);
		}
	} else {
		pthread_rwlock_rdlock(p_rwlock);
		p_lo->lo_data ++;				
	}
}


void host_pthread_rwlock_runlock(void* lock)
{
	struct lock_object *p_lo = (struct lock_object *)lock;
	
	pthread_rwlock_t *p_rwlock = (pthread_rwlock_t*)p_lo->ext_lock;

	int error;

	if(p_lo->lo_flags & LO_RECURSABLE){
		error = pthread_rwlock_unlock(p_rwlock);
		if(error){
			printf("panic\n");
			exit(-1);
		}
	}else{
		p_lo->lo_data --;
		pthread_rwlock_unlock(p_rwlock);
	}
		
}


int host_pthread_rwlock_tryrlock(void* lock)
{
	struct lock_object *p_lo = (struct lock_object *)lock;
	
	pthread_rwlock_t *p_rwlock = (pthread_rwlock_t*)p_lo->ext_lock;
	
	int error, retval;

	if(p_lo->lo_flags & LO_RECURSABLE){
		error = pthread_rwlock_tryrdlock(p_rwlock);
		if(error == 0){
			retval = 1;
		}else{
			retval = 0;
		}
		
	} else {
		error = pthread_rwlock_tryrdlock(p_rwlock);
		if(error == 0){
			p_lo->lo_data ++;						
			retval = 1;
		}else{ 
			retval = 0;
		}
	}
	return retval;
	
}


int host_pthread_rwlock_trywlock(void* lock)
{
	struct lock_object *p_lo = (struct lock_object *)lock;
	
	pthread_rwlock_t *p_rwlock = (pthread_rwlock_t*)p_lo->ext_lock;

	int error;
	pthread_t  tid;

	if(p_lo->lo_flags & LO_RECURSABLE){
		tid = pthread_self();
		
		if(p_lo->lo_owner == tid && p_lo->lo_data != 0){
			p_lo->lo_data ++; 
			error = 0;
		} else { /*!= tid*/
			error = pthread_rwlock_trywrlock(p_rwlock);			
			if(error == 0){
				p_lo->lo_owner = tid;
				if(p_lo->lo_data != 0){
					printf("panic\n");
					exit(-1);
				}
				p_lo->lo_data = 1;
			}
		}
	} else {
		error = pthread_rwlock_trywrlock(p_rwlock);
		if(error == 0){
			if(p_lo->lo_data != 0){
				printf("panic\n");
				exit(-1);
			}			
			p_lo->lo_data ++;						
			p_lo->lo_owner = (unsigned long int)pthread_self();
		}
		
	}

	return (error == 0) ? 1 : 0;

}


void host_pthread_rwlock_unlock(void* lock)
{
	struct lock_object *p_lo = (struct lock_object *)lock;
	pthread_t tid = pthread_self();

	if(p_lo->lo_owner == tid)
		host_pthread_rwlock_wunlock(lock);
	else host_pthread_rwlock_runlock(lock);
}


/*
 * condition
 */
void* host_pthread_cond_init()
{
	pthread_cond_t  *p_cond;

	p_cond = (pthread_cond_t*)malloc(sizeof(pthread_cond_t));
	pthread_cond_init(p_cond, NULL);

	return p_cond;	
}



void host_pthread_cond_destroy(void* cond)
{
	pthread_cond_t *p_cond = (pthread_cond_t*)cond;
	
	pthread_cond_destroy(p_cond);

	free(p_cond);
}


int host_pthread_cond_wait(void* cond, void* lock)
{
	pthread_cond_t *p_cond = (pthread_cond_t*)cond;
	struct lock_object *p_lo = (struct lock_object *)lock;
	pthread_mutex_t *p_mutex = (pthread_mutex_t*)p_lo->ext_lock;
	int error;
	
	host_thread_enter_wait_region();
	
	if(p_lo->lo_flags & LO_RECURSABLE){
		p_lo->lo_data --;
		p_lo->lo_owner = (unsigned long int)pthread_self();
	}else{
		if(p_lo->lo_data != 1){
			printf("panic\n");
			exit(-1);
		}
		p_lo->lo_data --;
		p_lo->lo_owner = (unsigned long int)pthread_self();
	}

	error = pthread_cond_wait(p_cond, p_mutex);

	if(p_lo->lo_flags & LO_RECURSABLE){
		p_lo->lo_data ++;		
		p_lo->lo_owner = (unsigned long int)pthread_self();
	}else{
		if(p_lo->lo_data != 0){
			printf("panic\n");
			exit(-1);
		}		
		p_lo->lo_data ++;		
		p_lo->lo_owner = (unsigned long int)pthread_self();
	}

	host_thread_exit_wait_region();

	return (error == 0) ? 0 : 1;	
}


void host_pthread_cond_signal(void* cond)
{
	pthread_cond_t *p_cond = (pthread_cond_t*)cond;

	pthread_cond_signal(p_cond);
}

