[vlc-devel] [PATCH 11/15] nacl: Add custom thread implementation
Julian Scheel
julian at jusst.de
Wed Mar 8 15:55:28 CET 2017
From: Dennis Hamester <dennis.hamester at startmail.com>
---
include/vlc_threads.h | 2 +-
src/Makefile.am | 1 +
src/nacl/thread.c | 510 ++++++++++++++++++++++++++++++++++++++++++++++++++
3 files changed, 512 insertions(+), 1 deletion(-)
create mode 100644 src/nacl/thread.c
diff --git a/include/vlc_threads.h b/include/vlc_threads.h
index 779046e718..f95d7b675f 100644
--- a/include/vlc_threads.h
+++ b/include/vlc_threads.h
@@ -157,7 +157,7 @@ static inline int vlc_poll (struct pollfd *fds, unsigned nfds, int timeout)
}
# define poll(u,n,t) vlc_poll(u, n, t)
-#elif defined (__ANDROID__) /* pthreads subset without pthread_cancel() */
+#elif defined (__ANDROID__) || defined(__native_client__) /* pthreads subset without pthread_cancel() */
# include <unistd.h>
# include <pthread.h>
# include <poll.h>
diff --git a/src/Makefile.am b/src/Makefile.am
index 43e0eac863..7666ec4718 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -386,6 +386,7 @@ if HAVE_NACL
libvlccore_la_SOURCES += \
android/error.c \
nacl/plugin.c \
+ nacl/thread.c \
posix/dirs.c \
posix/filesystem.c \
posix/netconf.c \
diff --git a/src/nacl/thread.c b/src/nacl/thread.c
new file mode 100644
index 0000000000..53c067f161
--- /dev/null
+++ b/src/nacl/thread.c
@@ -0,0 +1,510 @@
+/*****************************************************************************
+ * thread.c : nacl pthread back-end for LibVLC
+ *****************************************************************************
+ * Copyright (C) 1999-2017 VLC authors and VideoLAN
+ *
+ * Authors: Dennis Hamester <dhamester at jusst.de>
+ * Jean-Marc Dressler <polux at via.ecp.fr>
+ * Samuel Hocevar <sam at zoy.org>
+ * Gildas Bazin <gbazin at netcourrier.com>
+ * Clément Sténac
+ * Rémi Denis-Courmont
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
+ *****************************************************************************/
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include <vlc_common.h>
+#include <vlc_atomic.h>
+
+#include "libvlc.h"
+#include <signal.h>
+#include <errno.h>
+#include <time.h>
+#include <assert.h>
+
+#include <sys/types.h>
+#include <unistd.h> /* fsync() */
+#include <pthread.h>
+#include <sched.h>
+
+/* mutexes */
+void vlc_mutex_init(vlc_mutex_t *p_mutex)
+{
+ pthread_mutexattr_t attr;
+
+ pthread_mutexattr_init(&attr);
+#ifdef NDEBUG
+ pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_DEFAULT);
+#else
+ pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_ERRORCHECK);
+#endif
+ pthread_mutex_init(p_mutex, &attr);
+ pthread_mutexattr_destroy(&attr);
+}
+
+void vlc_mutex_init_recursive(vlc_mutex_t *p_mutex)
+{
+ pthread_mutexattr_t attr;
+
+ pthread_mutexattr_init(&attr);
+ pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE);
+ pthread_mutex_init(p_mutex, &attr);
+ pthread_mutexattr_destroy(&attr);
+}
+
+void vlc_mutex_destroy(vlc_mutex_t *p_mutex)
+{
+ pthread_mutex_destroy(p_mutex);
+}
+
+void vlc_mutex_lock(vlc_mutex_t *p_mutex)
+{
+ pthread_mutex_lock(p_mutex);
+}
+
+int vlc_mutex_trylock(vlc_mutex_t *p_mutex)
+{
+ return pthread_mutex_trylock(p_mutex);
+}
+
+void vlc_mutex_unlock(vlc_mutex_t *p_mutex)
+{
+ pthread_mutex_unlock(p_mutex);
+}
+
+struct vlc_thread
+{
+ pthread_t thread;
+ vlc_sem_t finished;
+
+ void *(*entry)(void*);
+ void *data;
+
+ struct
+ {
+ void *addr; /// Non-null if waiting on futex
+ vlc_mutex_t lock ; /// Protects futex address
+ } wait;
+
+ atomic_bool killed;
+ bool killable;
+};
+
+static __thread struct vlc_thread *thread = NULL;
+
+vlc_thread_t vlc_thread_self(void)
+{
+ return thread;
+}
+
+void vlc_threads_setup(libvlc_int_t *p_libvlc)
+{
+ (void)p_libvlc;
+}
+
+/* pthread */
+static void clean_detached_thread(void *data)
+{
+ struct vlc_thread *th = data;
+
+ /* release thread handle */
+ vlc_mutex_destroy(&th->wait.lock);
+ free(th);
+}
+
+static void *detached_thread(void *data)
+{
+ vlc_thread_t th = data;
+
+ thread = th;
+
+ vlc_cleanup_push(clean_detached_thread, th);
+ th->entry(th->data);
+ vlc_cleanup_pop();
+ clean_detached_thread(th);
+ return NULL;
+}
+
+static void finish_joinable_thread(void *data)
+{
+ vlc_thread_t th = data;
+
+ vlc_sem_post(&th->finished);
+}
+
+static void *joinable_thread(void *data)
+{
+ vlc_thread_t th = data;
+ void *ret;
+
+ vlc_cleanup_push(finish_joinable_thread, th);
+ thread = th;
+ ret = th->entry(th->data);
+ vlc_cleanup_pop();
+ vlc_sem_post(&th->finished);
+
+ return ret;
+}
+
+static int vlc_clone_attr(vlc_thread_t *th, void *(*entry) (void *),
+ void *data, bool detach)
+{
+ vlc_thread_t thread = malloc (sizeof (*thread));
+ if (unlikely(thread == NULL))
+ return ENOMEM;
+
+ int ret;
+
+ sigset_t oldset;
+ {
+ sigset_t set;
+ sigemptyset(&set);
+ sigdelset(&set, SIGHUP);
+ sigaddset(&set, SIGINT);
+ sigaddset(&set, SIGQUIT);
+ sigaddset(&set, SIGTERM);
+
+ sigaddset(&set, SIGPIPE); /* We don't want this one, really! */
+ pthread_sigmask(SIG_BLOCK, &set, &oldset);
+ }
+
+ if (!detach)
+ vlc_sem_init(&thread->finished, 0);
+ atomic_store(&thread->killed, false);
+ thread->killable = true;
+ thread->entry = entry;
+ thread->data = data;
+ thread->wait.addr = NULL;
+ vlc_mutex_init(&thread->wait.lock);
+
+ pthread_attr_t attr;
+ pthread_attr_init(&attr);
+ pthread_attr_setdetachstate(&attr, detach ? PTHREAD_CREATE_DETACHED
+ : PTHREAD_CREATE_JOINABLE);
+
+ ret = pthread_create(&thread->thread, &attr,
+ detach ? detached_thread : joinable_thread, thread);
+ pthread_attr_destroy(&attr);
+
+ pthread_sigmask(SIG_SETMASK, &oldset, NULL);
+ *th = thread;
+ return ret;
+}
+
+int vlc_clone(vlc_thread_t *th, void *(*entry) (void *), void *data,
+ int priority)
+{
+ VLC_UNUSED(priority);
+ return vlc_clone_attr(th, entry, data, false);
+}
+
+void vlc_join(vlc_thread_t handle, void **result)
+{
+ vlc_sem_wait(&handle->finished);
+ vlc_sem_destroy(&handle->finished);
+
+ pthread_join(handle->thread, result);
+ clean_detached_thread(handle);
+}
+
+int vlc_clone_detach(vlc_thread_t *th, void *(*entry) (void *), void *data,
+ int priority)
+{
+ VLC_UNUSED(priority);
+
+ vlc_thread_t dummy;
+ if (th == NULL)
+ th = &dummy;
+
+ return vlc_clone_attr(th, entry, data, true);
+}
+
+int vlc_set_priority(vlc_thread_t th, int priority)
+{
+ VLC_UNUSED(th);
+ VLC_UNUSED(priority);
+ return VLC_SUCCESS;
+}
+
+void vlc_cancel(vlc_thread_t thread_id)
+{
+ atomic_int *addr;
+
+ atomic_store(&thread_id->killed, true);
+
+ vlc_mutex_lock(&thread_id->wait.lock);
+ addr = thread_id->wait.addr;
+ if (addr != NULL)
+ {
+ atomic_fetch_or_explicit(addr, 1, memory_order_relaxed);
+ vlc_addr_broadcast(addr);
+ }
+ vlc_mutex_unlock(&thread_id->wait.lock);
+}
+
+int vlc_savecancel(void)
+{
+ if (!thread) /* not created by VLC, can't be cancelled */
+ return true;
+
+ int oldstate = thread->killable;
+ thread->killable = false;
+ return oldstate;
+}
+
+void vlc_restorecancel(int state)
+{
+ if (!thread) /* not created by VLC, can't be cancelled */
+ return;
+
+ thread->killable = state;
+}
+
+void vlc_testcancel(void)
+{
+ if (!thread) /* not created by VLC, can't be cancelled */
+ return;
+ if (!thread->killable)
+ return;
+ if (!atomic_load(&thread->killed))
+ return;
+
+ pthread_exit(NULL);
+}
+
+void vlc_control_cancel(int cmd, ...)
+{
+ vlc_thread_t th = vlc_thread_self();
+ va_list ap;
+
+ if (th == NULL)
+ return;
+
+ va_start(ap, cmd);
+ switch (cmd)
+ {
+ case VLC_CANCEL_ADDR_SET:
+ {
+ void *addr = va_arg(ap, void *);
+
+ vlc_mutex_lock(&th->wait.lock);
+ assert(th->wait.addr == NULL);
+ th->wait.addr = addr;
+ vlc_mutex_unlock(&th->wait.lock);
+ break;
+ }
+
+ case VLC_CANCEL_ADDR_CLEAR:
+ {
+ void *addr = va_arg(ap, void *);
+
+ vlc_mutex_lock(&th->wait.lock);
+ assert(th->wait.addr == addr);
+ th->wait.addr = NULL;
+ (void) addr;
+ vlc_mutex_unlock(&th->wait.lock);
+ break;
+ }
+
+ default:
+ vlc_assert_unreachable();
+ }
+ va_end(ap);
+}
+
+/* threadvar */
+
+int vlc_threadvar_create(vlc_threadvar_t *key, void (*destr) (void *))
+{
+ return pthread_key_create(key, destr);
+}
+
+void vlc_threadvar_delete(vlc_threadvar_t *p_tls)
+{
+ pthread_key_delete(*p_tls);
+}
+
+int vlc_threadvar_set(vlc_threadvar_t key, void *value)
+{
+ return pthread_setspecific(key, value);
+}
+
+void *vlc_threadvar_get(vlc_threadvar_t key)
+{
+ return pthread_getspecific(key);
+}
+
+/* time */
+mtime_t mdate(void)
+{
+ struct timespec ts;
+
+ if (unlikely(clock_gettime(CLOCK_MONOTONIC, &ts) != 0))
+ abort();
+
+ return (INT64_C(1000000) * ts.tv_sec) + (ts.tv_nsec / 1000);
+}
+
+/* cpu */
+
+unsigned vlc_GetCPUCount(void)
+{
+ /* _SC_NPROCESSORS_CONF is not available on NaCl */
+ return sysconf(_SC_NPROCESSORS_ONLN);
+}
+
+unsigned long vlc_thread_id(void)
+{
+ return (unsigned long)(void *)pthread_self();
+}
+
+typedef struct {
+ void *next;
+ void *addr;
+ int ref_count;
+ pthread_cond_t cv;
+} futex_t;
+
+static futex_t *futex_list = NULL;
+static pthread_mutex_t futex_list_mtx = PTHREAD_MUTEX_INITIALIZER;
+
+static futex_t* get_futex(void *addr, bool create) {
+ futex_t *empty = NULL;
+ futex_t *prev = NULL;
+ futex_t *futex = futex_list;
+ while (futex && (futex->addr != addr)) {
+ if (!futex->addr)
+ empty = futex;
+
+ prev = futex;
+ futex = futex->next;
+ }
+
+ if (!futex && create) {
+ if(empty) {
+ empty->addr = addr;
+ futex = empty;
+ }
+ else {
+ futex_t **last;
+ if (prev)
+ last = (futex_t **)&prev->next;
+ else
+ last = &futex_list;
+
+ *last = futex = malloc(sizeof(futex_t));
+ futex->next = NULL;
+ futex->addr = addr;
+ futex->ref_count = 0;
+ pthread_cond_init(&futex->cv, NULL);
+ }
+ }
+
+ if (futex)
+ ++futex->ref_count;
+
+ return futex;
+}
+
+static void release_futex(futex_t *futex) {
+ --futex->ref_count;
+ if (!futex->ref_count)
+ futex->addr = NULL;
+}
+
+void vlc_addr_signal(void *addr)
+{
+ if(pthread_mutex_lock(&futex_list_mtx))
+ return;
+
+ futex_t *futex = get_futex(addr, false);
+ if (futex) {
+ pthread_cond_signal(&futex->cv);
+ release_futex(futex);
+ }
+
+ pthread_mutex_unlock(&futex_list_mtx);
+}
+
+void vlc_addr_broadcast(void *addr)
+{
+ if(pthread_mutex_lock(&futex_list_mtx))
+ return;
+
+ futex_t *futex = get_futex(addr, false);
+ if (futex) {
+ pthread_cond_broadcast(&futex->cv);
+ release_futex(futex);
+ }
+
+ pthread_mutex_unlock(&futex_list_mtx);
+}
+
+void vlc_addr_wait(void *addr, unsigned val)
+{
+ volatile unsigned *ptr = (volatile unsigned *)addr;
+ if (*ptr != val)
+ return;
+
+ if(pthread_mutex_lock(&futex_list_mtx))
+ return;
+
+ futex_t *futex = get_futex(addr, true);
+ while (*ptr == val) {
+ if (pthread_cond_wait(&futex->cv, &futex_list_mtx))
+ break;
+ }
+
+ release_futex(futex);
+ pthread_mutex_unlock(&futex_list_mtx);
+}
+
+bool vlc_addr_timedwait(void *addr, unsigned val, mtime_t delay)
+{
+ mtime_t end = mdate() + delay;
+
+ volatile unsigned *ptr = (volatile unsigned *)addr;
+ if (*ptr != val)
+ return true;
+
+ if(pthread_mutex_lock(&futex_list_mtx))
+ return true;
+
+ bool timeout = false;
+ futex_t *futex = get_futex(addr, true);
+ while (*ptr == val) {
+ mtime_t now = mdate();
+ if (now >= end) {
+ timeout = true;
+ break;
+ }
+
+ delay = end - now;
+ struct timespec ts;
+ ts.tv_sec = delay / 1000000;
+ ts.tv_nsec = 1000 * (delay - 1000000 * ts.tv_sec);
+
+ if(pthread_cond_timedwait(&futex->cv, &futex_list_mtx, &ts))
+ break;
+ }
+
+ release_futex(futex);
+ pthread_mutex_unlock(&futex_list_mtx);
+ return !timeout;
+}
--
2.12.0
More information about the vlc-devel
mailing list