diff options
author | David Robillard <d@drobilla.net> | 2012-04-28 19:09:04 +0000 |
---|---|---|
committer | David Robillard <d@drobilla.net> | 2012-04-28 19:09:04 +0000 |
commit | 9614577f15ae26ef592e704c3ee3020b615e4697 (patch) | |
tree | dcb68e918525e6023cb3eac26bc27fa133a63e0c /raul | |
parent | 1b29f20d73d839c324cf4ce2df61d2e857617383 (diff) | |
download | raul-9614577f15ae26ef592e704c3ee3020b615e4697.tar.gz raul-9614577f15ae26ef592e704c3ee3020b615e4697.tar.bz2 raul-9614577f15ae26ef592e704c3ee3020b615e4697.zip |
Use more portable semaphore implementation, works in win32 and uses modern OSX API.
git-svn-id: http://svn.drobilla.net/lad/trunk/raul@4307 a436a847-0d15-0410-975c-d299462d15a1
Diffstat (limited to 'raul')
-rw-r--r-- | raul/Semaphore.hpp | 249 |
1 files changed, 172 insertions, 77 deletions
diff --git a/raul/Semaphore.hpp b/raul/Semaphore.hpp index 7df1817..4c6fd0b 100644 --- a/raul/Semaphore.hpp +++ b/raul/Semaphore.hpp @@ -1,5 +1,5 @@ /* This file is part of Raul. - * Copyright 2007-2011 David Robillard <http://drobilla.net> + * Copyright 2007-2012 David Robillard <http://drobilla.net> * * Raul 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 @@ -18,103 +18,198 @@ #ifndef RAUL_SEMAPHORE_HPP #define RAUL_SEMAPHORE_HPP +#include <stdexcept> + #ifdef __APPLE__ -#include <limits.h> -#include <CoreServices/CoreServices.h> +# include <mach/mach.h> +#elif defined(_WIN32) +# include <windows.h> #else -#include <semaphore.h> +# include <semaphore.h> +# include <errno.h> #endif -#include "raul/Noncopyable.hpp" - namespace Raul { -/** Counting semaphore. - * - * \ingroup raul - */ -class Semaphore : Noncopyable { +/** + Unnamed (process local) counting semaphore. + + The civilized person's synchronisation primitive. A counting semaphore is + an integer which is always non-negative, so, an attempted decrement (or + "wait") will block if the value is 0, until another thread does an increment + (or "post"). + + At least on Lignux, the main advantage of this is that it is fast and the + only safe way to reliably signal from a real-time audio thread. The + counting semantics also complement ringbuffers of events nicely. +*/ +class Semaphore +{ public: - inline Semaphore(unsigned int initial) { - #ifdef __APPLE__ - MPCreateSemaphore(UINT_MAX, initial, &_sem); - #else - sem_init(&_sem, 0, initial); - #endif + /** + Create a new semaphore. + + Chances are you want 1 wait() per 1 post(), an initial value of 0. + */ + inline Semaphore(unsigned initial) { + if (!init(initial)) { + throw std::runtime_error("Failed to create semaphore"); + } } inline ~Semaphore() { - #ifdef __APPLE__ - MPDeleteSemaphore(_sem); - #else - sem_destroy(&_sem); - #endif + destroy(); } - /** Destroy and reset the semaphore to an initial value. - * - * This must not be called while there are any waiters. - */ - inline void reset(unsigned int initial) { - #ifdef __APPLE__ - MPDeleteSemaphore(_sem); - MPCreateSemaphore(UINT_MAX, initial, &_sem); - #else - sem_destroy(&_sem); - sem_init(&_sem, 0, initial); - #endif + /** Destroy and reset to a new initial value. */ + inline void reset(unsigned initial) { + destroy(); + init(initial); } - /** Increment (and signal any waiters). - * - * Realtime safe. - */ - inline void post() { - #ifdef __APPLE__ - MPSignalSemaphore(_sem); - #else - sem_post(&_sem); - #endif - } + /** Post/Increment/Signal */ + inline void post(); - /** Wait until count is > 0, then decrement. - * - * Obviously not realtime safe. - */ - inline void wait() { - #ifdef __APPLE__ - MPWaitOnSemaphore(_sem, kDurationForever); - #else - /* Note that sem_wait always returns 0 in practice, except in - gdb (at least), where it returns nonzero, so the while is - necessary (and is the correct/safe solution in any case). - */ - while (sem_wait(&_sem) != 0) {} - #endif - } + /** Wait/Decrement. Returns false on error. */ + inline bool wait(); - /** Non-blocking version of wait(). - * - * \return true if decrement was successful (lock was acquired). - * - * Realtime safe? - */ - inline bool try_wait() { - #ifdef __APPLE__ - return MPWaitOnSemaphore(_sem, kDurationImmediate) == noErr; - #else - return (sem_trywait(&_sem) == 0); - #endif - } + /** Attempt Wait/Decrement. Returns true iff a decrement occurred. */ + inline bool try_wait(); private: - #ifdef __APPLE__ - MPSemaphoreID _sem; - #else + inline bool init(unsigned initial); + inline void destroy(); + +#if defined(__APPLE__) + semaphore_t _sem; // sem_t is a worthless broken mess on OSX +#elif defined(_WIN32) + HANDLE _sem; // types are overrated anyway +#else sem_t _sem; - #endif +#endif }; +#ifdef __APPLE__ + +inline bool +Semaphore::init(unsigned initial) +{ + if (semaphore_create(mach_task_self(), &sem->sem, SYNC_POLICY_FIFO, 0)) { + return false; + } + return true; +} + +inline void +Semaphore::destroy() +{ + semaphore_destroy(mach_task_self(), _sem); +} + +inline void +Semaphore::post() +{ + semaphore_signal(_sem); +} + +inline bool +Semaphore::wait() +{ + if (semaphore_wait(_sem) != KERN_SUCCESS) { + return false; + } + return true; +} + +inline bool +Semaphore::try_wait() +{ + const mach_timespec_t zero = { 0, 0 }; + return semaphore_timedwait(_sem, zero) == KERN_SUCCESS; +} + +#elif defined(_WIN32) + +inline bool +Semaphore::init(unsigned initial) +{ + if (!(_sem = CreateSemaphore(NULL, initial, LONG_MAX, NULL))) { + return false; + } + return true; +} + +inline void +Semaphore::destroy() +{ + CloseHandle(_sem); +} + +inline void +Semaphore::post() +{ + ReleaseSemaphore(_sem, 1, NULL); +} + +inline bool +Semaphore::wait() +{ + if (WaitForSingleObject(_sem, INFINITE) != WAIT_OBJECT_0) { + return false; + } + return true; +} + +inline bool +Semaphore::try_wait() +{ + return WaitForSingleObject(sem->sem, 0) == WAIT_OBJECT_0; +} + +#else /* !defined(__APPLE__) && !defined(_WIN32) */ + +inline bool +Semaphore::init(unsigned initial) +{ + if (sem_init(&_sem, 0, initial)) { + return false; + } + return true; +} + +inline void +Semaphore::destroy() +{ + sem_destroy(&_sem); +} + +inline void +Semaphore::post() +{ + sem_post(&_sem); +} + +inline bool +Semaphore::wait() +{ + while (sem_wait(&_sem)) { + if (errno != EINTR) { + return false; // We are all doomed + } + /* Otherwise, interrupted (rare/weird), so try again. */ + } + + return true; +} + +inline bool +Semaphore::try_wait() +{ + return (sem_trywait(&_sem) == 0); +} + +#endif + } // namespace Raul #endif // RAUL_SEMAPHORE_HPP |