summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--libs/ardour/ardour/lv2_plugin.h7
-rw-r--r--libs/ardour/ardour/worker.h90
-rw-r--r--libs/ardour/lv2/lv2plug.in/ns/ext/worker/worker.h150
-rw-r--r--libs/ardour/lv2_plugin.cc84
-rw-r--r--libs/ardour/worker.cc115
-rw-r--r--libs/ardour/wscript1
-rw-r--r--libs/pbd/pbd/semaphore.h198
7 files changed, 636 insertions, 9 deletions
diff --git a/libs/ardour/ardour/lv2_plugin.h b/libs/ardour/ardour/lv2_plugin.h
index 49d349f844..0c8ecaa34e 100644
--- a/libs/ardour/ardour/lv2_plugin.h
+++ b/libs/ardour/ardour/lv2_plugin.h
@@ -26,6 +26,7 @@
#include "ardour/plugin.h"
#include "ardour/uri_map.h"
+#include "ardour/worker.h"
#include "pbd/ringbuffer.h"
namespace ARDOUR {
@@ -33,7 +34,7 @@ namespace ARDOUR {
class AudioEngine;
class Session;
-class LV2Plugin : public ARDOUR::Plugin
+class LV2Plugin : public ARDOUR::Plugin, public ARDOUR::Workee
{
public:
LV2Plugin (ARDOUR::AudioEngine& engine,
@@ -129,6 +130,9 @@ class LV2Plugin : public ARDOUR::Plugin
void enable_ui_emmission();
void emit_to_ui(void* controller, UIMessageSink sink);
+ void work(uint32_t size, const void* data);
+ void work_response(uint32_t size, const void* data);
+
static URIMap _uri_map;
static uint32_t _midi_event_type_ev;
@@ -195,6 +199,7 @@ class LV2Plugin : public ARDOUR::Plugin
LV2_Feature _data_access_feature;
LV2_Feature _instance_access_feature;
LV2_Feature _make_path_feature;
+ LV2_Feature _work_schedule_feature;
mutable unsigned _state_version;
diff --git a/libs/ardour/ardour/worker.h b/libs/ardour/ardour/worker.h
new file mode 100644
index 0000000000..eca5aa21ca
--- /dev/null
+++ b/libs/ardour/ardour/worker.h
@@ -0,0 +1,90 @@
+/*
+ Copyright (C) 2012 Paul Davis
+ Author: David Robillard
+
+ 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#ifndef __ardour_worker_h__
+#define __ardour_worker_h__
+
+#include <stdint.h>
+
+#include <glibmm/thread.h>
+
+#include "pbd/ringbuffer.h"
+#include "pbd/semaphore.h"
+
+namespace ARDOUR {
+
+/**
+ An object that needs to schedule non-RT work in the audio thread.
+*/
+class Workee {
+public:
+ virtual ~Workee() {}
+
+ /**
+ Do some work in the worker thread.
+ */
+ virtual void work(uint32_t size, const void* data) = 0;
+
+ /**
+ Handle a response from the worker thread in the audio thread.
+ */
+ virtual void work_response(uint32_t size, const void* data) = 0;
+};
+
+/**
+ A worker thread for non-realtime tasks scheduled in the audio thread.
+*/
+class Worker
+{
+public:
+ Worker(Workee* workee, uint32_t ring_size);
+ ~Worker();
+
+ /**
+ Schedule work (audio thread).
+ @return false on error.
+ */
+ bool schedule(uint32_t size, const void* data);
+
+ /**
+ Respond from work (worker thread).
+ @return false on error.
+ */
+ bool respond(uint32_t size, const void* data);
+
+ /**
+ Emit any pending responses (audio thread).
+ */
+ void emit_responses();
+
+private:
+ void run();
+
+ Workee* _workee;
+ Glib::Thread* _thread;
+ RingBuffer<uint8_t>* _requests;
+ RingBuffer<uint8_t>* _responses;
+ uint8_t* _response;
+ PBD::Semaphore _sem;
+ bool _exit;
+};
+
+} // namespace ARDOUR
+
+#endif /* __ardour_worker_h__ */
diff --git a/libs/ardour/lv2/lv2plug.in/ns/ext/worker/worker.h b/libs/ardour/lv2/lv2plug.in/ns/ext/worker/worker.h
new file mode 100644
index 0000000000..74ac45bc1e
--- /dev/null
+++ b/libs/ardour/lv2/lv2plug.in/ns/ext/worker/worker.h
@@ -0,0 +1,150 @@
+/*
+ Copyright 2012 David Robillard <http://drobilla.net>
+
+ Permission to use, copy, modify, and/or distribute this software for any
+ purpose with or without fee is hereby granted, provided that the above
+ copyright notice and this permission notice appear in all copies.
+
+ THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+*/
+
+/**
+ @file worker.h C header for the LV2 Worker extension
+ <http://lv2plug.in/ns/ext/worker>.
+*/
+
+#ifndef LV2_WORKER_H
+#define LV2_WORKER_H
+
+#include <stdint.h>
+
+#include "lv2/lv2plug.in/ns/lv2core/lv2.h"
+
+#define LV2_WORKER_URI "http://lv2plug.in/ns/ext/worker"
+#define LV2_WORKER_PREFIX LV2_WORKER_URI "#"
+
+#define LV2_WORKER__interface LV2_WORKER_PREFIX "interface"
+#define LV2_WORKER__schedule LV2_WORKER_PREFIX "schedule"
+
+/**
+ A status code for worker functions.
+*/
+typedef enum {
+ LV2_WORKER_SUCCESS = 0, /**< Completed successfully. */
+ LV2_WORKER_ERR_UNKNOWN = 1, /**< Unknown error. */
+ LV2_WORKER_ERR_NO_SPACE = 2 /**< Failed due to lack of space. */
+} LV2_Worker_Status;
+
+typedef void* LV2_Worker_Respond_Handle;
+
+/**
+ A function to respond to run() from the worker method.
+
+ The @p data MUST be safe for the host to copy and later pass to
+ work_response(), and the host MUST guarantee that it will be eventually
+ passed to work_response() if this function returns LV2_WORKER_SUCCESS.
+*/
+typedef LV2_Worker_Status (*LV2_Worker_Respond_Function)(
+ LV2_Worker_Respond_Handle handle,
+ uint32_t size,
+ const void* data);
+
+/**
+ LV2 Plugin Worker Interface.
+
+ This is the interface provided by the plugin to implement a worker method.
+ The plugin's extension_data() method should return an LV2_Worker_Interface
+ when called with LV2_WORKER__interface as its argument.
+*/
+typedef struct _LV2_Worker_Interface {
+ /**
+ The worker method. This is called by the host in a non-realtime context
+ as requested, possibly with an arbitrary message to handle.
+
+ A response can be sent to run() using @p respond. The plugin MUST NOT
+ make any assumptions about which thread calls this method, other than
+ the fact that there are no real-time requirements.
+
+ @param instance The LV2 instance this is a method on.
+ @param respond A function for sending a response to run().
+ @param handle Must be passed to @p respond if it is called.
+ @param size The size of @p data.
+ @param data Data from run(), or NULL.
+ */
+ LV2_Worker_Status (*work)(LV2_Handle instance,
+ LV2_Worker_Respond_Function respond,
+ LV2_Worker_Respond_Handle handle,
+ uint32_t size,
+ const void* data);
+
+ /**
+ Handle a response from the worker. This is called by the host in the
+ run() context when a response from the worker is ready.
+
+ @param instance The LV2 instance this is a method on.
+ @param size The size of @p body.
+ @param body Message body, or NULL.
+ */
+ LV2_Worker_Status (*work_response)(LV2_Handle instance,
+ uint32_t size,
+ const void* body);
+
+ /**
+ Called when all responses for this cycle have been delivered.
+
+ Since work_response() may be called after run() finished, this provides
+ a hook for code that must run after the cycle is completed.
+
+ This field may be NULL if the plugin has no use for it. Otherwise, the
+ host MUST call it after every run(), regardless of whether or not any
+ responses were sent that cycle.
+ */
+ LV2_Worker_Status (*end_run)(LV2_Handle instance);
+} LV2_Worker_Interface;
+
+typedef void* LV2_Worker_Schedule_Handle;
+
+typedef struct _LV2_Worker_Schedule {
+ /**
+ Opaque host data.
+ */
+ LV2_Worker_Schedule_Handle handle;
+
+ /**
+ Request from run() that the host call the worker.
+
+ This function is in the audio threading class. It should be called from
+ run() to request that the host call the work() method in a non-realtime
+ context with the given arguments.
+
+ This function is always safe to call from run(), but it is not
+ guaranteed that the worker is actually called from a different thread.
+ In particular, when free-wheeling (e.g. for offline rendering), the
+ worker may be executed immediately. This allows single-threaded
+ processing with sample accuracy and avoids timing problems when run() is
+ executing much faster or slower than real-time.
+
+ Plugins SHOULD be written in such a way that if the worker runs
+ immediately, and responses from the worker are delivered immediately,
+ the effect of the work takes place immediately with sample accuracy.
+
+ The @p data MUST be safe for the host to copy and later pass to work(),
+ and the host MUST guarantee that it will be eventually passed to work()
+ if this function returns LV2_WORKER_SUCCESS.
+
+ @param handle The handle field of this struct.
+ @param size The size of @p data.
+ @param data Message to pass to work(), or NULL.
+ */
+ LV2_Worker_Status (*schedule_work)(LV2_Worker_Schedule_Handle handle,
+ uint32_t size,
+ const void* data);
+} LV2_Worker_Schedule;
+
+#endif /* LV2_WORKER_H */
diff --git a/libs/ardour/lv2_plugin.cc b/libs/ardour/lv2_plugin.cc
index 91dd1578fb..556b8d4357 100644
--- a/libs/ardour/lv2_plugin.cc
+++ b/libs/ardour/lv2_plugin.cc
@@ -43,6 +43,7 @@
#include "ardour/debug.h"
#include "ardour/lv2_plugin.h"
#include "ardour/session.h"
+#include "ardour/worker.h"
#include "i18n.h"
#include <locale.h>
@@ -51,6 +52,7 @@
#include "lv2/lv2plug.in/ns/ext/atom/atom.h"
#include "lv2/lv2plug.in/ns/ext/state/state.h"
+#include "lv2/lv2plug.in/ns/ext/worker/worker.h"
#include "lv2_evbuf.h"
@@ -113,20 +115,45 @@ public:
static LV2World _world;
+/** Called by the plugin to schedule non-RT work. */
+static LV2_Worker_Status
+work_schedule(LV2_Worker_Schedule_Handle handle,
+ uint32_t size,
+ const void* data)
+{
+ Worker* worker = (Worker*)handle;
+ return worker->schedule(size, data) ?
+ LV2_WORKER_SUCCESS : LV2_WORKER_ERR_UNKNOWN;
+}
+
+/** Called by the plugin to respond to non-RT work. */
+static LV2_Worker_Status
+work_respond(LV2_Worker_Respond_Handle handle,
+ uint32_t size,
+ const void* data)
+{
+ Worker* worker = (Worker*)handle;
+ return worker->respond(size, data) ?
+ LV2_WORKER_SUCCESS : LV2_WORKER_ERR_UNKNOWN;
+}
+
struct LV2Plugin::Impl {
Impl() : plugin(0), ui(0), ui_type(0), name(0), author(0), instance(0)
+ , worker(0), work_iface(0)
#ifdef HAVE_NEW_LILV
, state(0)
#endif
{}
- LilvPlugin* plugin;
- const LilvUI* ui;
- const LilvNode* ui_type;
- LilvNode* name;
- LilvNode* author;
- LilvInstance* instance;
+ LilvPlugin* plugin;
+ const LilvUI* ui;
+ const LilvNode* ui_type;
+ LilvNode* name;
+ LilvNode* author;
+ LilvInstance* instance;
+ Worker* worker;
+ LV2_Worker_Interface* work_iface;
#ifdef HAVE_NEW_LILV
- LilvState* state;
+ LilvState* state;
#endif
};
@@ -177,6 +204,7 @@ LV2Plugin::init(void* c_plugin, framecnt_t rate)
_instance_access_feature.URI = "http://lv2plug.in/ns/ext/instance-access";
_data_access_feature.URI = "http://lv2plug.in/ns/ext/data-access";
_make_path_feature.URI = LV2_STATE__makePath;
+ _work_schedule_feature.URI = LV2_WORKER__schedule;
LilvPlugin* plugin = _impl->plugin;
@@ -192,7 +220,7 @@ LV2Plugin::init(void* c_plugin, framecnt_t rate)
lilv_node_free(state_iface_uri);
#endif
- _features = (LV2_Feature**)malloc(sizeof(LV2_Feature*) * 7);
+ _features = (LV2_Feature**)malloc(sizeof(LV2_Feature*) * 8);
_features[0] = &_instance_access_feature;
_features[1] = &_data_access_feature;
_features[2] = &_make_path_feature;
@@ -200,6 +228,7 @@ LV2Plugin::init(void* c_plugin, framecnt_t rate)
_features[4] = _uri_map.urid_map_feature();
_features[5] = _uri_map.urid_unmap_feature();
_features[6] = NULL;
+ _features[7] = NULL;
LV2_State_Make_Path* make_path = (LV2_State_Make_Path*)malloc(
sizeof(LV2_State_Make_Path));
@@ -207,6 +236,18 @@ LV2Plugin::init(void* c_plugin, framecnt_t rate)
make_path->path = &lv2_state_make_path;
_make_path_feature.data = make_path;
+ LilvNode* worker_schedule = lilv_new_uri(_world.world, LV2_WORKER__schedule);
+ if (lilv_plugin_has_feature(plugin, worker_schedule)) {
+ LV2_Worker_Schedule* schedule = (LV2_Worker_Schedule*)malloc(
+ sizeof(LV2_Worker_Schedule));
+ _impl->worker = new Worker(this, 4096);
+ schedule->handle = _impl->worker;
+ schedule->schedule_work = work_schedule;
+ _work_schedule_feature.data = schedule;
+ _features[6] = &_work_schedule_feature;
+ }
+ lilv_node_free(worker_schedule);
+
_impl->instance = lilv_plugin_instantiate(plugin, rate, _features);
_impl->name = lilv_plugin_get_name(plugin);
_impl->author = lilv_plugin_get_author_name(plugin);
@@ -220,6 +261,8 @@ LV2Plugin::init(void* c_plugin, framecnt_t rate)
_data_access_extension_data.extension_data = _impl->instance->lv2_descriptor->extension_data;
_data_access_feature.data = &_data_access_extension_data;
+ _impl->work_iface = (LV2_Worker_Interface*)extension_data(LV2_WORKER__interface);
+
if (lilv_plugin_has_feature(plugin, _world.lv2_inPlaceBroken)) {
error << string_compose(
_("LV2: \"%1\" cannot be used, since it cannot do inplace processing"),
@@ -369,6 +412,10 @@ LV2Plugin::~LV2Plugin ()
lilv_node_free(_impl->name);
lilv_node_free(_impl->author);
+ free(_features);
+ free(_make_path_feature.data);
+ free(_work_schedule_feature.data);
+
delete _to_ui;
delete _from_ui;
@@ -850,6 +897,7 @@ LV2Plugin::write_to_ui(uint32_t index,
uint32_t size,
uint8_t* body)
{
+ std::cerr << "WRITE TO UI" << std::endl;
write_to(_to_ui, index, protocol, size, body);
}
@@ -888,6 +936,19 @@ LV2Plugin::emit_to_ui(void* controller, UIMessageSink sink)
}
void
+LV2Plugin::work(uint32_t size, const void* data)
+{
+ _impl->work_iface->work(
+ _impl->instance->lv2_handle, work_respond, _impl->worker, size, data);
+}
+
+void
+LV2Plugin::work_response(uint32_t size, const void* data)
+{
+ _impl->work_iface->work_response(_impl->instance->lv2_handle, size, data);
+}
+
+void
LV2Plugin::set_insert_info(const PluginInsert* insert)
{
_insert_id = insert->id();
@@ -1297,6 +1358,13 @@ LV2Plugin::run(pframes_t nframes)
}
lilv_instance_run(_impl->instance, nframes);
+
+ if (_impl->work_iface) {
+ _impl->worker->emit_responses();
+ if (_impl->work_iface->end_run) {
+ _impl->work_iface->end_run(_impl->instance->lv2_handle);
+ }
+ }
}
void
diff --git a/libs/ardour/worker.cc b/libs/ardour/worker.cc
new file mode 100644
index 0000000000..c108f653c4
--- /dev/null
+++ b/libs/ardour/worker.cc
@@ -0,0 +1,115 @@
+/*
+ Copyright (C) 2012 Paul Davis
+ Author: David Robillard
+
+ 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#include <stdlib.h>
+
+#include "ardour/worker.h"
+#include "pbd/error.h"
+
+namespace ARDOUR {
+
+Worker::Worker(Workee* workee, uint32_t ring_size)
+ : _workee(workee)
+ , _thread(Glib::Thread::create(sigc::mem_fun(*this, &Worker::run), true))
+ , _requests(new RingBuffer<uint8_t>(ring_size))
+ , _responses(new RingBuffer<uint8_t>(ring_size))
+ , _response((uint8_t*)malloc(ring_size))
+ , _sem(0)
+ , _exit(false)
+{}
+
+Worker::~Worker()
+{
+ _exit = true;
+ _sem.post();
+ _thread->join();
+}
+
+bool
+Worker::schedule(uint32_t size, const void* data)
+{
+ if (_requests->write((const uint8_t*)&size, sizeof(size)) != sizeof(size)) {
+ return false;
+ }
+ if (_requests->write((const uint8_t*)data, size) != size) {
+ return false; // FIXME: corruption
+ }
+ _sem.post();
+ return true;
+}
+
+bool
+Worker::respond(uint32_t size, const void* data)
+{
+ if (_responses->write((const uint8_t*)&size, sizeof(size)) != sizeof(size)) {
+ return false;
+ }
+ if (_responses->write((const uint8_t*)data, size) != size) {
+ return false; // FIXME: corruption
+ }
+ return true;
+}
+
+void
+Worker::emit_responses()
+{
+ uint32_t read_space = _responses->read_space();
+ uint32_t size = 0;
+ while (read_space > sizeof(size)) {
+ _responses->read((uint8_t*)&size, sizeof(size));
+ _responses->read(_response, size);
+ _workee->work_response(size, _response);
+ read_space -= sizeof(size) + size;
+ }
+}
+
+void
+Worker::run()
+{
+ void* buf = NULL;
+ size_t buf_size = 0;
+ while (true) {
+ _sem.wait();
+ if (_exit) {
+ return;
+ }
+
+ uint32_t size = 0;
+ if (_requests->read((uint8_t*)&size, sizeof(size)) < sizeof(size)) {
+ PBD::error << "Worker: Error reading size from request ring"
+ << endmsg;
+ continue;
+ }
+
+ if (size > buf_size) {
+ buf = realloc(buf, size);
+ buf_size = size;
+ }
+
+ if (_requests->read((uint8_t*)buf, size) < size) {
+ PBD::error << "Worker: Error reading body from request ring"
+ << endmsg;
+ continue; // TODO: This is probably fatal
+ }
+
+ _workee->work(size, buf);
+ }
+}
+
+} // namespace ARDOUR
diff --git a/libs/ardour/wscript b/libs/ardour/wscript
index ab04fde80b..b12ebf593d 100644
--- a/libs/ardour/wscript
+++ b/libs/ardour/wscript
@@ -218,6 +218,7 @@ libardour_sources = [
'user_bundle.cc',
'utils.cc',
'version.cc',
+ 'worker.cc',
]
def flac_supported():
diff --git a/libs/pbd/pbd/semaphore.h b/libs/pbd/pbd/semaphore.h
new file mode 100644
index 0000000000..f1b07ea4f5
--- /dev/null
+++ b/libs/pbd/pbd/semaphore.h
@@ -0,0 +1,198 @@
+/*
+ Copyright (C) 2012 Paul Davis
+ Author: David Robillard
+
+ 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., 675 Mass Ave, Cambridge, MA 02139, USA.
+*/
+
+#ifndef __pbd_semaphore_h__
+#define __pbd_semaphore_h__
+
+#ifdef __APPLE__
+# include <mach/mach.h>
+#elif defined(_WIN32)
+# include <windows.h>
+#else
+# include <semaphore.h>
+# include <errno.h>
+#endif
+
+#include "pbd/failed_constructor.h"
+
+namespace PBD {
+
+/**
+ 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:
+ /**
+ Create a new semaphore.
+
+ Chances are you want 1 wait() per 1 post(), an initial value of 0.
+ */
+ inline Semaphore(unsigned initial);
+
+ inline ~Semaphore();
+
+ /** Post/Increment/Signal */
+ inline void post();
+
+ /** Wait/Decrement. Returns false on error. */
+ inline bool wait();
+
+ /** Attempt Wait/Decrement. Returns true iff a decrement occurred. */
+ inline bool try_wait();
+
+private:
+#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
+};
+
+#ifdef __APPLE__
+
+inline
+Semaphore::Semaphore(unsigned initial)
+{
+ if (semaphore_create(mach_task_self(), &sem->sem, SYNC_POLICY_FIFO, 0)) {
+ throw failed_constructor();
+ }
+}
+
+inline
+Semaphore::~Semaphore()
+{
+ 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
+Semaphore::Semaphore(unsigned initial)
+{
+ if (!(_sem = CreateSemaphore(NULL, initial, LONG_MAX, NULL))) {
+ throw failed_constructor();
+ }
+}
+
+inline
+Semaphore::~Semaphore()
+{
+ 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) */
+
+Semaphore::Semaphore(unsigned initial)
+{
+ if (sem_init(&_sem, 0, initial)) {
+ throw failed_constructor();
+ }
+}
+
+inline
+Semaphore::~Semaphore()
+{
+ 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 PBD
+
+#endif /* __pbd_semaphore_h__ */