mutex.h
Go to the documentation of this file.
#ifndef WIBBLE_SYS_MUTEX_H
#define WIBBLE_SYS_MUTEX_H
/*
* Encapsulated pthread mutex and condition
*
* Copyright (C) 2003--2006 Enrico Zini <enrico@debian.org>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* This library 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include <wibble/sys/macros.h>
#include <wibble/exception.h>
#ifdef POSIX
#include <pthread.h>
#endif
#ifdef _WIN32
#include <windows.h>
#include <queue>
#include <time.h>
#endif
#include <errno.h>
namespace wibble {
namespace sys {
class Mutex
{
protected:
#ifdef POSIX
pthread_mutex_t mutex;
#endif
#ifdef _WIN32
HANDLE mutex;
bool singlylocking;
#endif
public:
Mutex(bool recursive = false)
{
int res = 0;
#ifdef POSIX
pthread_mutexattr_t attr;
pthread_mutexattr_init( &attr );
if ( recursive ) {
#if (__APPLE__ || __xlC__)
pthread_mutexattr_settype( &attr, PTHREAD_MUTEX_RECURSIVE );
#else
pthread_mutexattr_settype( &attr, PTHREAD_MUTEX_RECURSIVE_NP );
#endif
} else {
#ifndef NDEBUG
pthread_mutexattr_settype( &attr, PTHREAD_MUTEX_ERRORCHECK_NP );
#endif
}
res = pthread_mutex_init(&mutex, &attr);
#endif
#ifdef _WIN32
mutex = CreateMutex( NULL, FALSE, NULL );
singlylocking = false;
if (mutex == NULL)
res = (int)GetLastError();
#endif
if (res != 0)
throw wibble::exception::System(res, "creating pthread mutex");
}
Mutex( const Mutex & )
{
int res = 0;
#ifdef POSIX
pthread_mutexattr_t attr;
pthread_mutexattr_init( &attr );
res = pthread_mutex_init(&mutex, &attr);
#endif
#ifdef _WIN32
mutex = CreateMutex(NULL, FALSE, NULL);
singlylocking = false;
if(mutex == NULL)
res = (int)GetLastError();
#endif
if (res != 0)
throw wibble::exception::System(res, "creating pthread mutex");
}
~Mutex()
{
int res = 0;
#ifdef POSIX
res = pthread_mutex_destroy(&mutex);
#endif
#ifdef _WIN32
if(!CloseHandle(mutex))
res = (int)GetLastError();
#endif
if (res != 0)
throw wibble::exception::System(res, "destroying pthread mutex");
}
bool trylock()
{
int res = 0;
#ifdef POSIX
res = pthread_mutex_trylock(&mutex);
if ( res == EBUSY )
return false;
if ( res == 0 )
return true;
#endif
#ifdef _WIN32
DWORD dwWaitResult = !singlylocking ? WaitForSingleObject(mutex, 0) : WAIT_TIMEOUT;
if(dwWaitResult == WAIT_OBJECT_0)
return true;
if(dwWaitResult == WAIT_TIMEOUT)
return false;
res = (int)GetLastError();
#endif
throw wibble::exception::System(res, "(try)locking pthread mutex");
}
void lock()
{
int res = 0;
#ifdef POSIX
res = pthread_mutex_lock(&mutex);
#endif
#ifdef _WIN32
while(singlylocking)
Sleep(1);
if(WaitForSingleObject(mutex, INFINITE) != WAIT_OBJECT_0)
res = (int)GetLastError();
#endif
if (res != 0)
throw wibble::exception::System(res, "locking pthread mutex");
}
void unlock()
{
int res = 0;
#ifdef POSIX
res = pthread_mutex_unlock(&mutex);
#endif
#ifdef _WIN32
if(!ReleaseMutex(mutex))
res = (int)GetLastError();
#endif
if (res != 0)
throw wibble::exception::System(res, "unlocking pthread mutex");
}
void reinit()
{
#ifdef POSIX
if (int res = pthread_mutex_init(&mutex, 0))
throw wibble::exception::System(res, "reinitialising pthread mutex");
#endif
}
friend class Condition;
};
template< typename Mutex >
class MutexLockT
{
private:
// Disallow copy
MutexLockT(const MutexLockT&);
MutexLockT& operator=(const MutexLockT&);
public:
Mutex& mutex;
bool locked;
bool yield;
MutexLockT(Mutex& m) : mutex(m), locked( false ), yield( false ) {
mutex.lock();
locked = true;
}
~MutexLockT() {
if ( locked ) {
mutex.unlock();
checkYield();
}
}
void drop() {
mutex.unlock();
locked = false;
checkYield();
}
void reclaim() { mutex.lock(); locked = true; }
void setYield( bool y ) {
yield = y;
}
void checkYield() {
if ( yield )
#ifdef POSIX
sched_yield();
#elif _WIN32
Sleep(0);
#else
;
#endif
}
friend class Condition;
};
typedef MutexLockT< Mutex > MutexLock;
/*
* pthread condition wrapper.
*
* It works in association with a MutexLock.
*
* WARNING: the class allows copying and assignment; see Mutex: similar caveats
* apply. Do not copy or assign a Condition that may be in use.
*/
class Condition
{
protected:
#ifdef POSIX
pthread_cond_t cond;
#endif
#ifdef _WIN32
int waiters_count_; // number of waiting threads
CRITICAL_SECTION waiters_count_lock_;
HANDLE sema_; // semaphore used to queue up threads waiting for the condition
HANDLE waiters_done_;
// An auto-reset event used by the broadcast/signal thread to wait
// for all the waiting thread(s) to wake up and be released from the
// semaphore.
bool was_broadcast_;
// Keeps track of whether we were broadcasting or signaling. This
// allows us to optimize the code if we're just signaling.
#endif
public:
Condition()
{
int res = 0;
#ifdef POSIX
res = pthread_cond_init(&cond, 0);
#endif
#ifdef _WIN32
waiters_count_ = 0;
was_broadcast_ = false;
sema_ = CreateSemaphore(NULL, 0, 0x7fffffff, NULL);
InitializeCriticalSection(&waiters_count_lock_);
waiters_done_ = CreateEvent(NULL, FALSE, FALSE, NULL);
if(sema_ == NULL || waiters_done_ == NULL)
res = (int)GetLastError();
#endif
if (res != 0)
throw wibble::exception::System(res, "creating pthread condition");
}
Condition( const Condition & )
{
int res = 0;
#ifdef POSIX
res = pthread_cond_init(&cond, 0);
#endif
#ifdef _WIN32
waiters_count_ = 0;
was_broadcast_ = false;
sema_ = CreateSemaphore(NULL, 0, 0x7fffffff, NULL);
InitializeCriticalSection(&waiters_count_lock_);
waiters_done_ = CreateEvent(NULL, FALSE, FALSE, NULL);
if(sema_ == NULL || waiters_done_ == NULL)
res = (int)GetLastError();
#endif
if (res != 0)
throw wibble::exception::System(res, "creating pthread condition");
}
~Condition()
{
int res = 0;
#ifdef POSIX
res = pthread_cond_destroy(&cond);
#endif
#ifdef _WIN32
DeleteCriticalSection(&waiters_count_lock_);
if(!CloseHandle(sema_) || !CloseHandle(waiters_done_))
res = (int)GetLastError();
#endif
if (res != 0)
throw wibble::exception::System(res, "destroying pthread condition");
}
void signal()
{
int res = 0;
#ifdef POSIX
res = pthread_cond_signal(&cond);
#endif
#ifdef _WIN32
EnterCriticalSection(&waiters_count_lock_);
bool have_waiters = waiters_count_ > 0;
LeaveCriticalSection(&waiters_count_lock_);
// if there aren't any waiters, then this is a no-op
if(have_waiters && !ReleaseSemaphore(sema_, 1, 0))
res = (int)GetLastError();
#endif
if (res != 0)
throw wibble::exception::System(res, "signaling on a pthread condition");
}
void broadcast()
{
int res = 0;
#ifdef POSIX
res = pthread_cond_broadcast(&cond);
#endif
#ifdef _WIN32
for(bool once = true; once; once = false)
{
EnterCriticalSection(&waiters_count_lock_);
bool have_waiters = false;
if(waiters_count_ > 0) {
was_broadcast_ = true;
have_waiters = true;
}
if(have_waiters) {
if(!ReleaseSemaphore(sema_, waiters_count_, 0)) {
res = (int)GetLastError();
break;
}
LeaveCriticalSection(&waiters_count_lock_);
if(WaitForSingleObject(waiters_done_, INFINITE) != WAIT_OBJECT_0) {
res = (int)GetLastError();
break;
}
was_broadcast_ = false;
}
else
LeaveCriticalSection(&waiters_count_lock_);
}
#endif
if (res != 0)
throw wibble::exception::System(res, "broadcasting on a pthread condition");
}
void wait(MutexLock& l)
{
int res = 0;
#ifdef POSIX
res = pthread_cond_wait(&cond, &l.mutex.mutex);
#endif
#ifdef _WIN32
for(bool once = true; once; once = false)
{
EnterCriticalSection (&waiters_count_lock_);
waiters_count_++;
LeaveCriticalSection (&waiters_count_lock_);
if(SignalObjectAndWait(l.mutex.mutex, sema_, INFINITE, FALSE) != WAIT_OBJECT_0) {
res = (int)GetLastError();
break;
}
EnterCriticalSection (&waiters_count_lock_);
waiters_count_--;
bool last_waiter = was_broadcast_ && waiters_count_ == 0;
LeaveCriticalSection (&waiters_count_lock_);
if (last_waiter) {
if(SignalObjectAndWait (waiters_done_, l.mutex.mutex, INFINITE, FALSE) != WAIT_OBJECT_0)
{
res = (int)GetLastError();
break;
}
}
else {
if(WaitForSingleObject (l.mutex.mutex, INFINITE) != WAIT_OBJECT_0)
{
res = (int)GetLastError();
break;
}
}
}
#endif
if (res != 0)
throw wibble::exception::System(res, "waiting on a pthread condition");
}
void wait(Mutex& l)
{
int res = 0;
#ifdef POSIX
res = pthread_cond_wait(&cond, &l.mutex);
#endif
#ifdef _WIN32
for(bool once = true; once; once = false)
{
if(WaitForSingleObject(l.mutex, 0) == WAIT_OBJECT_0) {
l.singlylocking = true;
while(ReleaseMutex(l.mutex)) ;
if ((res = ((int)GetLastError() != 288))) //288 -> MUTEX_NOT_OWNED
break;
}
if(WaitForSingleObject(l.mutex, INFINITE) != WAIT_OBJECT_0) {
res = (int)GetLastError();
break;
}
l.singlylocking = false;
EnterCriticalSection (&waiters_count_lock_);
waiters_count_++;
LeaveCriticalSection (&waiters_count_lock_);
if(SignalObjectAndWait(l.mutex, sema_, INFINITE, FALSE) != WAIT_OBJECT_0) {
res = (int)GetLastError();
break;
}
EnterCriticalSection (&waiters_count_lock_);
waiters_count_--;
bool last_waiter = was_broadcast_ && waiters_count_ == 0;
LeaveCriticalSection (&waiters_count_lock_);
if(last_waiter) {
if(SignalObjectAndWait (waiters_done_, l.mutex, INFINITE, FALSE) != WAIT_OBJECT_0) {
res = (int)GetLastError();
break;
}
}
else {
if(WaitForSingleObject(l.mutex, INFINITE) != WAIT_OBJECT_0) {
res = (int)GetLastError();
break;
}
}
}
#endif
if (res != 0)
throw wibble::exception::System(res, "waiting on a pthread condition");
}
#ifdef POSIX
bool wait(MutexLock& l, const struct timespec& abstime);
#endif
};
}
}
// vim:set ts=4 sw=4:
#endif
wibble::exception::System
Base class for system exceptions.
Definition: exception.h:397
wibble::sys::Condition
Definition: mutex.h:260
wibble::sys::Condition::broadcast
void broadcast()
Wake up all processes waiting on the condition.
Definition: mutex.h:361
wibble::sys::Condition::Condition
Condition(const Condition &)
Definition: mutex.h:302
wibble::sys::Condition::wait
void wait(Mutex &l)
Definition: mutex.h:447
wibble::sys::Condition::signal
void signal()
Wake up one process waiting on the condition.
Definition: mutex.h:340
wibble::sys::Condition::Condition
Condition()
Definition: mutex.h:281
wibble::sys::Condition::~Condition
~Condition()
Definition: mutex.h:323
wibble::sys::Condition::wait
void wait(MutexLock &l)
Wait on the condition, locking with l.
Definition: mutex.h:403
wibble::sys::MutexLockT
Acquire a mutex lock, RAII-style.
Definition: mutex.h:201
wibble::sys::MutexLockT::MutexLockT
MutexLockT(Mutex &m)
Definition: mutex.h:212
wibble::sys::MutexLockT::reclaim
void reclaim()
Definition: mutex.h:229
wibble::sys::MutexLockT::mutex
Mutex & mutex
Definition: mutex.h:208
wibble::sys::MutexLockT::checkYield
void checkYield()
Definition: mutex.h:234
wibble::sys::MutexLockT::yield
bool yield
Definition: mutex.h:210
wibble::sys::MutexLockT::locked
bool locked
Definition: mutex.h:209
wibble::sys::MutexLockT::setYield
void setYield(bool y)
Definition: mutex.h:230
wibble::sys::MutexLockT::~MutexLockT
~MutexLockT()
Definition: mutex.h:217
wibble::sys::MutexLockT::drop
void drop()
Definition: mutex.h:224
wibble::sys::Mutex
pthread mutex wrapper; WARNING: the class allows copying and assignment, but this is not always safe.
Definition: mutex.h:48
wibble::sys::Mutex::~Mutex
~Mutex()
Definition: mutex.h:111
wibble::sys::Mutex::Mutex
Mutex(bool recursive=false)
Definition: mutex.h:60
wibble::sys::Mutex::lock
void lock()
Lock the mutex Normally it's better to use MutexLock.
Definition: mutex.h:150
wibble::sys::Mutex::trylock
bool trylock()
Definition: mutex.h:126
wibble::sys::Mutex::unlock
void unlock()
Unlock the mutex Normally it's better to use MutexLock.
Definition: mutex.h:169
wibble::sys::Mutex::Mutex
Mutex(const Mutex &)
Definition: mutex.h:91
wibble::sys::Mutex::reinit
void reinit()
Reinitialize the mutex.
Definition: mutex.h:185
exception.h
macros.h
wibble::sys::MutexLock
MutexLockT< Mutex > MutexLock
Definition: mutex.h:249
wibble
Definition: amorph.h:17