diff options
author | David Robillard <d@drobilla.net> | 2022-08-10 17:11:13 -0400 |
---|---|---|
committer | David Robillard <d@drobilla.net> | 2022-08-17 13:51:11 -0400 |
commit | 5cdf800837b2e8062042b300b1755db2482a89e3 (patch) | |
tree | 4cbfd92143d26db800892a495b30e461d7dea0aa | |
parent | 19c9e4a59b92ce1aaf2ae0cefe2e0f2b6e2e44e3 (diff) | |
download | jalv-5cdf800837b2e8062042b300b1755db2482a89e3.tar.gz jalv-5cdf800837b2e8062042b300b1755db2482a89e3.tar.bz2 jalv-5cdf800837b2e8062042b300b1755db2482a89e3.zip |
Make worker opaque
-rw-r--r-- | src/jalv.c | 64 | ||||
-rw-r--r-- | src/jalv_internal.h | 5 | ||||
-rw-r--r-- | src/worker.c | 113 | ||||
-rw-r--r-- | src/worker.h | 85 |
4 files changed, 181 insertions, 86 deletions
@@ -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 |