summaryrefslogtreecommitdiffstats
path: root/raul
diff options
context:
space:
mode:
authorDavid Robillard <d@drobilla.net>2012-04-28 19:09:04 +0000
committerDavid Robillard <d@drobilla.net>2012-04-28 19:09:04 +0000
commit9614577f15ae26ef592e704c3ee3020b615e4697 (patch)
treedcb68e918525e6023cb3eac26bc27fa133a63e0c /raul
parent1b29f20d73d839c324cf4ce2df61d2e857617383 (diff)
downloadraul-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.hpp249
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