[vlc-devel] [PATCH 11/15] nacl: Add custom thread implementation

Dennis Hamester dhamester at jusst.de
Wed Mar 8 17:09:09 CET 2017


On 08.03.2017 16:55, Hugo Beauzée-Luyssen wrote:
> 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,
> 

You're right, the pthreads part is mostly a verbatim copy of
android/thread.c. I didn't want to clutter that file with #ifdef's for a
platform that has nothing to do with Android.

But nacl/threads.c also implements all vlc_addr_* functions, because
NaCl does not have futexes.

Regards,
Dennis

-- 
Dennis Hamester
Software Development
-----------------------------------
jusst technologies GmbH

tel: +49 (0)40 1800 86 75
fax: +49 (0)40 1800 86 76
mail: dhamester at jusst.de

Ohlstedter Straße 12
22397 Hamburg
Deutschland

www.jusst.de

Vertretungsberechtigte Geschäftsführer: Julian Scheel, Wolfgang Scheel
Registergericht: Amtsgericht Hamburg
Registernummer: HRB 94300
USt-ID-Nr.: DE 243309917

-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 862 bytes
Desc: OpenPGP digital signature
URL: <http://mailman.videolan.org/pipermail/vlc-devel/attachments/20170308/d3c3dd87/attachment.sig>


More information about the vlc-devel mailing list