[vlc-devel] [PATCH 11/15] nacl: Add custom thread implementation
Hugo Beauzée-Luyssen
hugo at beauzee.fr
Wed Mar 8 16:55:12 CET 2017
On Wed, Mar 8, 2017, at 03:55 PM, Julian Scheel wrote:
> 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
>
> _______________________________________________
> vlc-devel mailing list
> To unsubscribe or modify your subscription options:
> https://mailman.videolan.org/listinfo/vlc-devel
Hi,
This seems to be mostly whitespaces and removing assertions compared to
android/thread.c
Is there any other difference?
Regards,
--
Hugo Beauzée-Luyssen
hugo at beauzee.fr
More information about the vlc-devel
mailing list