diff options
author | David Robillard <d@drobilla.net> | 2012-03-23 01:26:47 +0000 |
---|---|---|
committer | David Robillard <d@drobilla.net> | 2012-03-23 01:26:47 +0000 |
commit | 5ef77d9bd9b102c76dbef24fbcd006c4534e9fc1 (patch) | |
tree | 37c43c211e7ba050535f2c6e4130685fdbff2bc2 | |
parent | f161f400ce62326142f7ea5f613032f230d0d795 (diff) | |
download | lv2-5ef77d9bd9b102c76dbef24fbcd006c4534e9fc1.tar.xz |
Add worker extension to remove thread stuff from plugins.
-rw-r--r-- | doc/reference.doxygen.in | 1 | ||||
-rw-r--r-- | lv2/lv2plug.in/ns/ext/atom/forge.h | 1 | ||||
-rw-r--r-- | lv2/lv2plug.in/ns/ext/urid/urid.ttl | 4 | ||||
l--------- | lv2/lv2plug.in/ns/ext/worker/ext.pc.in | 1 | ||||
-rw-r--r-- | lv2/lv2plug.in/ns/ext/worker/manifest.ttl | 8 | ||||
l--------- | lv2/lv2plug.in/ns/ext/worker/waf | 1 | ||||
-rw-r--r-- | lv2/lv2plug.in/ns/ext/worker/worker.h | 138 | ||||
-rw-r--r-- | lv2/lv2plug.in/ns/ext/worker/worker.ttl | 58 | ||||
l--------- | lv2/lv2plug.in/ns/ext/worker/wscript | 1 | ||||
-rw-r--r-- | plugins/eg-sampler.lv2/sampler.c | 259 | ||||
-rw-r--r-- | plugins/eg-sampler.lv2/sampler.ttl | 12 | ||||
-rw-r--r-- | plugins/eg-sampler.lv2/wscript | 8 | ||||
-rw-r--r-- | plugins/eg-sampler.lv2/zix/common.h | 83 | ||||
-rw-r--r-- | plugins/eg-sampler.lv2/zix/ring.c | 231 | ||||
-rw-r--r-- | plugins/eg-sampler.lv2/zix/ring.h | 136 | ||||
-rw-r--r-- | plugins/eg-sampler.lv2/zix/sem.h | 236 | ||||
-rw-r--r-- | plugins/eg-sampler.lv2/zix/thread.h | 133 |
17 files changed, 325 insertions, 986 deletions
diff --git a/doc/reference.doxygen.in b/doc/reference.doxygen.in index f97a93f..9ed3968 100644 --- a/doc/reference.doxygen.in +++ b/doc/reference.doxygen.in @@ -587,6 +587,7 @@ INPUT = \ @LV2_SRCDIR@/lv2/lv2plug.in/ns/ext/ui-resize/ui-resize.h \ @LV2_SRCDIR@/lv2/lv2plug.in/ns/ext/uri-map/uri-map.h \ @LV2_SRCDIR@/lv2/lv2plug.in/ns/ext/urid/urid.h \ + @LV2_SRCDIR@/lv2/lv2plug.in/ns/ext/worker/worker.h \ @LV2_SRCDIR@/lv2/lv2plug.in/ns/extensions/ui/ui.h \ @LV2_SRCDIR@/lv2/lv2plug.in/ns/lv2core/lv2.h diff --git a/lv2/lv2plug.in/ns/ext/atom/forge.h b/lv2/lv2plug.in/ns/ext/atom/forge.h index 322e14e..ea5977e 100644 --- a/lv2/lv2plug.in/ns/ext/atom/forge.h +++ b/lv2/lv2plug.in/ns/ext/atom/forge.h @@ -120,7 +120,6 @@ static inline void lv2_atom_forge_init(LV2_Atom_Forge* forge, LV2_URID_Map* map) { lv2_atom_forge_set_buffer(forge, NULL, 0); - forge->stack = NULL; forge->Blank = map->map(map->handle, LV2_ATOM__Blank); forge->Bool = map->map(map->handle, LV2_ATOM__Bool); forge->Double = map->map(map->handle, LV2_ATOM__Double); diff --git a/lv2/lv2plug.in/ns/ext/urid/urid.ttl b/lv2/lv2plug.in/ns/ext/urid/urid.ttl index 121ffaf..d080107 100644 --- a/lv2/lv2plug.in/ns/ext/urid/urid.ttl +++ b/lv2/lv2plug.in/ns/ext/urid/urid.ttl @@ -68,7 +68,7 @@ urid:map lv2:documentation """ <p>A feature which is used to map URIs to integers. To support this feature, the host must pass an LV2_Feature to LV2_Descriptor::instantiate() with URI -LV2_URID_MAP_URI and data pointed to an instance of LV2_URID_Map.</p> +LV2_URID__map and data pointed to an instance of LV2_URID_Map.</p> """ . urid:unmap @@ -76,6 +76,6 @@ urid:unmap lv2:documentation """ <p>A feature which is used to unmap URIs previously mapped to integers by urid:map. To support this feature, the host must pass an LV2_Feature to -LV2_Descriptor::instantiate() with URI LV2_URID_UNMAP_URI and data pointed to +LV2_Descriptor::instantiate() with URI LV2_URID__unmapl and data pointed to an instance of LV2_URID_Unmap.</p> """ . diff --git a/lv2/lv2plug.in/ns/ext/worker/ext.pc.in b/lv2/lv2plug.in/ns/ext/worker/ext.pc.in new file mode 120000 index 0000000..03dd044 --- /dev/null +++ b/lv2/lv2plug.in/ns/ext/worker/ext.pc.in @@ -0,0 +1 @@ +../../../../../ext.pc.in
\ No newline at end of file diff --git a/lv2/lv2plug.in/ns/ext/worker/manifest.ttl b/lv2/lv2plug.in/ns/ext/worker/manifest.ttl new file mode 100644 index 0000000..af46f01 --- /dev/null +++ b/lv2/lv2plug.in/ns/ext/worker/manifest.ttl @@ -0,0 +1,8 @@ +@prefix lv2: <http://lv2plug.in/ns/lv2core#> . +@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> . + +<http://lv2plug.in/ns/ext/worker> + a lv2:Specification ; + lv2:minorVersion 0 ; + lv2:microVersion 1 ; + rdfs:seeAlso <worker.ttl> . diff --git a/lv2/lv2plug.in/ns/ext/worker/waf b/lv2/lv2plug.in/ns/ext/worker/waf new file mode 120000 index 0000000..5235032 --- /dev/null +++ b/lv2/lv2plug.in/ns/ext/worker/waf @@ -0,0 +1 @@ +../../../../../waf
\ No newline at end of file diff --git a/lv2/lv2plug.in/ns/ext/worker/worker.h b/lv2/lv2plug.in/ns/ext/worker/worker.h new file mode 100644 index 0000000..612a24c --- /dev/null +++ b/lv2/lv2plug.in/ns/ext/worker/worker.h @@ -0,0 +1,138 @@ +/* + 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); +} 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 body. + @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/lv2/lv2plug.in/ns/ext/worker/worker.ttl b/lv2/lv2plug.in/ns/ext/worker/worker.ttl new file mode 100644 index 0000000..ea49056 --- /dev/null +++ b/lv2/lv2plug.in/ns/ext/worker/worker.ttl @@ -0,0 +1,58 @@ +# LV2 Worker Extension +# Copyright 2012 David Robillard <d@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. + +@prefix work: <http://lv2plug.in/ns/ext/worker#> . +@prefix lv2: <http://lv2plug.in/ns/lv2core#> . +@prefix rdf: <http://www.w3.org/1999/02/22-rdf-syntax-ns#> . +@prefix rdfs: <http://www.w3.org/2000/01/rdf-schema#> . +@prefix xsd: <http://www.w3.org/2001/XMLSchema#> . +@prefix doap: <http://usefulinc.com/ns/doap#> . +@prefix foaf: <http://xmlns.com/foaf/0.1/> . + +<http://lv2plug.in/ns/ext/worker> + a lv2:Specification , + lv2:Feature ; + doap:name "LV2 Worker" ; + doap:shortdesc "Support for a non-realtime plugin worker method." ; + lv2:documentation """ +""" . + +work:Interface + a rdfs:Class ; + rdfs:subClassOf lv2:ExtensionData ; + lv2:documentation """ +<p>A structure (LV2_Worker_Interface) which contains the worker method to be +called by the host. In order to support this extension, the plugin must return +a valid LV2_Worker_Interface from LV2_Descriptor::extension_data() when it is +called with URI LV2_WORKER__Interface.</p> + +<p>The plugin data file should describe this like so:</p> +<pre class="turtle-code"> +@prefix work: <http://lv2plug.in/ns/ext/worker#> . + +<plugin> + a lv2:Plugin ; + lv2:extensionData work:Interface . +</pre> +""" . + +work:schedule + a lv2:Feature ; + lv2:documentation """ +<p>A feature which provides functions for use by the plugin to implement a +worker method. To support this feature, the host must pass an LV2_Feature to +LV2_Descriptor::instantiate() with URI LV2_WORKER__schedule and data pointed to +an instance of LV2_Worker_Schedule.</p> +""" . diff --git a/lv2/lv2plug.in/ns/ext/worker/wscript b/lv2/lv2plug.in/ns/ext/worker/wscript new file mode 120000 index 0000000..7e2c01b --- /dev/null +++ b/lv2/lv2plug.in/ns/ext/worker/wscript @@ -0,0 +1 @@ +../../../../../ext.wscript
\ No newline at end of file diff --git a/plugins/eg-sampler.lv2/sampler.c b/plugins/eg-sampler.lv2/sampler.c index ec59ef3..229762e 100644 --- a/plugins/eg-sampler.lv2/sampler.c +++ b/plugins/eg-sampler.lv2/sampler.c @@ -44,16 +44,11 @@ #include "lv2/lv2plug.in/ns/ext/patch/patch.h" #include "lv2/lv2plug.in/ns/ext/state/state.h" #include "lv2/lv2plug.in/ns/ext/urid/urid.h" +#include "lv2/lv2plug.in/ns/ext/worker/worker.h" #include "lv2/lv2plug.in/ns/lv2core/lv2.h" -#include "zix/sem.h" -#include "zix/thread.h" -#include "zix/ring.h" - #include "./uris.h" -#define RING_SIZE 4096 - enum { SAMPLER_CONTROL = 0, SAMPLER_RESPONSE = 1, @@ -71,39 +66,38 @@ typedef struct { typedef struct { /* Features */ - LV2_URID_Map* map; + LV2_URID_Map* map; + LV2_Worker_Schedule* schedule; /* Forge for creating atoms */ LV2_Atom_Forge forge; - /* Worker thread, communication, and sync */ - ZixThread worker_thread; - ZixSem signal; - ZixRing* to_worker; - ZixRing* from_worker; - bool exit; - /* Sample */ Sample* sample; /* Ports */ - float* output_port; - LV2_Atom_Sequence* control_port; - LV2_Atom_Sequence* notify_port; + float* output_port; + LV2_Atom_Sequence* control_port; + LV2_Atom_Sequence* notify_port; + LV2_Atom_Forge_Frame notify_frame; /* URIs */ SamplerURIs uris; + /* Current position in run() */ + uint32_t frame_offset; + /* Playback state */ sf_count_t frame; bool play; } Sampler; -/** An atom-like message used internally to apply/free samples. - * - * This is only used internally via ringbuffers, since it is not POD and - * therefore not strictly an Atom. - */ +/** + An atom-like message used internally to apply/free samples. + + This is only used internally to communicate with the worker, it is not an + Atom because it is not POD. +*/ typedef struct { LV2_Atom atom; Sample* sample; @@ -120,7 +114,7 @@ load_sample(Sampler* plugin, const char* path) SNDFILE* const sndfile = sf_open(path, SFM_READ, info); if (!sndfile || !info->frames || (info->channels != 1)) { - fprintf(stderr, "failed to open sample '%s'.\n", path); + fprintf(stderr, "Failed to open sample '%s'.\n", path); free(sample); return NULL; } @@ -128,7 +122,7 @@ load_sample(Sampler* plugin, const char* path) /* Read data */ float* const data = malloc(sizeof(float) * info->frames); if (!data) { - fprintf(stderr, "failed to allocate memory for sample.\n"); + fprintf(stderr, "Failed to allocate memory for sample.\n"); return NULL; } sf_seek(sndfile, 0ul, SEEK_SET); @@ -144,32 +138,7 @@ load_sample(Sampler* plugin, const char* path) return sample; } -static bool -handle_set_message(Sampler* plugin, - const LV2_Atom_Object* obj) -{ - /* Get file path from message */ - const LV2_Atom* file_path = read_set_file(&plugin->uris, obj); - if (!file_path) { - return false; - } - - /* Load sample. */ - Sample* sample = load_sample(plugin, LV2_ATOM_BODY(file_path)); - if (sample) { - /* Loaded sample, send it to run() to be applied. */ - const SampleMessage msg = { - { sizeof(sample), plugin->uris.eg_applySample }, - sample - }; - zix_ring_write( - plugin->from_worker, &msg, lv2_atom_pad_size(sizeof(msg))); - } - - return true; -} - -void +static void free_sample(Sample* sample) { if (sample) { @@ -180,33 +149,65 @@ free_sample(Sample* sample) } } -void* -worker_thread_main(void* arg) +/** Handle work (load or free a sample) in a non-realtime thread. */ +static LV2_Worker_Status +work(LV2_Handle instance, + LV2_Worker_Respond_Function respond, + LV2_Worker_Respond_Handle handle, + uint32_t size, + const void* data) { - Sampler* plugin = (Sampler*)arg; - - while (!zix_sem_wait(&plugin->signal) && !plugin->exit) { - /* Peek message header to see how much we need to read. */ - LV2_Atom head; - zix_ring_peek(plugin->to_worker, &head, sizeof(head)); - - /* Read message. */ - const uint32_t size = lv2_atom_pad_size(sizeof(LV2_Atom) + head.size); - uint8_t buf[size]; - LV2_Atom* obj = (LV2_Atom*)buf; - zix_ring_read(plugin->to_worker, buf, size); - - if (obj->type == plugin->uris.eg_freeSample) { - /* Free old sample */ - SampleMessage* msg = (SampleMessage*)obj; - free_sample(msg->sample); - } else { - /* Handle set message (load sample). */ - handle_set_message(plugin, (LV2_Atom_Object*)obj); + Sampler* self = (Sampler*)instance; + LV2_Atom* atom = (LV2_Atom*)data; + if (atom->type == self->uris.eg_freeSample) { + /* Free old sample */ + SampleMessage* msg = (SampleMessage*)data; + free_sample(msg->sample); + } else { + /* Handle set message (load sample). */ + LV2_Atom_Object* obj = (LV2_Atom_Object*)data; + + /* Get file path from message */ + const LV2_Atom* file_path = read_set_file(&self->uris, obj); + if (!file_path) { + return LV2_WORKER_ERR_UNKNOWN; + } + + /* Load sample. */ + Sample* sample = load_sample(self, LV2_ATOM_BODY(file_path)); + if (sample) { + /* Loaded sample, send it to run() to be applied. */ + respond(handle, sizeof(sample), &sample); } } - return 0; + return LV2_WORKER_SUCCESS; +} + +/** Handle a response from work() in the audio thread. */ +static LV2_Worker_Status +work_response(LV2_Handle instance, + uint32_t size, + const void* data) +{ + Sampler* self = (Sampler*)instance; + + SampleMessage msg = { { sizeof(Sample*), self->uris.eg_freeSample }, + self->sample }; + + /* Send a message to the worker to free the current sample */ + self->schedule->schedule_work(self->schedule->handle, sizeof(msg), &msg); + + /* Install the new sample */ + self->sample = *(Sample**)data; + + /* Send a notification that we're using a new sample. */ + lv2_atom_forge_frame_time(&self->forge, self->frame_offset); + write_set_file(&self->forge, &self->uris, + self->sample->path, + self->sample->path_len); + + return LV2_WORKER_SUCCESS; } static void @@ -242,52 +243,34 @@ instantiate(const LV2_Descriptor* descriptor, return NULL; } + memset(plugin, 0, sizeof(Sampler)); plugin->sample = (Sample*)malloc(sizeof(Sample)); if (!plugin->sample) { return NULL; } memset(plugin->sample, 0, sizeof(Sample)); - memset(&plugin->uris, 0, sizeof(plugin->uris)); - /* Scan host features for URID map */ - LV2_URID_Map* map = NULL; + /* Scan and store host features */ for (int i = 0; features[i]; ++i) { - if (!strcmp(features[i]->URI, LV2_URID_URI "#map")) { - map = (LV2_URID_Map*)features[i]->data; + if (!strcmp(features[i]->URI, LV2_URID__map)) { + plugin->map = (LV2_URID_Map*)features[i]->data; + } else if (!strcmp(features[i]->URI, LV2_WORKER__schedule)) { + plugin->schedule = (LV2_Worker_Schedule*)features[i]->data; } } - if (!map) { + if (!plugin->map) { fprintf(stderr, "Host does not support urid:map.\n"); goto fail; + } else if (!plugin->schedule) { + fprintf(stderr, "Host does not support work:schedule.\n"); + goto fail; } /* Map URIS and initialise forge */ - plugin->map = map; map_sampler_uris(plugin->map, &plugin->uris); lv2_atom_forge_init(&plugin->forge, plugin->map); - /* Create signal for waking up worker thread */ - if (zix_sem_init(&plugin->signal, 0)) { - fprintf(stderr, "Could not initialize semaphore.\n"); - goto fail; - } - - /* Create worker thread */ - plugin->exit = false; - if (zix_thread_create( - &plugin->worker_thread, 1024, worker_thread_main, plugin)) { - fprintf(stderr, "Could not initialize worker thread.\n"); - goto fail; - } - - /* Create ringbuffers for communicating with worker thread */ - plugin->to_worker = zix_ring_new(RING_SIZE); - plugin->from_worker = zix_ring_new(RING_SIZE); - - zix_ring_mlock(plugin->to_worker); - zix_ring_mlock(plugin->from_worker); - /* Load the default sample file */ const size_t path_len = strlen(path); const size_t file_len = strlen(default_sample_file); @@ -308,12 +291,6 @@ cleanup(LV2_Handle instance) { Sampler* plugin = (Sampler*)instance; - plugin->exit = true; - zix_sem_post(&plugin->signal); - zix_thread_join(plugin->worker_thread, 0); - zix_sem_destroy(&plugin->signal); - zix_ring_free(plugin->to_worker); - zix_ring_free(plugin->from_worker); free_sample(plugin->sample); free(plugin); } @@ -328,9 +305,19 @@ run(LV2_Handle instance, sf_count_t pos = 0; float* output = plugin->output_port; + /* Set up forge to write directly to notify output port. */ + const uint32_t notify_capacity = plugin->notify_port->atom.size; + lv2_atom_forge_set_buffer(&plugin->forge, + (uint8_t*)plugin->notify_port, + notify_capacity); + + /* Start a sequence in the notify output port. */ + lv2_atom_forge_sequence_head(&plugin->forge, &plugin->notify_frame, 0); + /* Read incoming events */ LV2_SEQUENCE_FOREACH(plugin->control_port, i) { LV2_Atom_Event* const ev = lv2_sequence_iter_get(i); + plugin->frame_offset = ev->time.frames; if (ev->body.type == uris->midi_Event) { uint8_t* const data = (uint8_t* const)(ev + 1); if ((data[0] & 0xF0) == 0x90) { @@ -341,13 +328,11 @@ run(LV2_Handle instance, } else if (is_object_type(uris, ev->body.type)) { const LV2_Atom_Object* obj = (LV2_Atom_Object*)&ev->body; if (obj->body.otype == uris->patch_Set) { - /* Received a set message, send it to the worker thread. */ + /* Received a set message, send it to the worker. */ fprintf(stderr, "Queueing set message\n"); - zix_ring_write(plugin->to_worker, - obj, - lv2_atom_pad_size( - lv2_atom_total_size(&obj->atom))); - zix_sem_post(&plugin->signal); + plugin->schedule->schedule_work(plugin->schedule->handle, + lv2_atom_total_size(&ev->body), + &ev->body); } else { fprintf(stderr, "Unknown object type %d\n", obj->body.otype); } @@ -380,47 +365,6 @@ run(LV2_Handle instance, for (; pos < sample_count; ++pos) { output[pos] = 0.0f; } - - /* Set up forge to write directly to notify output port buffer */ - const uint32_t notify_capacity = plugin->notify_port->atom.size; - - lv2_atom_forge_set_buffer(&plugin->forge, - (uint8_t*)plugin->notify_port, - notify_capacity); - - LV2_Atom_Forge_Frame seq_frame; - lv2_atom_forge_sequence_head(&plugin->forge, &seq_frame, 0); - - /* Read messages from worker thread */ - SampleMessage m; - const uint32_t msize = lv2_atom_pad_size(sizeof(m)); - while (zix_ring_read(plugin->from_worker, &m, msize) == msize) { - if (m.atom.type == uris->eg_applySample) { - /* Send a message to the worker to free the current sample */ - SampleMessage free_msg = { - { sizeof(plugin->sample), uris->eg_freeSample }, - plugin->sample - }; - zix_ring_write(plugin->to_worker, - &free_msg, - lv2_atom_pad_size(sizeof(free_msg))); - zix_sem_post(&plugin->signal); - - /* Install the new sample */ - plugin->sample = m.sample; - - /* Send a notification that we're using a new sample. */ - lv2_atom_forge_frame_time(&plugin->forge, 0); - write_set_file(&plugin->forge, uris, - plugin->sample->path, - plugin->sample->path_len); - - } else { - fprintf(stderr, "Unknown message from worker\n"); - } - } - - lv2_atom_forge_pop(&plugin->forge, &seq_frame); } static void @@ -477,12 +421,15 @@ restore(LV2_Handle instance, } } -const void* +static const void* extension_data(const char* uri) { - static const LV2_State_Interface state = { save, restore }; + static const LV2_State_Interface state = { save, restore }; + static const LV2_Worker_Interface worker = { work, work_response }; if (!strcmp(uri, LV2_STATE__Interface)) { return &state; + } else if (!strcmp(uri, LV2_WORKER__Interface)) { + return &worker; } return NULL; } diff --git a/plugins/eg-sampler.lv2/sampler.ttl b/plugins/eg-sampler.lv2/sampler.ttl index fa93ba4..a70be7b 100644 --- a/plugins/eg-sampler.lv2/sampler.ttl +++ b/plugins/eg-sampler.lv2/sampler.ttl @@ -21,12 +21,14 @@ @prefix lv2: <http://lv2plug.in/ns/lv2core#> . @prefix ui: <http://lv2plug.in/ns/extensions/ui#> . @prefix urid: <http://lv2plug.in/ns/ext/urid#> . +@prefix work: <http://lv2plug.in/ns/ext/worker#> . <http://lv2plug.in/plugins/eg-sampler> a lv2:Plugin ; doap:name "Example Sampler" ; doap:license <http://opensource.org/licenses/isc> ; - lv2:requiredFeature urid:map ; + lv2:requiredFeature urid:map , + work:schedule ; lv2:optionalFeature lv2:hardRTCapable ; lv2:extensionData <http://lv2plug.in/ns/ext/state#Interface> ; ui:ui <http://lv2plug.in/plugins/eg-sampler#ui> ; @@ -38,7 +40,7 @@ <http://lv2plug.in/ns/ext/patch#Message> ; lv2:index 0 ; lv2:symbol "control" ; - lv2:name "Control" + lv2:name "Control" ; ] , [ a lv2:OutputPort , atom:MessagePort ; @@ -46,13 +48,13 @@ atom:supports <http://lv2plug.in/ns/ext/patch#Message> ; lv2:index 1 ; lv2:symbol "notify" ; - lv2:name "Notify" + lv2:name "Notify" ; ] , [ a lv2:AudioPort , lv2:OutputPort ; lv2:index 2 ; lv2:symbol "out" ; - lv2:name "Out" + lv2:name "Out" ; ] . <http://lv2plug.in/plugins/eg-sampler#ui> @@ -61,5 +63,5 @@ ui:portNotification [ ui:plugin <http://lv2plug.in/plugins/eg-sampler> ; lv2:symbol "notify" ; - ui:notifyType atom:Blank ; + ui:notifyType atom:Blank ; ] . diff --git a/plugins/eg-sampler.lv2/wscript b/plugins/eg-sampler.lv2/wscript index c25b28d..1f3ec04 100644 --- a/plugins/eg-sampler.lv2/wscript +++ b/plugins/eg-sampler.lv2/wscript @@ -31,6 +31,8 @@ def configure(conf): uselib_store='LV2_PATCH') autowaf.check_pkg(conf, 'lv2-lv2plug.in-ns-ext-state', uselib_store='LV2_STATE') + autowaf.check_pkg(conf, 'lv2-lv2plug.in-ns-ext-worker', + uselib_store='LV2_WORKER') conf.check(function_name='mlock', header_name='sys/mman.h', @@ -85,11 +87,11 @@ def build(bld): # Build plugin library obj = bld(features = 'c cshlib', env = penv, - source = ['sampler.c', 'zix/ring.c'], + source = ['sampler.c'], name = 'sampler', target = '%s/sampler' % bundle, install_path = '${LV2DIR}/%s' % bundle, - use = 'SNDFILE LV2CORE LV2_URID LV2_ATOM LV2_STATE LV2_MESSAGE', + use = 'SNDFILE LV2CORE LV2_URID LV2_ATOM LV2_STATE LV2_PATCH LV2_WORKER', includes = includes) # Build UI library @@ -100,6 +102,6 @@ def build(bld): name = 'sampler_ui', target = '%s/sampler_ui' % bundle, install_path = '${LV2DIR}/%s' % bundle, - use = 'GTK2 LV2CORE LV2_URID LV2_ATOM LV2_STATE LV2_MESSAGE', + use = 'GTK2 LV2CORE LV2_URID LV2_ATOM LV2_PATCH', includes = includes) diff --git a/plugins/eg-sampler.lv2/zix/common.h b/plugins/eg-sampler.lv2/zix/common.h deleted file mode 100644 index f113cfe..0000000 --- a/plugins/eg-sampler.lv2/zix/common.h +++ /dev/null @@ -1,83 +0,0 @@ -/* - Copyright 2011-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. -*/ - -#ifndef ZIX_COMMON_H -#define ZIX_COMMON_H - -/** - @addtogroup zix - @{ -*/ - -/** @cond */ -#ifdef ZIX_SHARED -# ifdef _WIN32 -# define ZIX_LIB_IMPORT __declspec(dllimport) -# define ZIX_LIB_EXPORT __declspec(dllexport) -# else -# define ZIX_LIB_IMPORT __attribute__((visibility("default"))) -# define ZIX_LIB_EXPORT __attribute__((visibility("default"))) -# endif -# ifdef ZIX_INTERNAL -# define ZIX_API ZIX_LIB_EXPORT -# else -# define ZIX_API ZIX_LIB_IMPORT -# endif -#else -# define ZIX_API -#endif -/** @endcond */ - -#ifdef __cplusplus -extern "C" { -#else -# include <stdbool.h> -#endif - -typedef enum { - ZIX_STATUS_SUCCESS, - ZIX_STATUS_ERROR, - ZIX_STATUS_NO_MEM, - ZIX_STATUS_NOT_FOUND, - ZIX_STATUS_EXISTS, - ZIX_STATUS_BAD_ARG, - ZIX_STATUS_BAD_PERMS, -} ZixStatus; - -/** - Function for comparing two elements. -*/ -typedef int (*ZixComparator)(const void* a, const void* b, void* user_data); - -/** - Function for testing equality of two elements. -*/ -typedef bool (*ZixEqualFunc)(const void* a, const void* b); - -/** - Function to destroy an element. -*/ -typedef void (*ZixDestroyFunc)(void* ptr); - -/** - @} -*/ - -#ifdef __cplusplus -} /* extern "C" */ -#endif - -#endif /* ZIX_COMMON_H */ diff --git a/plugins/eg-sampler.lv2/zix/ring.c b/plugins/eg-sampler.lv2/zix/ring.c deleted file mode 100644 index 29d415c..0000000 --- a/plugins/eg-sampler.lv2/zix/ring.c +++ /dev/null @@ -1,231 +0,0 @@ -/* - Copyright 2011-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. -*/ - -#include <stdint.h> -#include <stdlib.h> -#include <string.h> - -#ifdef HAVE_MLOCK -# include <sys/mman.h> -# define ZIX_MLOCK(ptr, size) mlock((ptr), (size)) -#elif defined(_WIN32) -# include <windows.h> -# define ZIX_MLOCK(ptr, size) VirtualLock((ptr), (size)) -#else -# pragma message("warning: No memory locking, possible RT violations") -# define ZIX_MLOCK(ptr, size) -#endif - -#if defined(__APPLE__) -# include <libkern/OSAtomic.h> -# define ZIX_FULL_BARRIER() OSMemoryBarrier() -#elif defined(_WIN32) -# include <windows.h> -# define ZIX_FULL_BARRIER() MemoryBarrier() -#elif (__GNUC__ > 4) || (__GNUC__ == 4 && __GNUC_MINOR__ >= 1) -# define ZIX_FULL_BARRIER() __sync_synchronize() -#else -# pragma message("warning: No memory barriers, possible SMP bugs") -# define ZIX_FULL_BARRIER() -#endif - -/* No support for any systems with separate read and write barriers */ -#define ZIX_READ_BARRIER() ZIX_FULL_BARRIER() -#define ZIX_WRITE_BARRIER() ZIX_FULL_BARRIER() - -#include "zix/ring.h" - -struct ZixRingImpl { - uint32_t write_head; ///< Read index into buf - uint32_t read_head; ///< Write index into buf - uint32_t size; ///< Size (capacity) in bytes - uint32_t size_mask; ///< Mask for fast modulo - char* buf; ///< Contents -}; - -static inline uint32_t -next_power_of_two(uint32_t size) -{ - // http://graphics.stanford.edu/~seander/bithacks.html#RoundUpPowerOf2 - size--; - size |= size >> 1; - size |= size >> 2; - size |= size >> 4; - size |= size >> 8; - size |= size >> 16; - size++; - return size; -} - -ZixRing* -zix_ring_new(uint32_t size) -{ - ZixRing* ring = (ZixRing*)malloc(sizeof(ZixRing)); - ring->write_head = 0; - ring->read_head = 0; - ring->size = next_power_of_two(size); - ring->size_mask = ring->size - 1; - ring->buf = (char*)malloc(ring->size); - return ring; -} - -void -zix_ring_free(ZixRing* ring) -{ - free(ring->buf); - free(ring); -} - -void -zix_ring_mlock(ZixRing* ring) -{ - ZIX_MLOCK(ring, sizeof(ZixRing)); - ZIX_MLOCK(ring->buf, ring->size); -} - -void -zix_ring_reset(ZixRing* ring) -{ - ring->write_head = 0; - ring->read_head = 0; -} - -static inline uint32_t -read_space_internal(const ZixRing* ring, uint32_t r, uint32_t w) -{ - if (r < w) { - return w - r; - } else { - return (w - r + ring->size) & ring->size_mask; - } -} - -uint32_t -zix_ring_read_space(const ZixRing* ring) -{ - return read_space_internal(ring, ring->read_head, ring->write_head); -} - -static inline uint32_t -write_space_internal(const ZixRing* ring, uint32_t r, uint32_t w) -{ - if (r == w) { - return ring->size - 1; - } else if (r < w) { - return ((r - w + ring->size) & ring->size_mask) - 1; - } else { - return (r - w) - 1; - } -} - -uint32_t -zix_ring_write_space(const ZixRing* ring) -{ - return write_space_internal(ring, ring->read_head, ring->write_head); -} - -uint32_t -zix_ring_capacity(const ZixRing* ring) -{ - return ring->size - 1; -} - -static inline uint32_t -peek_internal(const ZixRing* ring, uint32_t r, uint32_t w, - uint32_t size, void* dst) -{ - if (read_space_internal(ring, r, w) < size) { - return 0; - } - - if (r + size < ring->size) { - memcpy(dst, &ring->buf[r], size); - } else { - const uint32_t first_size = ring->size - r; - memcpy(dst, &ring->buf[r], first_size); - memcpy((char*)dst + first_size, &ring->buf[0], size - first_size); - } - - return size; -} - -uint32_t -zix_ring_peek(ZixRing* ring, void* dst, uint32_t size) -{ - const uint32_t r = ring->read_head; - const uint32_t w = ring->write_head; - - return peek_internal(ring, r, w, size, dst); -} - -uint32_t -zix_ring_read(ZixRing* ring, void* dst, uint32_t size) -{ - const uint32_t r = ring->read_head; - const uint32_t w = ring->write_head; - - if (peek_internal(ring, r, w, size, dst)) { - ZIX_READ_BARRIER(); - ring->read_head = (r + size) & ring->size_mask; - return size; - } else { - return 0; - } -} - -uint32_t -zix_ring_skip(ZixRing* ring, uint32_t size) -{ - const uint32_t r = ring->read_head; - const uint32_t w = ring->write_head; - if (read_space_internal(ring, r, w) < size) { - return 0; - } - - ZIX_READ_BARRIER(); - ring->read_head = (r + size) & ring->size_mask; - return size; -} - -uint32_t -zix_ring_write(ZixRing* ring, const void* src, uint32_t size) -{ - const uint32_t r = ring->read_head; - const uint32_t w = ring->write_head; - if (write_space_internal(ring, r, w) < size) { - return 0; - } - - if (w + size <= ring->size) { - memcpy(&ring->buf[w], src, size); - ZIX_WRITE_BARRIER(); - ring->write_head = (w + size) & ring->size_mask; - } else { - const uint32_t this_size = ring->size - w; - memcpy(&ring->buf[w], src, this_size); - memcpy(&ring->buf[0], (char*)src + this_size, size - this_size); - ZIX_WRITE_BARRIER(); - ring->write_head = size - this_size; - } - - return size; -} - -void* -zix_ring_write_head(ZixRing* ring) -{ - return &ring->buf[ring->write_head]; -} diff --git a/plugins/eg-sampler.lv2/zix/ring.h b/plugins/eg-sampler.lv2/zix/ring.h deleted file mode 100644 index dd45769..0000000 --- a/plugins/eg-sampler.lv2/zix/ring.h +++ /dev/null @@ -1,136 +0,0 @@ -/* - Copyright 2011-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. -*/ - -#ifndef ZIX_RING_H -#define ZIX_RING_H - -#include <stdint.h> - -#include "zix/common.h" - -#ifdef __cplusplus -extern "C" { -#endif - -/** - @addtogroup zix - @{ - @name Ring - @{ -*/ - -/** - A lock-free ring buffer. - - Thread-safe with a single reader and single writer, and realtime safe - on both ends. -*/ -typedef struct ZixRingImpl ZixRing; - -/** - Create a new ring. - @param size Size in bytes (note this may be rounded up). - - At most @c size - 1 bytes may be stored in the ring at once. -*/ -ZixRing* -zix_ring_new(uint32_t size); - -/** - Destroy a ring. -*/ -void -zix_ring_free(ZixRing* ring); - -/** - Lock the ring data into physical memory. - - This function is NOT thread safe or real-time safe, but it should be called - after zix_ring_new() to lock all ring memory to avoid page faults while - using the ring (i.e. this function MUST be called first in order for the - ring to be truly real-time safe). - -*/ -void -zix_ring_mlock(ZixRing* ring); - -/** - Reset (empty) a ring. - - This function is NOT thread-safe, it may only be called when there are no - readers or writers. -*/ -void -zix_ring_reset(ZixRing* ring); - -/** - Return the number of bytes of space available for reading. -*/ -uint32_t -zix_ring_read_space(const ZixRing* ring); - -/** - Return the number of bytes of space available for writing. -*/ -uint32_t -zix_ring_write_space(const ZixRing* ring); - -/** - Return the capacity (i.e. total write space when empty). -*/ -uint32_t -zix_ring_capacity(const ZixRing* ring); - -/** - Read from the ring without advancing the read head. -*/ -uint32_t -zix_ring_peek(ZixRing* ring, void* dst, uint32_t size); - -/** - Read from the ring and advance the read head. -*/ -uint32_t -zix_ring_read(ZixRing* ring, void* dst, uint32_t size); - -/** - Skip data in the ring (advance read head without reading). -*/ -uint32_t -zix_ring_skip(ZixRing* ring, uint32_t size); - -/** - Write data to the ring. -*/ -uint32_t -zix_ring_write(ZixRing* ring, const void* src, uint32_t size); - -/** - Return a pointer to the current position of the write head. -*/ -void* -zix_ring_write_head(ZixRing* ring); - -/** - @} - @} -*/ - -#ifdef __cplusplus -} /* extern "C" */ -#endif - -#endif /* ZIX_RING_H */ diff --git a/plugins/eg-sampler.lv2/zix/sem.h b/plugins/eg-sampler.lv2/zix/sem.h deleted file mode 100644 index d536c99..0000000 --- a/plugins/eg-sampler.lv2/zix/sem.h +++ /dev/null @@ -1,236 +0,0 @@ -/* - Copyright 2012-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. -*/ - -#ifndef ZIX_SEM_H -#define ZIX_SEM_H - -#ifdef __APPLE__ -# include <mach/mach.h> -#elif defined(_WIN32) -# include <windows.h> -#else -# include <semaphore.h> -# include <errno.h> -#endif - -#include "zix/common.h" - -#ifdef __cplusplus -extern "C" { -#endif - -/** - @addtogroup zix - @{ - @name Semaphore - @{ -*/ - -/** - A counting semaphore. - - This is an integer that is always positive, and has two main operations: - increment (post) and decrement (wait). If a decrement can not be performed - (i.e. the value is 0) the caller will be blocked until another thread posts - and the operation can succeed. - - Semaphores can be created with any starting value, but typically this will - be 0 so the semaphore can be used as a simple signal where each post - corresponds to one wait. - - Semaphores are very efficient (much moreso than a mutex/cond pair). In - particular, at least on Linux, post is async-signal-safe, which means it - does not block and will not be interrupted. If you need to signal from - a realtime thread, this is the most appropriate primitive to use. -*/ -typedef struct ZixSemImpl ZixSem; - -/** - Create and initialize @c sem to @c initial. -*/ -static inline ZixStatus -zix_sem_init(ZixSem* sem, unsigned initial); - -/** - Destroy @c sem. -*/ -static inline void -zix_sem_destroy(ZixSem* sem); - -/** - Increment (and signal any waiters). - Realtime safe. -*/ -static inline void -zix_sem_post(ZixSem* sem); - -/** - Wait until count is > 0, then decrement. - Obviously not realtime safe. -*/ -static inline ZixStatus -zix_sem_wait(ZixSem* sem); - -/** - Non-blocking version of wait(). - - @return true if decrement was successful (lock was acquired). -*/ -static inline bool -zix_sem_try_wait(ZixSem* sem); - -/** - @cond -*/ - -#ifdef __APPLE__ - -struct ZixSemImpl { - semaphore_t sem; -}; - -static inline ZixStatus -zix_sem_init(ZixSem* sem, unsigned initial) -{ - return semaphore_create(mach_task_self(), &sem->sem, SYNC_POLICY_FIFO, 0) - ? ZIX_STATUS_ERROR : ZIX_STATUS_SUCCESS; -} - -static inline void -zix_sem_destroy(ZixSem* sem) -{ - semaphore_destroy(mach_task_self(), sem->sem); -} - -static inline void -zix_sem_post(ZixSem* sem) -{ - semaphore_signal(sem->sem); -} - -static inline ZixStatus -zix_sem_wait(ZixSem* sem) -{ - if (semaphore_wait(sem->sem) != KERN_SUCCESS) { - return ZIX_STATUS_ERROR; - } - return ZIX_STATUS_SUCCESS; -} - -static inline bool -zix_sem_try_wait(ZixSem* sem) -{ - const mach_timespec_t zero = { 0, 0 }; - return semaphore_timedwait(sem->sem, zero) == KERN_SUCCESS; -} - -#elif defined(_WIN32) - -struct ZixSemImpl { - HANDLE sem; -}; - -static inline ZixStatus -zix_sem_init(ZixSem* sem, unsigned initial) -{ - sem->sem = CreateSemaphore(NULL, initial, LONG_MAX, NULL); - return (sem->sem) ? ZIX_STATUS_ERROR : ZIX_STATUS_SUCCESS; -} - -static inline void -zix_sem_destroy(ZixSem* sem) -{ - CloseHandle(sem->sem); -} - -static inline void -zix_sem_post(ZixSem* sem) -{ - ReleaseSemaphore(sem->sem, 1, NULL); -} - -static inline ZixStatus -zix_sem_wait(ZixSem* sem) -{ - if (WaitForSingleObject(sem->sem, INFINITE) != WAIT_OBJECT_0) { - return ZIX_STATUS_ERROR; - } - return ZIX_STATUS_SUCCESS; -} - -static inline bool -zix_sem_try_wait(ZixSem* sem) -{ - WaitForSingleObject(sem->sem, 0); -} - -#else /* !defined(__APPLE__) && !defined(_WIN32) */ - -struct ZixSemImpl { - sem_t sem; -}; - -static inline ZixStatus -zix_sem_init(ZixSem* sem, unsigned initial) -{ - return sem_init(&sem->sem, 0, initial) - ? ZIX_STATUS_ERROR : ZIX_STATUS_SUCCESS; -} - -static inline void -zix_sem_destroy(ZixSem* sem) -{ - sem_destroy(&sem->sem); -} - -static inline void -zix_sem_post(ZixSem* sem) -{ - sem_post(&sem->sem); -} - -static inline ZixStatus -zix_sem_wait(ZixSem* sem) -{ - while (sem_wait(&sem->sem)) { - if (errno != EINTR) { - return ZIX_STATUS_ERROR; - } - /* Otherwise, interrupted, so try again. */ - } - - return ZIX_STATUS_SUCCESS; -} - -static inline bool -zix_sem_try_wait(ZixSem* sem) -{ - return (sem_trywait(&sem->sem) == 0); -} - -#endif - -/** - @endcond - @} - @} -*/ - -#ifdef __cplusplus -} /* extern "C" */ -#endif - -#endif /* ZIX_SEM_H */ diff --git a/plugins/eg-sampler.lv2/zix/thread.h b/plugins/eg-sampler.lv2/zix/thread.h deleted file mode 100644 index 602b701..0000000 --- a/plugins/eg-sampler.lv2/zix/thread.h +++ /dev/null @@ -1,133 +0,0 @@ -/* - Copyright 2012-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. -*/ - -#ifndef ZIX_THREAD_H -#define ZIX_THREAD_H - -#ifdef _WIN32 -# include <windows.h> -#else -# include <errno.h> -# include <pthread.h> -#endif - -#include "zix/common.h" - -#ifdef __cplusplus -extern "C" { -#else -# include <stdbool.h> -#endif - -/** - @addtogroup zix - @{ - @name Thread - @{ -*/ - -#ifdef _WIN32 -typedef HANDLE ZixThread; -#else -typedef pthread_t ZixThread; -#endif - -/** - Initialize @c thread to a new thread. - - The thread will immediately be launched, calling @c function with @c arg - as the only parameter. -*/ -static inline ZixStatus -zix_thread_create(ZixThread* thread, - size_t stack_size, - void* (*function)(void*), - void* arg); - -/** - Join @c thread (block until @c thread exits). -*/ -static inline ZixStatus -zix_thread_join(ZixThread thread, void** retval); - -#ifdef _WIN32 - -static inline ZixStatus -zix_thread_create(ZixThread* thread, - size_t stack_size, - void* (*function)(void*), - void* arg) -{ - *thread = CreateThread(NULL, stack_size, - (LPTHREAD_START_ROUTINE)function, arg, - 0, NULL); - return *thread ? ZIX_STATUS_SUCCESS : ZIX_STATUS_ERROR; -} - -static inline ZixStatus -zix_thread_join(ZixThread thread, void** retval) -{ - return WaitForSingleObject(thread, INFINITE) - ? ZIX_STATUS_SUCCESS : ZIX_STATUS_ERROR; -} - -#else /* !defined(_WIN32) */ - -static inline ZixStatus -zix_thread_create(ZixThread* thread, - size_t stack_size, - void* (*function)(void*), - void* arg) -{ - pthread_attr_t attr; - pthread_attr_init(&attr); - pthread_attr_setstacksize(&attr, stack_size); - - const int ret = pthread_create(thread, NULL, function, arg); - pthread_attr_destroy(&attr); - - if (ret == EAGAIN) { - return ZIX_STATUS_NO_MEM; - } else if (ret == EINVAL) { - return ZIX_STATUS_BAD_ARG; - } else if (ret == EPERM) { - return ZIX_STATUS_BAD_PERMS; - } else if (ret) { - return ZIX_STATUS_ERROR; - } - - return ZIX_STATUS_SUCCESS; -} - -static inline ZixStatus -zix_thread_join(ZixThread thread, void** retval) -{ - return pthread_join(thread, retval) - ? ZIX_STATUS_ERROR : ZIX_STATUS_SUCCESS; -} - -#endif - -/** - @} - @} -*/ - -#ifdef __cplusplus -} /* extern "C" */ -#endif - -#endif /* ZIX_THREAD_H */ |