From 806dd3218ab67efcc68e25bfe3a68fddfec029b6 Mon Sep 17 00:00:00 2001
From: David Robillard <d@drobilla.net>
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

(limited to 'plugins')

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 <stdlib.h>
 #include <string.h>
 
-#include <pthread.h>
-
 #include <sndfile.h>
 
 #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 <windows.h>
 #else
 #    include <semaphore.h>
+#    include <errno.h>
 #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 <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 */
-- 
cgit v1.2.1