aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDavid Robillard <d@drobilla.net>2022-08-10 17:11:13 -0400
committerDavid Robillard <d@drobilla.net>2022-08-17 13:51:11 -0400
commit5cdf800837b2e8062042b300b1755db2482a89e3 (patch)
tree4cbfd92143d26db800892a495b30e461d7dea0aa
parent19c9e4a59b92ce1aaf2ae0cefe2e0f2b6e2e44e3 (diff)
downloadjalv-5cdf800837b2e8062042b300b1755db2482a89e3.tar.gz
jalv-5cdf800837b2e8062042b300b1755db2482a89e3.tar.bz2
jalv-5cdf800837b2e8062042b300b1755db2482a89e3.zip
Make worker opaque
-rw-r--r--src/jalv.c64
-rw-r--r--src/jalv_internal.h5
-rw-r--r--src/worker.c113
-rw-r--r--src/worker.h85
4 files changed, 181 insertions, 86 deletions
diff --git a/src/jalv.c b/src/jalv.c
index 7b6da03..9b96993 100644
--- a/src/jalv.c
+++ b/src/jalv.c
@@ -711,15 +711,11 @@ jalv_run(Jalv* jalv, uint32_t nframes)
// Run plugin for this cycle
lilv_instance_run(jalv->instance, nframes);
- // Process any worker replies
+ // Process any worker replies and end the cycle
LV2_Handle handle = lilv_instance_get_handle(jalv->instance);
- jalv_worker_emit_responses(&jalv->state_worker, handle);
- jalv_worker_emit_responses(&jalv->worker, handle);
-
- // Notify the plugin the run() cycle is finished
- if (jalv->worker.iface && jalv->worker.iface->end_run) {
- jalv->worker.iface->end_run(jalv->instance->lv2_handle);
- }
+ jalv_worker_emit_responses(jalv->state_worker, handle);
+ jalv_worker_emit_responses(jalv->worker, handle);
+ jalv_worker_end_run(jalv->worker);
// Check if it's time to send updates to the UI
jalv->event_delta_t += nframes;
@@ -914,11 +910,6 @@ jalv_open(Jalv* const jalv, int* argc, char*** argv)
jalv->map.map = map_uri;
init_feature(&jalv->features.map_feature, LV2_URID__map, &jalv->map);
- jalv->worker.lock = &jalv->work_lock;
- jalv->worker.exit = &jalv->exit;
- jalv->state_worker.lock = &jalv->work_lock;
- jalv->state_worker.exit = &jalv->exit;
-
jalv->unmap.handle = jalv;
jalv->unmap.unmap = unmap_uri;
init_feature(&jalv->features.unmap_feature, LV2_URID__unmap, &jalv->unmap);
@@ -988,12 +979,10 @@ jalv_open(Jalv* const jalv, int* argc, char*** argv)
LV2_STATE__makePath,
&jalv->features.make_path);
- jalv->features.sched.handle = &jalv->worker;
jalv->features.sched.schedule_work = jalv_worker_schedule;
init_feature(
&jalv->features.sched_feature, LV2_WORKER__schedule, &jalv->features.sched);
- jalv->features.ssched.handle = &jalv->state_worker;
jalv->features.ssched.schedule_work = jalv_worker_schedule;
init_feature(&jalv->features.state_sched_feature,
LV2_WORKER__schedule,
@@ -1010,9 +999,7 @@ jalv_open(Jalv* const jalv, int* argc, char*** argv)
&jalv->features.request_value);
zix_sem_init(&jalv->done, 0);
-
zix_sem_init(&jalv->paused, 0);
- zix_sem_init(&jalv->worker.sem, 0);
// Find all installed plugins
LilvWorld* world = lilv_world_new();
@@ -1108,6 +1095,17 @@ jalv_open(Jalv* const jalv, int* argc, char*** argv)
return -4;
}
+ // Create workers if necessary
+ if (lilv_plugin_has_extension_data(jalv->plugin,
+ jalv->nodes.work_interface)) {
+ jalv->worker = jalv_worker_new(&jalv->work_lock, true);
+ jalv->features.sched.handle = jalv->worker;
+ if (jalv->safe_restore) {
+ jalv->state_worker = jalv_worker_new(&jalv->work_lock, false);
+ jalv->features.ssched.handle = jalv->state_worker;
+ }
+ }
+
// Load preset, if specified
if (jalv->opts.preset) {
LilvNode* preset = lilv_new_uri(jalv->world, jalv->opts.preset);
@@ -1312,27 +1310,19 @@ jalv_open(Jalv* const jalv, int* argc, char*** argv)
jalv->features.ext_data.data_access =
lilv_instance_get_descriptor(jalv->instance)->extension_data;
- jalv->worker.handle = jalv->instance->lv2_handle;
- jalv->state_worker.handle = jalv->instance->lv2_handle;
+ const LV2_Worker_Interface* worker_iface =
+ (const LV2_Worker_Interface*)lilv_instance_get_extension_data(
+ jalv->instance, LV2_WORKER__interface);
+
+ jalv_worker_start(jalv->worker, worker_iface, jalv->instance->lv2_handle);
+ jalv_worker_start(
+ jalv->state_worker, worker_iface, jalv->instance->lv2_handle);
printf("\n");
if (!jalv->buf_size_set) {
jalv_allocate_port_buffers(jalv);
}
- // Create workers if necessary
- if (lilv_plugin_has_extension_data(jalv->plugin,
- jalv->nodes.work_interface)) {
- const LV2_Worker_Interface* iface =
- (const LV2_Worker_Interface*)lilv_instance_get_extension_data(
- jalv->instance, LV2_WORKER__interface);
-
- jalv_worker_init(&jalv->worker, iface, true);
- if (jalv->safe_restore) {
- jalv_worker_init(&jalv->state_worker, iface, false);
- }
- }
-
// Apply loaded state to plugin instance if necessary
if (state) {
jalv_apply_state(jalv, state);
@@ -1375,10 +1365,8 @@ jalv_open(Jalv* const jalv, int* argc, char*** argv)
int
jalv_close(Jalv* const jalv)
{
- jalv->exit = true;
-
// Terminate the worker
- jalv_worker_finish(&jalv->worker);
+ jalv_worker_exit(jalv->worker);
// Deactivate audio
if (jalv->backend) {
@@ -1394,10 +1382,8 @@ jalv_close(Jalv* const jalv)
}
// Destroy the worker
- jalv_worker_destroy(&jalv->worker);
- if (jalv->safe_restore) {
- jalv_worker_destroy(&jalv->state_worker);
- }
+ jalv_worker_free(jalv->worker);
+ jalv_worker_free(jalv->state_worker);
// Deactivate plugin
#if USE_SUIL
diff --git a/src/jalv_internal.h b/src/jalv_internal.h
index f7b820e..01d4c86 100644
--- a/src/jalv_internal.h
+++ b/src/jalv_internal.h
@@ -78,8 +78,8 @@ struct JalvImpl {
ZixRing* ui_to_plugin; ///< Port events from UI
ZixRing* plugin_to_ui; ///< Port events from plugin
void* ui_event_buf; ///< Buffer for reading UI port events
- JalvWorker worker; ///< Worker thread implementation
- JalvWorker state_worker; ///< Synchronous worker for state restore
+ JalvWorker* worker; ///< Worker thread implementation
+ JalvWorker* state_worker; ///< Synchronous worker for state restore
ZixSem work_lock; ///< Lock for plugin work() method
ZixSem done; ///< Exit semaphore
ZixSem paused; ///< Paused signal from process thread
@@ -112,7 +112,6 @@ struct JalvImpl {
float bpm; ///< Transport tempo in beats per minute
bool rolling; ///< Transport speed (0=stop, 1=play)
bool buf_size_set; ///< True iff buffer size callback fired
- bool exit; ///< True iff execution is finished
bool has_ui; ///< True iff a control UI is present
bool request_update; ///< True iff a plugin update is needed
bool safe_restore; ///< Plugin restore() is thread-safe
diff --git a/src/worker.c b/src/worker.c
index 1fdfaaf..63df419 100644
--- a/src/worker.c
+++ b/src/worker.c
@@ -5,6 +5,7 @@
#include "lv2/core/lv2.h"
#include "lv2/worker/worker.h"
+#include "zix/common.h"
#include "zix/ring.h"
#include "zix/sem.h"
#include "zix/thread.h"
@@ -14,6 +15,19 @@
#define MAX_PACKET_SIZE 4096U
+struct JalvWorkerImpl {
+ ZixRing* requests; ///< Requests to the worker
+ ZixRing* responses; ///< Responses from the worker
+ void* response; ///< Worker response buffer
+ ZixSem* lock; ///< Lock for plugin work() method
+ bool exit; ///< Exit flag
+ ZixSem sem; ///< Worker semaphore
+ ZixThread thread; ///< Worker thread
+ LV2_Handle handle; ///< Plugin handle
+ const LV2_Worker_Interface* iface; ///< Plugin worker interface
+ bool threaded; ///< Run work in another thread
+};
+
static LV2_Worker_Status
jalv_worker_write_packet(ZixRing* const target,
const uint32_t size,
@@ -46,7 +60,7 @@ worker_func(void* const data)
while (true) {
// Wait for a request
zix_sem_wait(&worker->sem);
- if (*worker->exit) {
+ if (worker->exit) {
break;
}
@@ -77,41 +91,86 @@ worker_func(void* const data)
return NULL;
}
-void
-jalv_worker_init(JalvWorker* const worker,
- const LV2_Worker_Interface* const iface,
- const bool threaded)
+static ZixStatus
+jalv_worker_launch(JalvWorker* const worker)
{
- worker->iface = iface;
- worker->threaded = threaded;
- worker->responses = zix_ring_new(NULL, MAX_PACKET_SIZE);
- worker->response = malloc(MAX_PACKET_SIZE);
+ ZixStatus st = ZIX_STATUS_SUCCESS;
+
+ if ((st = zix_sem_init(&worker->sem, 0)) ||
+ (st = zix_thread_create(&worker->thread, 4096U, worker_func, worker))) {
+ return st;
+ }
+
+ ZixRing* const requests = zix_ring_new(NULL, MAX_PACKET_SIZE);
+ if (!requests) {
+ zix_thread_join(worker->thread, NULL);
+ zix_sem_destroy(&worker->sem);
+ return ZIX_STATUS_NO_MEM;
+ }
- if (threaded) {
- worker->requests = zix_ring_new(NULL, MAX_PACKET_SIZE);
+ zix_ring_mlock(requests);
+ worker->requests = requests;
+ return ZIX_STATUS_SUCCESS;
+}
- zix_thread_create(&worker->thread, 4096U, worker_func, worker);
- zix_ring_mlock(worker->requests);
+JalvWorker*
+jalv_worker_new(ZixSem* const lock, const bool threaded)
+{
+ JalvWorker* const worker = (JalvWorker*)calloc(1, sizeof(JalvWorker));
+ ZixRing* const responses = zix_ring_new(NULL, MAX_PACKET_SIZE);
+ void* const response = calloc(1, MAX_PACKET_SIZE);
+
+ if (worker && responses && response) {
+ worker->threaded = threaded;
+ worker->responses = responses;
+ worker->response = response;
+ worker->lock = lock;
+ worker->exit = false;
+
+ zix_ring_mlock(responses);
+ if (!threaded || !jalv_worker_launch(worker)) {
+ return worker;
+ }
}
- zix_ring_mlock(worker->responses);
+ free(worker);
+ zix_ring_free(responses);
+ free(response);
+ return NULL;
}
void
-jalv_worker_finish(JalvWorker* const worker)
+jalv_worker_start(JalvWorker* const worker,
+ const LV2_Worker_Interface* const iface,
+ LV2_Handle handle)
{
- if (worker->threaded) {
+ if (worker) {
+ worker->iface = iface;
+ worker->handle = handle;
+ }
+}
+
+void
+jalv_worker_exit(JalvWorker* const worker)
+{
+ if (worker && worker->threaded) {
+ worker->exit = true;
zix_sem_post(&worker->sem);
zix_thread_join(worker->thread, NULL);
+ worker->threaded = false;
}
}
void
-jalv_worker_destroy(JalvWorker* const worker)
+jalv_worker_free(JalvWorker* const worker)
{
- zix_ring_free(worker->requests);
- zix_ring_free(worker->responses);
- free(worker->response);
+ if (worker) {
+ jalv_worker_exit(worker);
+ zix_ring_free(worker->requests);
+ zix_ring_free(worker->responses);
+ free(worker->response);
+ free(worker);
+ }
}
LV2_Worker_Status
@@ -119,10 +178,10 @@ jalv_worker_schedule(LV2_Worker_Schedule_Handle handle,
const uint32_t size,
const void* const data)
{
- JalvWorker* worker = (JalvWorker*)handle;
+ JalvWorker* const worker = (JalvWorker*)handle;
LV2_Worker_Status st = LV2_WORKER_SUCCESS;
- if (!size) {
+ if (!worker || !size) {
return LV2_WORKER_ERR_UNKNOWN;
}
@@ -148,7 +207,7 @@ jalv_worker_emit_responses(JalvWorker* const worker, LV2_Handle lv2_handle)
{
static const uint32_t size_size = (uint32_t)sizeof(uint32_t);
- if (worker->responses) {
+ if (worker && worker->responses) {
uint32_t size = 0U;
while (zix_ring_read(worker->responses, &size, size_size) == size_size) {
if (zix_ring_read(worker->responses, worker->response, size) == size) {
@@ -157,3 +216,11 @@ jalv_worker_emit_responses(JalvWorker* const worker, LV2_Handle lv2_handle)
}
}
}
+
+void
+jalv_worker_end_run(JalvWorker* const worker)
+{
+ if (worker && worker->iface && worker->iface->end_run) {
+ worker->iface->end_run(worker->handle);
+ }
+}
diff --git a/src/worker.h b/src/worker.h
index 5956e23..09d35ca 100644
--- a/src/worker.h
+++ b/src/worker.h
@@ -6,9 +6,7 @@
#include "attributes.h"
-#include "zix/ring.h"
#include "zix/sem.h"
-#include "zix/thread.h"
#include "lv2/core/lv2.h"
#include "lv2/worker/worker.h"
@@ -18,40 +16,85 @@
JALV_BEGIN_DECLS
-// Worker for running non-realtime tasks for plugins
-
-typedef struct {
- ZixRing* requests; ///< Requests to the worker
- ZixRing* responses; ///< Responses from the worker
- void* response; ///< Worker response buffer
- ZixSem* lock; ///< Lock for plugin work() method
- bool* exit; ///< Pointer to exit flag
- ZixSem sem; ///< Worker semaphore
- ZixThread thread; ///< Worker thread
- LV2_Handle handle; ///< Plugin handle
- const LV2_Worker_Interface* iface; ///< Plugin worker interface
- bool threaded; ///< Run work in another thread
-} JalvWorker;
+/**
+ A worker for running non-realtime tasks for plugins.
+ The worker can be used in threaded mode, which allows non-realtime work to be
+ done with latency when running realtime, or non-threaded mode, which performs
+ work immediately for state restoration or offline rendering.
+*/
+typedef struct JalvWorkerImpl JalvWorker;
+
+/**
+ Allocate a new worker and launch its thread if necessary.
+
+ @param lock Pointer to lock used to guard doing work.
+ @param threaded If true, launch a thread to perform work in.
+ @return A newly allocated worker, or null on error.
+*/
+JalvWorker*
+jalv_worker_new(ZixSem* lock, bool threaded);
+
+/**
+ Start performing work for a plugin, launching the thread if necessary.
+
+ This must be called before scheduling any work.
+
+ @param iface Worker interface from plugin.
+ @param handle Handle to the LV2 plugin this worker is for.
+*/
void
-jalv_worker_init(JalvWorker* worker,
- const LV2_Worker_Interface* iface,
- bool threaded);
+jalv_worker_start(JalvWorker* worker,
+ const LV2_Worker_Interface* iface,
+ LV2_Handle handle);
+
+/**
+ Terminate the worker's thread if necessary.
+ For threaded workers, this blocks until the thread has exited. For
+ non-threaded workers, this does nothing.
+*/
void
-jalv_worker_finish(JalvWorker* worker);
+jalv_worker_exit(JalvWorker* worker);
+/**
+ Free a worker allocated with jalv_worker_new().
+
+ Calls jalv_worker_exit() to terminate the running thread if necessary.
+*/
void
-jalv_worker_destroy(JalvWorker* worker);
+jalv_worker_free(JalvWorker* worker);
+
+/**
+ Schedule work to be performed by the worker in the audio thread.
+ For threaded workers, this enqueues a request that will be handled by the
+ worker thread asynchronously. For non-threaded workers, the work is
+ performed immediately before returning.
+*/
LV2_Worker_Status
jalv_worker_schedule(LV2_Worker_Schedule_Handle handle,
uint32_t size,
const void* data);
+/**
+ Emit any pending responses to the plugin in the audio thread.
+
+ This dispatches responses from work that has been completed since the last
+ call, so the plugin knows it is finished and can apply the changes.
+*/
void
jalv_worker_emit_responses(JalvWorker* worker, LV2_Handle lv2_handle);
+/**
+ Notify the plugin that the run() cycle is finished.
+
+ This must be called near the end of every cycle, after any potential calls
+ to jalv_worker_emit_responses().
+*/
+void
+jalv_worker_end_run(JalvWorker* worker);
+
JALV_END_DECLS
#endif // JALV_WORKER_H