#ifndef WIBBLE_SYS_MUTEX_H #define WIBBLE_SYS_MUTEX_H /* * Encapsulated pthread mutex and condition * * Copyright (C) 2003--2006 Enrico Zini * * 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 #include #ifdef POSIX #include #endif #ifdef _WIN32 #define FALSE 0 #include #include #include #endif #include namespace wibble { namespace sys { /** * pthread mutex wrapper; WARNING: the class allows copying and assignment, * but this is not always safe. You should never copy a locked mutex. It is * however safe to copy when there is no chance of any of the running threads * using the mutex. */ 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"); } /// Lock the mutex /// Normally it's better to use MutexLock 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"); } /// Unlock the mutex /// Normally it's better to use MutexLock 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"); } /// Reinitialize the 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; }; /** * Acquire a mutex lock, RAII-style */ 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"); } /// Wake up one process waiting on the 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"); } /// Wake up all processes waiting on the 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"); } /** * Wait on the condition, locking with l. l is unlocked before waiting and * locked again before returning. */ 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 /** * Wait on the condition, locking with l. l is unlocked before waiting and * locked again before returning. If the time abstime is reached before * the condition is signaled, then l is locked and the function returns * false. * * @returns * true if the wait succeeded, or false if the timeout was reached before * the condition is signaled. */ bool wait(MutexLock& l, const struct timespec& abstime); #endif }; } } // vim:set ts=4 sw=4: #endif