blob: e6f2bfa2e027c2975c00db7228060e022448f4fc [file] [log] [blame]
// Copyright (C) 1999-2005 Open Source Telecom Corporation.
// Copyright (C) 2006-2010 David Sugar, Tycho Softworks
//
// 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>
#include <cstdlib>
#ifdef HAVE_GCC_CXX_BITS_ATOMIC
using namespace __gnu_cxx;
#endif
#ifdef CCXX_NAMESPACES
namespace ost {
using namespace std;
#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)) {
pthread_rwlockattr_destroy(&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
}
else
pthread_rwlockattr_destroy(&attr);
#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)
std::cerr << Thread::get()->getName() << ": entering" << _name << std::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)
std::cerr << Thread::get()->getName() << ": leaving" << _name << std::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: 4
* End:
*/