[vlc-devel] [PATCH] android: threads support

Rémi Denis-Courmont remi at remlab.net
Mon Oct 8 10:42:57 CEST 2012


On Sun,  7 Oct 2012 15:49:53 +0200, Rafaël Carré <funman at videolan.org>
wrote:
>  #if defined (LIBVLC_USE_PTHREAD)
> +# ifdef LIBVLC_USE_PTHREAD_CANCEL
>  typedef pthread_t       vlc_thread_t;
> +# else
> +typedef struct vlc_thread *vlc_thread_t;
> +# endif
>  typedef pthread_mutex_t vlc_mutex_t;
>  #define VLC_STATIC_MUTEX PTHREAD_MUTEX_INITIALIZER
>  typedef pthread_cond_t  vlc_cond_t;
>  #define VLC_STATIC_COND  PTHREAD_COND_INITIALIZER
> +
> +# ifndef LIBVLC_NEED_RWLOCK
>  typedef pthread_rwlock_t vlc_rwlock_t;
> -#define VLC_STATIC_RWLOCK PTHREAD_RWLOCK_INITIALIZER
> +#  define VLC_STATIC_RWLOCK PTHREAD_RWLOCK_INITIALIZER
> +# endif
> +
>  typedef pthread_key_t   vlc_threadvar_t;
>  typedef struct vlc_timer *vlc_timer_t;
>  
>  #if defined (__APPLE__)
>  typedef semaphore_t     vlc_sem_t;
> -#else
> +#elif !defined (LIBVLC_NEED_SEMAPHORE)
>  typedef sem_t           vlc_sem_t;
>  #endif

I still don't like this big pile of ifdefs. pthread is pthread is pthread.

And it would get worse with monotonic clock and proper condition
variables.

> @@ -332,7 +350,7 @@ VLC_API int vlc_savecancel(void);
>  VLC_API void vlc_restorecancel(int state);
>  VLC_API void vlc_testcancel(void);
>  
> -#if defined (LIBVLC_USE_PTHREAD_CANCEL)
> +#if defined (LIBVLC_USE_PTHREAD)
>  /**
>   * Registers a new procedure to run if the thread is cancelled (or
>   otherwise
>   * exits prematurely). Any call to vlc_cleanup_push() <b>must</b>
paired
>   with a
> @@ -383,7 +401,9 @@ struct vlc_cleanup_t
>          vlc_control_cancel (VLC_CLEANUP_POP); \
>          vlc_cleanup_data.proc (vlc_cleanup_data.data); \
>      } while (0)
> +#endif /* LIBVLC_USE_PTHREAD_CANCEL */
>  
> +#if !defined (LIBVLC_USE_PTHREAD_CANCEL)
>  /* poll() with cancellation */
>  static inline int vlc_poll (struct pollfd *fds, unsigned nfds, int
>  timeout)
>  {
> diff --git a/src/Makefile.am b/src/Makefile.am
> index 9d1d87d..ab8c46c 100644
> --- a/src/Makefile.am
> +++ b/src/Makefile.am
> @@ -259,9 +259,9 @@ SOURCES_libvlc_darwin = \
>  
>  SOURCES_libvlc_android = \
>  	android/dirs.c \
> +	android/thread.c \
>  	posix/filesystem.c \
>  	posix/plugin.c \
> -	posix/thread.c \
>  	posix/timer.c \
>  	posix/linux_cpu.c \
>  	posix/linux_specific.c \
> diff --git a/src/android/thread.c b/src/android/thread.c
> new file mode 100644
> index 0000000..3737751
> --- /dev/null
> +++ b/src/android/thread.c
> @@ -0,0 +1,494 @@
>
+/*****************************************************************************
> + * thread.c : android pthread back-end for LibVLC
> +
>
*****************************************************************************
> + * Copyright (C) 1999-2012 VLC authors and VideoLAN
> + *
> + * Authors: 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 <sys/types.h>
> +#include <unistd.h> /* fsync() */
> +#include <pthread.h>
> +#include <sched.h>
> +
> +#include <android/log.h>
> +#include <sys/syscall.h> /* __NR_gettid */
> +
> +/* FIXME: Android has a monotonic clock
> + * XXX : how to use it with pthread_cond_wait() ? */
> +# warning Monotonic clock not available. Expect timing issues.
> +
> +/* helper */
> +static struct timespec mtime_to_ts (mtime_t date)
> +{
> +    lldiv_t d = lldiv (date, CLOCK_FREQ);
> +    struct timespec ts = { d.quot, d.rem * (1000000000 / CLOCK_FREQ) };
> +
> +    return ts;
> +}
> +
> +/* debug */
> +#define vlc_assert(x) do { \
> +    if (unlikely(!x)) { \
> +    __android_log_print(ANDROID_LOG_ERROR, "vlc", "assert failed %s:%d:
> %s", \
> +        __FILE__, __LINE__, #x \
> +        ); \
> +        abort(); \
> +    } \
> +} while(0)
> +
> +#ifndef NDEBUG
> +static void
> +vlc_thread_fatal (const char *action, int error,
> +                  const char *function, const char *file, unsigned
line)
> +{
> +    char buf[1000];
> +    const char *msg;
> +
> +    switch (strerror_r (error, buf, sizeof (buf)))
> +    {
> +        case 0:
> +            msg = buf;
> +            break;
> +        case ERANGE: /* should never happen */
> +            msg = "unknown (too big to display)";
> +            break;
> +        default:
> +            msg = "unknown (invalid error number)";
> +            break;
> +    }
> +
> +    __android_log_print(ANDROID_LOG_ERROR, "vlc",
> +        "LibVLC fatal error %s (%d) in thread %d "
> +        "at %s:%u in %s\n Error message: %s\n",
> +        action, error, syscall (__NR_gettid), file, line, function,
msg);
> +
> +    abort ();
> +}
> +
> +# define VLC_THREAD_ASSERT( action ) \
> +    if (unlikely(val)) \
> +        vlc_thread_fatal (action, val, __func__, __FILE__, __LINE__)
> +#else
> +# define VLC_THREAD_ASSERT( action ) ((void)val)
> +#endif
> +
> +/* 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)
> +{
> +    int val = pthread_mutex_destroy( p_mutex );
> +    VLC_THREAD_ASSERT ("destroying mutex");
> +}
> +
> +#ifndef NDEBUG
> +void vlc_assert_locked (vlc_mutex_t *p_mutex)
> +{
> +    vlc_assert (pthread_mutex_lock (p_mutex) == EDEADLK);
> +}
> +#endif
> +
> +void vlc_mutex_lock (vlc_mutex_t *p_mutex)
> +{
> +    int val = pthread_mutex_lock( p_mutex );
> +    VLC_THREAD_ASSERT ("locking mutex");
> +}
> +
> +int vlc_mutex_trylock (vlc_mutex_t *p_mutex)
> +{
> +    int val = pthread_mutex_trylock( p_mutex );
> +
> +    if (val != EBUSY)
> +        VLC_THREAD_ASSERT ("locking mutex");
> +    return val;
> +}
> +
> +void vlc_mutex_unlock (vlc_mutex_t *p_mutex)
> +{
> +    int val = pthread_mutex_unlock( p_mutex );
> +    VLC_THREAD_ASSERT ("unlocking mutex");
> +}
> +
> +struct vlc_thread
> +{
> +    pthread_t      thread;
> +    pthread_cond_t *cond; /// Non-null if thread waiting on cond
> +    vlc_mutex_t    lock ; /// Protects cond
> +
> +    void *(*entry)(void*);
> +    void *data;
> +
> +    vlc_atomic_t killable;

OK but killable does not really need to be atomic, unlike killed and
finished.

> +    vlc_atomic_t killed;
> +    vlc_atomic_t finished;
> +    bool detached;
> +};
> +
> +static __thread struct vlc_thread *thread = NULL;
> +
> +void vlc_threads_setup (libvlc_int_t *p_libvlc)
> +{
> +    (void)p_libvlc;
> +}
> +
> +static void *andro_Thread(void *data)
> +{
> +    thread = data;
> +    void *ret = thread->entry(thread->data);
> +    if (thread->detached) {
> +        /* release thread handle */
> +        vlc_mutex_destroy(&thread->lock);
> +        free(thread);
> +    } else {
> +        vlc_atomic_set(&thread->finished, true);
> +        /* thread handle will be freed when vlc_join() is called */
> +    }
> +    return ret;
> +}
> +
> +/* cond */
> +
> +void vlc_cond_init (vlc_cond_t *p_condvar)
> +{
> +    if (unlikely(pthread_cond_init (p_condvar, NULL)))
> +        abort ();
> +}
> +
> +void vlc_cond_init_daytime (vlc_cond_t *p_condvar)
> +{
> +    vlc_cond_init(p_condvar);
> +}
> +
> +void vlc_cond_destroy (vlc_cond_t *p_condvar)
> +{
> +    int val = pthread_cond_destroy( p_condvar );
> +    VLC_THREAD_ASSERT ("destroying condition");
> +}
> +
> +void vlc_cond_signal (vlc_cond_t *p_condvar)
> +{
> +    int val = pthread_cond_signal( p_condvar );
> +    VLC_THREAD_ASSERT ("signaling condition variable");
> +}
> +
> +void vlc_cond_broadcast (vlc_cond_t *p_condvar)
> +{
> +    pthread_cond_broadcast (p_condvar);
> +}
> +
> +void vlc_cond_wait (vlc_cond_t *p_condvar, vlc_mutex_t *p_mutex)
> +{
> +    if (thread) {
> +        vlc_testcancel();
> +        vlc_mutex_lock(&thread->lock);
> +        thread->cond = p_condvar;
> +        vlc_mutex_unlock(&thread->lock);
> +    }
> +
> +    int val = pthread_cond_wait( p_condvar, p_mutex );
> +
> +    if (thread) {
> +        vlc_mutex_lock(&thread->lock);
> +        thread->cond = NULL;
> +        vlc_mutex_unlock(&thread->lock);
> +        vlc_testcancel();
> +    }
> +
> +    VLC_THREAD_ASSERT ("waiting on condition");
> +}
> +
> +int vlc_cond_timedwait (vlc_cond_t *p_condvar, vlc_mutex_t *p_mutex,
> +                        mtime_t deadline)
> +{
> +    struct timespec ts = mtime_to_ts (deadline);
> +
> +    if (thread) {
> +        vlc_testcancel();
> +        vlc_mutex_lock(&thread->lock);
> +        thread->cond = p_condvar;
> +        vlc_mutex_unlock(&thread->lock);
> +    }
> +
> +    int val = pthread_cond_timedwait (p_condvar, p_mutex, &ts);
> +    if (val != ETIMEDOUT)
> +        VLC_THREAD_ASSERT ("timed-waiting on condition");
> +
> +    if (thread) {
> +        vlc_mutex_lock(&thread->lock);
> +        thread->cond = NULL;
> +        vlc_mutex_unlock(&thread->lock);
> +        vlc_testcancel();
> +    }
> +
> +    return val;
> +}
> +
> +/* pthread */
> +
> +static int vlc_clone_attr (vlc_thread_t *th, pthread_attr_t *attr,
> +                           void *(*entry) (void *), void *data, int
> priority)
> +{
> +    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);
> +    }
> +
> +    (void) priority;
> +
> +    vlc_thread_t thread = malloc (sizeof (*thread));
> +    if (unlikely(thread == NULL)) {
> +        if (attr)
> +            pthread_attr_destroy(attr);
> +        return ENOMEM;
> +    }
> +
> +    vlc_atomic_set(&thread->killable, true);
> +    vlc_atomic_set(&thread->killed, false);
> +    vlc_atomic_set(&thread->finished, false);
> +    int state = PTHREAD_CREATE_JOINABLE;
> +    if (attr)
> +        pthread_attr_getdetachstate(attr, &state);
> +    thread->detached = state == PTHREAD_CREATE_DETACHED;
> +    thread->cond = NULL;
> +    thread->entry = entry;
> +    thread->data = data;
> +    vlc_mutex_init(&thread->lock);
> +
> +    *th = thread;
> +    ret = pthread_create (&thread->thread, attr, andro_Thread, thread);
> +
> +    pthread_sigmask (SIG_SETMASK, &oldset, NULL);
> +    if (attr)
> +        pthread_attr_destroy (attr);
> +    return ret;
> +}
> +
> +int vlc_clone (vlc_thread_t *th, void *(*entry) (void *), void *data,
> +               int priority)
> +{
> +    pthread_attr_t attr;
> +
> +    pthread_attr_init (&attr);
> +    return vlc_clone_attr (th, &attr, entry, data, priority);
> +}
> +
> +void vlc_join (vlc_thread_t handle, void **result)
> +{
> +    do {
> +        vlc_testcancel();
> +        msleep(CLOCK_FREQ / 100);:
> +    } while (!vlc_atomic_get(&handle->finished));

OK but the fast path could trivially avoid sleeping:

    vlc_testcancel();
    while (!vlc_atomic_get(&handle->finished));
        msleep(CLOCK_FREQ / 100);

(msleep() tests for cancellation anyway.)

> +    int val = pthread_join (handle->thread, result);
> +    VLC_THREAD_ASSERT ("joining thread");
> +    vlc_mutex_destroy(&handle->lock);
> +    free(handle);
> +}
> +
> +int vlc_clone_detach (vlc_thread_t *th, void *(*entry) (void *), void
> *data,
> +                      int priority)
> +{
> +    vlc_thread_t dummy;
> +    pthread_attr_t attr;
> +
> +    if (th == NULL)
> +        th = &dummy;
> +
> +    pthread_attr_init (&attr);
> +    pthread_attr_setdetachstate (&attr, PTHREAD_CREATE_DETACHED);
> +    return vlc_clone_attr (th, &attr, entry, data, priority);
> +}
> +
> +int vlc_set_priority (vlc_thread_t th, int priority)
> +{
> +    (void) th; (void) priority;
> +    return VLC_SUCCESS;
> +}
> +
> +void vlc_cancel (vlc_thread_t thread_id)
> +{
> +    vlc_atomic_set(&thread_id->killed, true);
> +    if (!vlc_atomic_get(&thread_id->killable))
> +        return;
> +
> +    vlc_mutex_lock(&thread_id->lock);
> +    vlc_cond_t *cond = thread_id->cond;
> +
> +    if (cond)
> +        pthread_cond_broadcast(cond);
> +    vlc_mutex_unlock(&thread_id->lock);
> +}
> +
> +int vlc_savecancel (void)
> +{
> +    if (!thread) /* not created by VLC, can't be cancelled */
> +        return true;
> +
> +    int oldstate = vlc_atomic_get(&thread->killable);
> +    vlc_atomic_set(&thread->killable, false);
> +    return oldstate;
> +}
> +
> +void vlc_restorecancel (int state)
> +{
> +    if (!thread) /* not created by VLC, can't be cancelled */
> +        return;
> +/*
> +    int val = vlc_atomic_get(&thread->killable) != false;
> +    __android_log_print(ANDROID_LOG_ERROR, "vlc",
> +       "LibVLC fatal error %p , 0x%x", thread,
> vlc_atomic_get(&thread->killable));
> +    if (val)
> +        abort();
> +    VLC_THREAD_ASSERT("restoring cancellation while not disabled");
> +    */
> +    vlc_atomic_set(&thread->killable, state);
> +}
> +
> +void vlc_testcancel (void)
> +{
> +    if (!thread) /* not created by VLC, can't be cancelled */
> +        return;
> +    if (!vlc_atomic_get(&thread->killable))
> +        return;
> +    if (!vlc_atomic_get(&thread->killed))
> +        return;
> +
> +    vlc_atomic_set(&thread->finished, true);
> +    pthread_exit(NULL);
> +}
> +
> +/* 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_REALTIME, &ts) != 0))
> +        abort ();
> +
> +    return (INT64_C(1000000) * ts.tv_sec) + (ts.tv_nsec / 1000);
> +}
> +
> +#undef mwait
> +void mwait (mtime_t deadline)
> +{
> +    deadline -= mdate ();
> +    if (deadline > 0)
> +        msleep (deadline);
> +}
> +
> +#undef msleep
> +void msleep (mtime_t delay)
> +{
> +    struct timespec ts = mtime_to_ts (delay);
> +
> +    vlc_testcancel();
> +    for (;;) {
> +        struct timespec t = { 0, 10 * 1000 * 1000 };
> +        if (ts.tv_sec <= 0 && t.tv_nsec > ts.tv_nsec)
> +            t.tv_nsec = ts.tv_nsec;
> +        while (nanosleep (&t, &t) == -1) {
> +            vlc_testcancel();
> +            vlc_assert (errno == EINTR);
> +        }
> +
> +        ts.tv_nsec -= 10 * 1000 * 1000;
> +        if (ts.tv_nsec < 0) {
> +            if (--ts.tv_sec < 0)
> +                return;
> +            ts.tv_nsec += 1000 * 1000 * 1000;
> +        }
> +    }
> +}

OKish but fails to account for drift.

> +
> +/* cpu */
> +
> +unsigned vlc_GetCPUCount(void)
> +{
> +    return sysconf(_SC_NPROCESSORS_CONF);
> +}

-- 
Rémi Denis-Courmont
Sent from my collocated server



More information about the vlc-devel mailing list