From 806dd3218ab67efcc68e25bfe3a68fddfec029b6 Mon Sep 17 00:00:00 2001 From: David Robillard Date: Mon, 13 Feb 2012 01:01:38 +0000 Subject: Use portable thread implementation. --- plugins/eg-sampler.lv2/sampler.c | 52 +++++++------- plugins/eg-sampler.lv2/zix/sem.h | 32 ++++++--- plugins/eg-sampler.lv2/zix/thread.h | 133 ++++++++++++++++++++++++++++++++++++ 3 files changed, 182 insertions(+), 35 deletions(-) create mode 100644 plugins/eg-sampler.lv2/zix/thread.h diff --git a/plugins/eg-sampler.lv2/sampler.c b/plugins/eg-sampler.lv2/sampler.c index 8ee2c05..c6bf30c 100644 --- a/plugins/eg-sampler.lv2/sampler.c +++ b/plugins/eg-sampler.lv2/sampler.c @@ -37,8 +37,6 @@ #include #include -#include - #include #include "lv2/lv2plug.in/ns/ext/atom/atom-helpers.h" @@ -48,6 +46,7 @@ #include "lv2/lv2plug.in/ns/lv2core/lv2.h" #include "zix/sem.h" +#include "zix/thread.h" #include "./uris.h" @@ -70,10 +69,14 @@ typedef struct { /* Features */ LV2_URID_Map* map; + /* Worker thread */ + ZixThread worker_thread; + ZixSem signal; + bool exit; + /* Sample */ SampleFile* samp; SampleFile* pending_samp; - ZixSem signal; int pending_sample_ready; /* Ports */ @@ -92,11 +95,9 @@ typedef struct { } uris; /* Playback state */ - bool play; sf_count_t frame; + bool play; - /* File loading */ - pthread_t worker_thread; } Sampler; static void @@ -142,8 +143,7 @@ worker_thread_main(void* arg) { Sampler* plugin = (Sampler*)arg; - /* TODO: This thread never exits cleanly */ - while (true) { + while (!plugin->exit) { /* Wait for run() to signal that we need to load a sample */ zix_sem_wait(&plugin->signal); @@ -154,21 +154,6 @@ worker_thread_main(void* arg) return 0; } -static void -cleanup(LV2_Handle instance) -{ - Sampler* plugin = (Sampler*)instance; - pthread_cancel(plugin->worker_thread); - pthread_join(plugin->worker_thread, 0); - zix_sem_destroy(&plugin->signal); - - free(plugin->samp->data); - free(plugin->pending_samp->data); - free(plugin->samp); - free(plugin->pending_samp); - free(instance); -} - static void connect_port(LV2_Handle instance, uint32_t port, @@ -216,7 +201,9 @@ instantiate(const LV2_Descriptor* descriptor, } /* Create worker thread */ - if (pthread_create(&plugin->worker_thread, 0, worker_thread_main, plugin)) { + 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; } @@ -257,6 +244,23 @@ fail: return 0; } +static void +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); + + free(plugin->samp->data); + free(plugin->pending_samp->data); + free(plugin->samp); + free(plugin->pending_samp); + free(instance); +} + static void run(LV2_Handle instance, uint32_t sample_count) diff --git a/plugins/eg-sampler.lv2/zix/sem.h b/plugins/eg-sampler.lv2/zix/sem.h index 98117c8..0b2bbb1 100644 --- a/plugins/eg-sampler.lv2/zix/sem.h +++ b/plugins/eg-sampler.lv2/zix/sem.h @@ -23,6 +23,7 @@ # include #else # include +# include #endif #include "zix/common.h" @@ -80,7 +81,7 @@ zix_sem_post(ZixSem* sem); Wait until count is > 0, then decrement. Obviously not realtime safe. */ -static inline void +static inline ZixStatus zix_sem_wait(ZixSem* sem); /** @@ -120,10 +121,13 @@ zix_sem_post(ZixSem* sem) semaphore_signal(sem->sem); } -static inline void +static inline ZixStatus zix_sem_wait(ZixSem* sem) { - semaphore_wait(sem->sem); + if (semaphore_wait(sem->sem) != KERN_SUCCESS) { + return ZIX_STATUS_ERROR; + } + return ZIX_STATUS_SUCCESS; } static inline bool @@ -158,10 +162,13 @@ zix_sem_post(ZixSem* sem) ReleaseSemaphore(sem->sem, 1, NULL); } -static inline void +static inline ZixStatus zix_sem_wait(ZixSem* sem) { - WaitForSingleObject(sem->sem, INFINITE); + if (WaitForSingleObject(sem->sem, INFINITE) != WAIT_OBJECT_0) { + return ZIX_STATUS_ERROR; + } + return ZIX_STATUS_SUCCESS; } static inline bool @@ -195,14 +202,17 @@ zix_sem_post(ZixSem* sem) sem_post(&sem->sem); } -static inline void +static inline ZixStatus zix_sem_wait(ZixSem* sem) { - /* Note that sem_wait always returns 0 in practice, except in - gdb (at least), where it returns nonzero, so the while is - necessary (and is the correct/safe solution in any case). - */ - while (sem_wait(&sem->sem) != 0) {} + while (sem_wait(&sem->sem)) { + if (errno != EINTR) { + return ZIX_STATUS_ERROR; + } + /* Otherwise, interrupted, so try again. */ + } + + return ZIX_STATUS_SUCCESS; } static inline bool diff --git a/plugins/eg-sampler.lv2/zix/thread.h b/plugins/eg-sampler.lv2/zix/thread.h new file mode 100644 index 0000000..ff5a727 --- /dev/null +++ b/plugins/eg-sampler.lv2/zix/thread.h @@ -0,0 +1,133 @@ +/* + Copyright 2012 David Robillard + + 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 +#else +# include +# include +#endif + +#include "zix/common.h" + +#ifdef __cplusplus +extern "C" { +#else +# include +#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 */ -- cgit v1.2.1