// Copyright (C) 1999-2005 Open Source Telecom Corporation.
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; either version 2 of the License, or
// (at your option) any later version.
// 
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
// 
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software 
// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
// 
// As a special exception, you may use this file as part of a free software
// library without restriction.  Specifically, if other files instantiate
// templates or use macros or inline functions from this file, or you compile
// this file and link it with other files to produce an executable, this
// file does not by itself cause the resulting executable to be covered by
// the GNU General Public License.  This exception does not however    
// invalidate any other reasons why the executable file might be covered by
// the GNU General Public License.    
//
// This exception applies only to the code released under the name GNU
// Common C++.  If you copy code from other releases into a copy of GNU
// Common C++, as the General Public License permits, the exception does
// not apply to the code that you add in this way.  To avoid misleading
// anyone as to the status of such modified files, you must delete
// this exception notice from them.
//
// If you write modifications of your own for GNU Common C++, it is your choice
// whether to permit this exception to apply to your modifications.
// If you do not wish that, delete this exception notice.
//

#include <cc++/config.h>
#include <cc++/export.h>
#include <cc++/exception.h>
#include <cc++/thread.h>
#include "private.h"
#include <cc++/slog.h>
#include <iostream>
#include <cerrno>

#ifdef HAVE_GCC_CXX_BITS_ATOMIC
using namespace __gnu_cxx;
#endif

#ifdef	CCXX_NAMESPACES
namespace ost {
#endif

bool Mutex::_debug = false;

ThreadLock::ThreadLock()
{
#ifdef HAVE_PTHREAD_RWLOCK
	pthread_rwlockattr_t attr;
	
	pthread_rwlockattr_init(&attr);
	if(pthread_rwlock_init(&_lock, &attr))
	{
#ifdef	CCXX_EXCEPTIONS
		if(Thread::getException() == Thread::throwObject)
			throw(this);
#ifdef	COMMON_STD_EXCEPTION
		else if(Thread::getException() == Thread::throwException)
			throw(SyncException("Mutex constructor failure"));
#endif
#endif
	}
#endif
}

ThreadLock::~ThreadLock()
{
#ifdef HAVE_PTHREAD_RWLOCK
	pthread_rwlock_destroy(&_lock);
#endif
}

void ThreadLock::readLock(void)
{
#ifdef HAVE_PTHREAD_RWLOCK
	pthread_rwlock_rdlock(&_lock);
#else
	mutex.enterMutex();
#endif
}

void ThreadLock::writeLock(void)
{
#ifdef HAVE_PTHREAD_RWLOCK
	pthread_rwlock_wrlock(&_lock);
#else
	mutex.enterMutex();
#endif
}

void ThreadLock::unlock(void)
{
#ifdef HAVE_PTHREAD_RWLOCK
	pthread_rwlock_unlock(&_lock);
#else
	mutex.leaveMutex();
#endif
}

bool ThreadLock::tryReadLock(void)
{
#ifdef	HAVE_PTHREAD_RWLOCK
	if(pthread_rwlock_tryrdlock(&_lock))
		return false;
	return true;
#else
	return mutex.tryEnterMutex();
#endif
}

bool ThreadLock::tryWriteLock(void)
{
#ifdef	HAVE_PTHREAD_RWLOCK
	if(pthread_rwlock_trywrlock(&_lock))
		return false;
	return true;
#else
	return mutex.tryEnterMutex();
#endif
}

#ifndef WIN32
Conditional::Conditional(const char *id) 
{
	pthread_mutexattr_t attr;
	pthread_mutexattr_init(&attr);
	pthread_mutex_init(&_mutex, &attr);
	pthread_mutexattr_destroy(&attr);
	if(pthread_cond_init(&_cond, NULL) && Thread::getException() == Thread::throwObject)
		THROW(this);
}

Conditional::~Conditional()
{
	pthread_cond_destroy(&_cond);
	pthread_mutex_destroy(&_mutex);
}

bool Conditional::tryEnterMutex(void)
{
        if(pthread_mutex_trylock(&_mutex) != 0)
		return false;
	return true;
}

void Conditional::enterMutex(void)
{
        pthread_mutex_lock(&_mutex);
}

void Conditional::leaveMutex(void)
{
        pthread_mutex_unlock(&_mutex);
}

void Conditional::signal(bool broadcast)
{
	if(broadcast)
		pthread_cond_broadcast(&_cond);
	else
		pthread_cond_signal(&_cond);
}

bool Conditional::wait(timeout_t timeout, bool locked)
{
	struct timespec ts;
	int rc;

	if(!locked)
		enterMutex();
	if(!timeout)
	{
		pthread_cond_wait(&_cond, &_mutex);
		if(!locked)
			leaveMutex();
		return true;
	}
	getTimeout(&ts, timeout);
	rc = pthread_cond_timedwait(&_cond, &_mutex, &ts);
	if(!locked)
		leaveMutex();
	if(rc == ETIMEDOUT)
		return false;
	return true;
}

#endif

#ifndef WIN32
Mutex::Mutex(const char *name)
{
	pthread_mutexattr_t _attr;

	pthread_mutexattr_init(&_attr);
#ifdef	PTHREAD_MUTEXTYPE_RECURSIVE
	pthread_mutexattr_settype(&_attr, PTHREAD_MUTEXTYPE_RECURSIVE);
#endif
	pthread_mutex_init(&_mutex, &_attr);
	pthread_mutexattr_destroy(&_attr);

#ifndef	PTHREAD_MUTEXTYPE_RECURSIVE
	_level = 0;
	_tid = NULL;
#endif
	_name = name;
}

Mutex::~Mutex()
{
	pthread_mutex_destroy(&_mutex);
}

#ifdef PTHREAD_MUTEXTYPE_RECURSIVE

bool Mutex::tryEnterMutex(void)
{
	return (pthread_mutex_trylock(&_mutex) == 0) ? true : false;
}

void Mutex::enterMutex(void)
{
	if(_debug && _name)
		slog.debug() << Thread::get()->getName() 
			<< ": entering " << _name << std::endl;

	pthread_mutex_lock(&_mutex);
}

void Mutex::leaveMutex(void)
{
	pthread_mutex_unlock(&_mutex);
	if(_debug && _name)
		slog.debug() << Thread::get()->getName() 
			<< ": leaving" << _name << std::endl;

}

#else // !PTHREAD_MUTEXTYPE_RECURSIVE

void Mutex::enterMutex(void)
{
	if(_tid == Thread::get())
	{
		++_level;
		return;
	}
	if(_debug && _name)
		cerr << Thread::get()->getName() << ": entering" << _name << endl;

	pthread_mutex_lock(&_mutex);
	++_level;
	_tid = Thread::get();
}

void Mutex::leaveMutex(void)
{
	if(_tid != Thread::get())
		return;
	if(--_level > 0)
		return;
	_tid = NULL;
	_level = 0;
	pthread_mutex_unlock(&_mutex);
    if(_debug && _name)
		cerr << Thread::get()->getName() << ": leaving" << _name << endl;
}

bool Mutex::tryEnterMutex(void)
{
	if(_tid == Thread::get())
	{
		++_level;
		return true;
	}
	if ( pthread_mutex_trylock(&_mutex) != 0 )
		return false;
	_tid = Thread::get();
	++_level;
	return true;
}
#endif

#else // WIN32

Mutex::Mutex(const char *name)
#ifdef MUTEX_UNDERGROUND_WIN32_MUTEX 
	:_mutex(0)
#endif // MUTEX_UNDERGROUND_WIN32_MUTEX
{
#ifdef MUTEX_UNDERGROUND_WIN32_CRITICALSECTION 
#if _WIN32_WINNT >= 0x0403
	if(!InitializeCriticalSectionAndSpinCount(&_criticalSection, 4000))
	{
		THROW(this);
	}
#elif _WIN32_WINNT >= 0x0400
	// can rise STATUS_NO_MEMORY exception in low memory situations.
	InitializeCriticalSection(&_criticalSection); 
#else
#error "Not supported Windows version"
#endif
#endif // MUTEX_UNDERGROUND_WIN32_CRITICALSECTION

#ifdef MUTEX_UNDERGROUND_WIN32_MUTEX 
	_mutex = ::CreateMutex(NULL,FALSE,NULL);
	if(!_mutex)
		THROW(this);
#endif // MUTEX_UNDERGROUND_WIN32_MUTEX


	_name = name;
}

void Mutex::enterMutex(void)
{
	if(_debug && _name)
		slog.debug() << Thread::get()->getName() 
			<< ": entering " << _name << std::endl;

#ifdef MUTEX_UNDERGROUND_WIN32_CRITICALSECTION 
	::EnterCriticalSection(&_criticalSection);
#endif // MUTEX_UNDERGROUND_WIN32_CRITICALSECTION

#ifdef MUTEX_UNDERGROUND_WIN32_MUTEX 
	Thread::waitThread(_mutex, INFINITE);
#endif // MUTEX_UNDERGROUND_WIN32_MUTEX

}

bool Mutex::tryEnterMutex(void)
{
#ifdef MUTEX_UNDERGROUND_WIN32_CRITICALSECTION 
	return (::TryEnterCriticalSection(&_criticalSection) == TRUE);
#endif // MUTEX_UNDERGROUND_WIN32_CRITICALSECTION

#ifdef MUTEX_UNDERGROUND_WIN32_MUTEX
	return (Thread::waitThread(_mutex, 0) == WAIT_OBJECT_0);
#endif // MUTEX_UNDERGROUND_WIN32_MUTEX
}

Mutex::~Mutex()
{
#ifdef MUTEX_UNDERGROUND_WIN32_CRITICALSECTION 
	::DeleteCriticalSection(&_criticalSection);
#endif // MUTEX_UNDERGROUND_WIN32_CRITICALSECTION

#ifdef MUTEX_UNDERGROUND_WIN32_MUTEX 
	::CloseHandle(_mutex);
#endif // MUTEX_UNDERGROUND_WIN32_MUTEX

}
	
void Mutex::leaveMutex(void)
{
#ifdef MUTEX_UNDERGROUND_WIN32_CRITICALSECTION 
	::LeaveCriticalSection(&_criticalSection);
#endif // MUTEX_UNDERGROUND_WIN32_CRITICALSECTION

#ifdef MUTEX_UNDERGROUND_WIN32_MUTEX 
	if (!ReleaseMutex(_mutex))
		THROW(this);
#endif // MUTEX_UNDERGROUND_WIN32_MUTEX

	if(_debug && _name)
		slog.debug() << Thread::get()->getName() 
			<< ": leaving" << _name << std::endl;
}
#endif	// WIN32 MUTEX

#ifdef WIN32
MutexCounter::MutexCounter(const char *id) : Mutex(id)
{
	counter = 0;
};
#endif

MutexCounter::MutexCounter(int initial, const char *id) : Mutex(id)
{
	counter = initial;
}

int operator++(MutexCounter &mc)
{
	int rtn;

	mc.enterMutex();
	rtn = mc.counter++;
	mc.leaveMutex();
	return rtn;
}

// ??? why cannot be < 0 ???
int operator--(MutexCounter &mc)
{
	int rtn = 0;

	mc.enterMutex();
	if(mc.counter)
	{
		rtn = --mc.counter;
		if(!rtn)
		{
			mc.leaveMutex();
			THROW(mc);
		}
	}
	mc.leaveMutex();
	return rtn;
}

#ifndef CCXX_USE_WIN32_ATOMIC
#ifdef	HAVE_ATOMIC
AtomicCounter::AtomicCounter()
{
#if     defined(HAVE_ATOMIC_AIX) || defined(HAVE_GCC_BITS_ATOMIC) \
    || defined(HAVE_GCC_CXX_BITS_ATOMIC)
	counter = 0;
#else
	atomic.counter = 0;
#endif
}

AtomicCounter::AtomicCounter(int value)
{
#if     defined(HAVE_ATOMIC_AIX) || defined(HAVE_GCC_BITS_ATOMIC) \
    || defined(HAVE_GCC_CXX_BITS_ATOMIC)
	counter = 0;
#else
	atomic.counter = value;
#endif
}

AtomicCounter::~AtomicCounter() {};

int AtomicCounter::operator++(void)
{
#ifdef	HAVE_ATOMIC_AIX
	return fetch_and_add((atomic_p)&counter, 1);
#elif   defined(HAVE_GCC_BITS_ATOMIC) || defined(HAVE_GCC_CXX_BITS_ATOMIC)
	// Modified by JCE from 2v1.3.8 source, 30/Mar/2005
	// BUG FIX: __exchange_and_add() does not seem to return updated <counter>
	__exchange_and_add(&counter, 1);
	return counter;
	// end modification by JCE
#else
	atomic_inc(&atomic);
	return atomic_read(&atomic);
#endif
}

int AtomicCounter::operator--(void)
{
#ifdef	HAVE_ATOMIC_AIX
	return fetch_and_add((atomic_p)&counter, -1);
#elif   defined(HAVE_GCC_BITS_ATOMIC) || defined(HAVE_GCC_CXX_BITS_ATOMIC)
	// Modified by JCE from 2v1.3.8 source, 30/Mar/2005
	// BUG FIX: __exchange_and_add() does not seem to return updated <counter>
	__exchange_and_add(&counter, -1);
	return counter;
	// end modification by JCE
#else
	int chk = atomic_dec_and_test(&atomic);
	if(chk)
		return 0;
	chk = atomic_read(&atomic);
	if(!chk)
		++chk;
	return chk;
#endif
}

int AtomicCounter::operator+=(int change)
{
#ifdef	HAVE_ATOMIC_AIX
	return fetch_and_add((atomic_p)&counter, change);
#elif   defined(HAVE_GCC_BITS_ATOMIC) || defined(HAVE_GCC_CXX_BITS_ATOMIC)
	// Modified by JCE from 2v1.3.8 source, 30/Mar/2005
	// BUG FIX: __exchange_and_add() does not seem to return updated <counter>
	__exchange_and_add(&counter, change);
	return counter;
	// end modification by JCE
#else
	atomic_add(change, &atomic);
	return atomic_read(&atomic);
#endif
}

int AtomicCounter::operator-=(int change)
{
#ifdef	HAVE_ATOMIC_AIX
	return fetch_and_add((atomic_p)&counter, -change);
#elif   defined(HAVE_GCC_BITS_ATOMIC) || defined(HAVE_GCC_CXX_BITS_ATOMIC)
	// Modified by JCE from 2v1.3.8 source, 30/Mar/2005
	// BUG FIX: __exchange_and_add() does not seem to return updated <counter>
	__exchange_and_add(&counter, -change);
	return counter;
	// end modification by JCE
#else
	atomic_sub(change, &atomic);
	return atomic_read(&atomic);
#endif
}

int AtomicCounter::operator+(int change)
{
#if     defined(HAVE_ATOMIC_AIX) || defined(HAVE_GCC_BITS_ATOMIC) \
    || defined(HAVE_GCC_CXX_BITS_ATOMIC)
	return counter + change;
#else
	return atomic_read(&atomic) + change;
#endif
}

int AtomicCounter::operator-(int change)
{
#if     defined(HAVE_ATOMIC_AIX) || defined(HAVE_GCC_BITS_ATOMIC) \
    || defined(HAVE_GCC_CXX_BITS_ATOMIC)
	return counter - change;
#else
	return atomic_read(&atomic) - change;
#endif
}

int AtomicCounter::operator=(int value)
{
#if     defined(HAVE_ATOMIC_AIX) || defined(HAVE_GCC_BITS_ATOMIC) \
    || defined(HAVE_GCC_CXX_BITS_ATOMIC)
	return counter = value;
#else
	atomic_set(&atomic, value);
	return atomic_read(&atomic);
#endif
}

bool AtomicCounter::operator!(void)
{
#if     defined(HAVE_ATOMIC_AIX) || defined(HAVE_GCC_BITS_ATOMIC) \
    || defined(HAVE_GCC_CXX_BITS_ATOMIC)
	int value = counter;
#else
	int value = atomic_read(&atomic);
#endif
	if(value)
		return false;
	return true;
}

AtomicCounter::operator int()
{
#if     defined(HAVE_ATOMIC_AIX) || defined(HAVE_GCC_BITS_ATOMIC) \
    || defined(HAVE_GCC_CXX_BITS_ATOMIC)
	return counter;
#else
	return atomic_read(&atomic);
#endif
}

#else // !HAVE_ATOMIC

AtomicCounter::AtomicCounter()
{
	counter = 0;

        pthread_mutexattr_t _attr;
        pthread_mutexattr_init(&_attr);
        pthread_mutex_init(&_mutex, &_attr);
        pthread_mutexattr_destroy(&_attr);
}

AtomicCounter::AtomicCounter(int value)
{
	counter = value;
	
        pthread_mutexattr_t _attr;
        pthread_mutexattr_init(&_attr);
        pthread_mutex_init(&_mutex, &_attr);
        pthread_mutexattr_destroy(&_attr);
}

AtomicCounter::~AtomicCounter()
{
        pthread_mutex_destroy(&_mutex);
}

int AtomicCounter::operator++(void)
{
	int value;

	pthread_mutex_lock(&_mutex);
	value = ++counter;
	pthread_mutex_unlock(&_mutex);
	return value;
}

int AtomicCounter::operator--(void)
{
	int value;
	pthread_mutex_lock(&_mutex);
	value = --counter;
	pthread_mutex_unlock(&_mutex);
	return value;
}

int AtomicCounter::operator+=(int change)
{
	int value;
	pthread_mutex_lock(&_mutex);
	counter += change;
	value = counter;
	pthread_mutex_unlock(&_mutex);
	return value;
}

int AtomicCounter::operator-=(int change)
{
	int value;
	pthread_mutex_lock(&_mutex);
	counter -= change;
	value = counter;
	pthread_mutex_unlock(&_mutex);
	return value;
}

int AtomicCounter::operator+(int change)
{
	int value;
	pthread_mutex_lock(&_mutex);
	value = counter + change;
	pthread_mutex_unlock(&_mutex);
	return value;
}

int AtomicCounter::operator-(int change)
{
	int value;
	pthread_mutex_lock(&_mutex);
	value = counter - change;
	pthread_mutex_unlock(&_mutex);
	return value;
}

AtomicCounter::operator int()
{
	int value;
	pthread_mutex_lock(&_mutex);
	value = counter;
	pthread_mutex_unlock(&_mutex);
	return value;
}

int AtomicCounter::operator=(int value)
{
	int ret;
	pthread_mutex_lock(&_mutex);
	ret = counter;
	counter = value;
	pthread_mutex_unlock(&_mutex);
	return ret;
}

bool AtomicCounter::operator!(void)
{
	int value;
	pthread_mutex_lock(&_mutex);
	value = counter;
	pthread_mutex_unlock(&_mutex);
	if(value)
		return false;
	return true;
}
#endif // HAVE_ATOMIC
#else // WIN32
int AtomicCounter::operator+=(int change)
{
	// FIXME: enhance with InterlockExchangeAdd
	while(--change>=0)
		InterlockedIncrement(&atomic);
	
	return atomic;
}

int AtomicCounter::operator-=(int change)
{
	// FIXME: enhance with InterlockExchangeAdd
	while(--change>=0)
		InterlockedDecrement(&atomic);

	return atomic;
}
#endif // !WIN32

#ifdef	CCXX_NAMESPACES
}
#endif

/** EMACS **
 * Local variables:
 * mode: c++
 * c-basic-offset: 8
 * End:
 */

