/*******************************************************************************
 *	BCondInt.cc	BCondBool Classes
 *			T.Barnaby,	BEAM Ltd,	10/12/02
 *******************************************************************************
 */
#include <BCondInt.h>
#include <sys/time.h>
#include <stdio.h>
#include <errno.h>

static struct timespec getTimeout(uint32_t timeOutUs){
 	struct timeval	tv;
	struct timespec	ts;
	
	gettimeofday(&tv, 0);
	ts.tv_sec = tv.tv_sec + timeOutUs / 1000000;
	ts.tv_nsec = (tv.tv_usec + timeOutUs % 1000000) * 1000;
	ts.tv_sec += (ts.tv_nsec / 1000000000);
	ts.tv_nsec %=  1000000000;
	
	return ts;
}

BCondInt::BCondInt(){
	pthread_mutex_init(&omutex, 0);
	pthread_cond_init(&ocond, 0);
	ovalue = 0;
}

BCondInt::~BCondInt(){
	pthread_cond_destroy(&ocond);
	pthread_mutex_destroy(&omutex);
}

void BCondInt::setValue(BInt value){
	pthread_mutex_lock(&omutex);
	ovalue = value;
	pthread_cond_broadcast(&ocond);
	pthread_mutex_unlock(&omutex);
}

BInt BCondInt::value() const {
	return ovalue;
}

BInt BCondInt::increment(BInt v){
	BInt	ret;
	
	pthread_mutex_lock(&omutex);
	ovalue += v;
	ret = ovalue;
	pthread_cond_broadcast(&ocond);
	pthread_mutex_unlock(&omutex);
	return ret;
}

BInt BCondInt::decrement(BInt v){
	BInt	ret;
	
	pthread_mutex_lock(&omutex);
	ovalue -= v;
	ret = ovalue;
	pthread_cond_broadcast(&ocond);
	pthread_mutex_unlock(&omutex);
	return ret;
}
		
Bool BCondInt::waitMoreThanOrEqual(BInt v, Bool decrement, BTimeout timeoutUs){
	Bool		ret = 0;
	struct timeval	tv;
	struct timespec	ts;
	
	pthread_mutex_lock(&omutex);
	if(timeoutUs != BTimeoutForever){
		ts = getTimeout(timeoutUs);
	
		while(!ret && (ovalue < v))
			ret = pthread_cond_timedwait(&ocond, &omutex, &ts);
	}
	else {
		while(ovalue < v)
			pthread_cond_wait(&ocond, &omutex);
	}
	
	if(ret == 0)
		ovalue -= decrement;
		
	pthread_mutex_unlock(&omutex);
	return !ret;
}

Bool BCondInt::waitLessThanOrEqual(BInt v, Bool increment, BTimeout timeoutUs){
	Bool		ret = 0;
	struct timespec	ts;
	
	pthread_mutex_lock(&omutex);
	if(timeoutUs != BTimeoutForever){
		ts = getTimeout(timeoutUs);
	
		while(!ret && (ovalue > v))
			ret = pthread_cond_timedwait(&ocond, &omutex, &ts);
	}
	else {
		while(ovalue > v)
			pthread_cond_wait(&ocond, &omutex);
	}

	if(ret == 0)
		ovalue += increment;
		
	pthread_mutex_unlock(&omutex);
	return !ret;
}

Bool BCondInt::waitLessThan(BInt v, BTimeout timeoutUs){
	Bool		ret = 0;
	struct timeval	tv;
	struct timespec	ts;
	
	pthread_mutex_lock(&omutex);
	if(timeoutUs != BTimeoutForever){
		ts = getTimeout(timeoutUs);
	
		while(!ret && (ovalue >= v))
			ret = pthread_cond_timedwait(&ocond, &omutex, &ts);
	}
	else {
		while(ovalue >= v)
			pthread_cond_wait(&ocond, &omutex);
	}
	pthread_mutex_unlock(&omutex);
	return !ret;
}




BCondValue::BCondValue(){
	pthread_mutex_init(&omutex, 0);
	pthread_cond_init(&ocond, 0);
	ovalue = 0;
}

BCondValue::~BCondValue(){
	pthread_cond_destroy(&ocond);
	pthread_mutex_destroy(&omutex);
}

void BCondValue::setValue(int value){
	pthread_mutex_lock(&omutex);
	ovalue = value;
	pthread_cond_broadcast(&ocond);
	pthread_mutex_unlock(&omutex);
}

int BCondValue::value(){
	return ovalue;
}

int BCondValue::increment(int v){
	int	ret;
	
	pthread_mutex_lock(&omutex);
	ovalue += v;
	ret = ovalue;
	pthread_cond_broadcast(&ocond);
	pthread_mutex_unlock(&omutex);
	return ret;
}

int BCondValue::decrement(int v){
	int	ret;
	
	pthread_mutex_lock(&omutex);
	ovalue -= v;
	ret = ovalue;
	pthread_cond_broadcast(&ocond);
	pthread_mutex_unlock(&omutex);
	return ret;
}
		
int BCondValue::waitMoreThanOrEqual(int v, int decrement, int timeOutUs){
	int		ret = 0;
	struct timeval	tv;
	struct timespec	ts;
	
	pthread_mutex_lock(&omutex);
	if(timeOutUs){
		gettimeofday(&tv, 0);
		ts.tv_sec = tv.tv_sec + (tv.tv_usec + timeOutUs) / 1000000;
		ts.tv_nsec = ((tv.tv_usec + timeOutUs) % 1000000) * 1000;
	
		while(!ret && (ovalue < v))
			ret = pthread_cond_timedwait(&ocond, &omutex, &ts);
	}
	else {
		while(ovalue < v)
			pthread_cond_wait(&ocond, &omutex);
	}
	
	if(ret == 0)
		ovalue -= decrement;
		
	pthread_mutex_unlock(&omutex);
	return ret;
}

int BCondValue::waitLessThanOrEqual(int v, int increment, int timeOutUs){
	int		ret = 0;
	struct timeval	tv;
	struct timespec	ts;
	
	pthread_mutex_lock(&omutex);
	if(timeOutUs){
		gettimeofday(&tv, 0);
		ts.tv_sec = tv.tv_sec + (tv.tv_usec + timeOutUs) / 1000000;
		ts.tv_nsec = ((tv.tv_usec + timeOutUs) % 1000000) * 1000;
	
		while(!ret && (ovalue > v))
			ret = pthread_cond_timedwait(&ocond, &omutex, &ts);
	}
	else {
		while(ovalue > v)
			pthread_cond_wait(&ocond, &omutex);
	}

	if(ret == 0)
		ovalue += increment;
		
	pthread_mutex_unlock(&omutex);
	return ret;
}

int BCondValue::waitLessThan(int v, int timeOutUs){
	int		ret = 0;
	struct timeval	tv;
	struct timespec	ts;
	
	pthread_mutex_lock(&omutex);
	if(timeOutUs){
		gettimeofday(&tv, 0);
		ts.tv_sec = tv.tv_sec + (tv.tv_usec + timeOutUs) / 1000000;
		ts.tv_nsec = ((tv.tv_usec + timeOutUs) % 1000000) * 1000;
	
		while(!ret && (ovalue >= v))
			ret = pthread_cond_timedwait(&ocond, &omutex, &ts);
	}
	else {
		while(ovalue >= v)
			pthread_cond_wait(&ocond, &omutex);
	}
	pthread_mutex_unlock(&omutex);
	return ret;
}





BCondBool::BCondBool(){
	pthread_mutex_init(&omutex, 0);
	pthread_cond_init(&ocond, 0);
	ovalue = 0;
}

BCondBool::~BCondBool(){
	pthread_cond_destroy(&ocond);
	pthread_mutex_destroy(&omutex);
}

int BCondBool::set(){
	int	ret;
	
	pthread_mutex_lock(&omutex);
	ret = ovalue = 1;
	pthread_cond_broadcast(&ocond);
	pthread_mutex_unlock(&omutex);
	return ret;
}

int BCondBool::clear(){
	int	ret;
	
	pthread_mutex_lock(&omutex);
	ret = ovalue = 0;
	pthread_mutex_unlock(&omutex);
	return ret;
}
		
int BCondBool::value(){
	return ovalue;
}

int BCondBool::wait(){
	int ret = 0;
	
	pthread_mutex_lock(&omutex);
	while(ovalue != 1)
		ret = pthread_cond_wait(&ocond, &omutex);
	pthread_mutex_unlock(&omutex);
	return ret;
}

int BCondBool::timedWait(int timeOutUs){
	int		ret = 0;
	struct timeval	tv;
	struct timespec	ts;
	
	gettimeofday(&tv, 0);
	ts.tv_sec = tv.tv_sec + timeOutUs / 1000000;
	ts.tv_nsec = (tv.tv_usec + timeOutUs % 1000000) * 1000;
	ts.tv_sec += (ts.tv_nsec / 1000000000);
	ts.tv_nsec %=  1000000000;
	
	pthread_mutex_lock(&omutex);
	while(ovalue != 1){
		ret = pthread_cond_timedwait(&ocond, &omutex, &ts);
		if(ret == ETIMEDOUT)
			break;
	}
	pthread_mutex_unlock(&omutex);
	return ret;
}



BCondWrap::BCondWrap(){
	pthread_mutex_init(&omutex, 0);
	pthread_cond_init(&ocond, 0);
	ovalue = 0;
}

BCondWrap::~BCondWrap(){
	pthread_cond_destroy(&ocond);
	pthread_mutex_destroy(&omutex);
}

void BCondWrap::setValue(uint32_t value){
	pthread_mutex_lock(&omutex);
	ovalue = value;
	pthread_cond_broadcast(&ocond);
	pthread_mutex_unlock(&omutex);
}

uint32_t BCondWrap::value(){
	return ovalue;
}

uint32_t BCondWrap::increment(uint32_t v){
	int	ret;
	
	pthread_mutex_lock(&omutex);
	ovalue += v;
	ret = ovalue;
	pthread_cond_broadcast(&ocond);
	pthread_mutex_unlock(&omutex);
	return ret;
}

uint32_t BCondWrap::decrement(uint32_t v){
	int	ret;
	
	pthread_mutex_lock(&omutex);
	ovalue -= v;
	ret = ovalue;
	pthread_cond_broadcast(&ocond);
	pthread_mutex_unlock(&omutex);
	return ret;
}
		
int BCondWrap::waitMoreThanOrEqual(uint32_t v, uint32_t decrement, uint32_t timeOutUs){
	int		ret = 0;
	struct timeval	tv;
	struct timespec	ts;
	
	pthread_mutex_lock(&omutex);
	if(timeOutUs){
		gettimeofday(&tv, 0);
		ts.tv_sec = tv.tv_sec + (tv.tv_usec + timeOutUs) / 1000000;
		ts.tv_nsec = ((tv.tv_usec + timeOutUs) % 1000000) * 1000;
	
		while(!ret && (diff(v) < 0))
			ret = pthread_cond_timedwait(&ocond, &omutex, &ts);
	}
	else {
		while(diff(v) < 0)
			pthread_cond_wait(&ocond, &omutex);
	}

	if(ret == 0)
		ovalue -= decrement;
		
	pthread_mutex_unlock(&omutex);
	return ret;
}

int BCondWrap::waitLessThanOrEqual(uint32_t v, uint32_t increment, uint32_t timeOutUs){
	int		ret = 0;
	struct timeval	tv;
	struct timespec	ts;
	
	pthread_mutex_lock(&omutex);
	if(timeOutUs){
		gettimeofday(&tv, 0);
		ts.tv_sec = tv.tv_sec + (tv.tv_usec + timeOutUs) / 1000000;
		ts.tv_nsec = ((tv.tv_usec + timeOutUs) % 1000000) * 1000;
	
		while(!ret && (diff(v) > 0))
			ret = pthread_cond_timedwait(&ocond, &omutex, &ts);
	}
	else {
		while(diff(v) > 0)
			pthread_cond_wait(&ocond, &omutex);
	}

	if(ret == 0)
		ovalue += increment;
		
	pthread_mutex_unlock(&omutex);
	return ret;
}

int BCondWrap::waitLessThan(uint32_t v, uint32_t timeOutUs){
	int		ret = 0;
	struct timeval	tv;
	struct timespec	ts;
	
	pthread_mutex_lock(&omutex);
	if(timeOutUs){
		gettimeofday(&tv, 0);
		ts.tv_sec = tv.tv_sec + (tv.tv_usec + timeOutUs) / 1000000;
		ts.tv_nsec = ((tv.tv_usec + timeOutUs) % 1000000) * 1000;
	
		while(!ret && (diff(v) >= 0))
			ret = pthread_cond_timedwait(&ocond, &omutex, &ts);
	}
	else {
		while(diff(v) >= 0)
			pthread_cond_wait(&ocond, &omutex);
	}
	pthread_mutex_unlock(&omutex);
	return ret;
}

int BCondWrap::diff(uint32_t v){
	return (ovalue - v);
}



BCondResource::BCondResource(){
	pthread_mutex_init(&omutex, 0);
	pthread_cond_init(&ocond, 0);
	olock = 0;
	ouse = 0;
}

BCondResource::~BCondResource(){
	pthread_cond_destroy(&ocond);
	pthread_mutex_destroy(&omutex);
}

int BCondResource::lock(uint32_t timeOutUs){
	int		ret = 0;
	struct timespec	ts = getTimeout(timeOutUs);
	
	pthread_mutex_lock(&omutex);
	olock = 1;
	while(ouse != 0){
		if(timeOutUs){
			ret = pthread_cond_timedwait(&ocond, &omutex, &ts);
			if(ret == ETIMEDOUT){
				olock = 0;
				break;
			}
		}
		else {
			 pthread_cond_wait(&ocond, &omutex);
		}
	}
	pthread_mutex_unlock(&omutex);
	return ret;
}

int BCondResource::unlock(){
	int	ret = 0;
	
	pthread_mutex_lock(&omutex);
	olock = 0;
	pthread_mutex_unlock(&omutex);
	pthread_cond_broadcast(&ocond);
	return ret;
}
		
int BCondResource::start(uint32_t timeOutUs){
	int		ret = 0;
	struct timespec	ts = getTimeout(timeOutUs);
	
	pthread_mutex_lock(&omutex);
	while(olock == 1){
		if(timeOutUs){
			ret = pthread_cond_timedwait(&ocond, &omutex, &ts);
			if(ret == ETIMEDOUT){
				pthread_mutex_unlock(&omutex);
				return ret;
			}
		}
		else {
			 pthread_cond_wait(&ocond, &omutex);
		}
	}
	
	ouse++;
	pthread_mutex_unlock(&omutex);
	return ret;
}

int BCondResource::end(){
	int		ret = 0;
	
	pthread_mutex_lock(&omutex);
	if(--ouse < 0)
		ouse = 0;

	pthread_mutex_unlock(&omutex);
	pthread_cond_broadcast(&ocond);
	return ret;
}

int BCondResource::locked(){
	return olock;
}

int BCondResource::inUse(){
	return ouse;
}
