[vlc-devel] [vlc-commits] thread: add generic futex-based muteces

Jean-Baptiste Kempf jb at videolan.org
Sun Feb 23 15:54:15 CET 2020


This commit fails to build on Android:
https://code.videolan.org/videolan/vlc/-/jobs/356997

Best,

On Fri, Feb 21, 2020, at 18:26, Rémi Denis-Courmont wrote:
> vlc | branch: master | Rémi Denis-Courmont <remi at remlab.net> | Wed Feb 
> 19 21:09:06 2020 +0200| [0a8a53335b5770cd1acaa612b495bdbc41a8d1ee] | 
> committer: Rémi Denis-Courmont
> 
> thread: add generic futex-based muteces
> 
> This provides a common implementation of fast (non-debug),
> error-checking (debug) and recursive muteces on top of
> vlc_atomic_wait() and vlc_atomic_notify_one(), using Drepper's tristate
> mutex algorithm.
> 
> Benefits of this implementation include:
>  - Error checking is supported on all platforms (including Windows).
>  - Initialization can never fail, is not subject to aborts.
>  - Destruction is a no-op and can be removed altogether.
>  - Static muteces do not leak or need kludges.
>  - Ownership can be checked directly without the mutex mark system.
>  - Non-ownership can be checked in assertion.
> 
> Because the player code uses the same vlc_mutex_t typedef for both
> non-recursive and recursive usages, disentanglement is not possible.
> This patchset thus supports both semantics together as before.
> 
> > http://git.videolan.org/gitweb.cgi/vlc.git/?a=commit;h=0a8a53335b5770cd1acaa612b495bdbc41a8d1ee
> ---
> 
>  include/vlc_threads.h |  55 ++++++++++++++++------
>  src/misc/threads.c    | 124 ++++++++++++++++++++++++++++++++++++++++++++++++++
>  2 files changed, 166 insertions(+), 13 deletions(-)
> 
> diff --git a/include/vlc_threads.h b/include/vlc_threads.h
> index 836f042891..598e6fb203 100644
> --- a/include/vlc_threads.h
> +++ b/include/vlc_threads.h
> @@ -77,6 +77,7 @@ typedef struct
>      };
>  } vlc_mutex_t;
>  #define VLC_STATIC_MUTEX { false, { { false, 0 } } }
> +#define LIBVLC_DONT_WANT_MUTEX
>  #define LIBVLC_NEED_RWLOCK
>  typedef INIT_ONCE vlc_once_t;
>  #define VLC_STATIC_ONCE INIT_ONCE_STATIC_INIT
> @@ -125,6 +126,7 @@ typedef struct
>      };
>  } vlc_mutex_t;
>  #define VLC_STATIC_MUTEX { false, { { false, 0 } } }
> +#define LIBVLC_DONT_WANT_MUTEX
>  #define LIBVLC_NEED_RWLOCK
>  typedef struct
>  {
> @@ -180,6 +182,7 @@ typedef pthread_t vlc_osthread_t;
>  #define vlc_thread_equal(a,b) pthread_equal(a,b)
>  typedef pthread_mutex_t vlc_mutex_t;
>  #define VLC_STATIC_MUTEX PTHREAD_MUTEX_INITIALIZER
> +#define LIBVLC_DONT_WANT_MUTEX
>  typedef pthread_once_t  vlc_once_t;
>  #define VLC_STATIC_ONCE   PTHREAD_ONCE_INIT
>  typedef pthread_key_t   vlc_threadvar_t;
> @@ -227,6 +230,7 @@ typedef pthread_t       vlc_osthread_t;
>  #define vlc_thread_equal(a,b) pthread_equal(a,b)
>  typedef pthread_mutex_t vlc_mutex_t;
>  #define VLC_STATIC_MUTEX PTHREAD_MUTEX_INITIALIZER
> +#define LIBVLC_DONT_WANT_MUTEX
>  typedef pthread_rwlock_t vlc_rwlock_t;
>  #define VLC_STATIC_RWLOCK PTHREAD_RWLOCK_INITIALIZER
>  typedef pthread_once_t  vlc_once_t;
> @@ -271,21 +275,9 @@ typedef struct
>  typedef pthread_t vlc_osthread_t;
>  #define vlc_thread_equal(a,b) pthread_equal(a,b)
>  
> -/**
> - * Mutex.
> - *
> - * Storage space for a mutual exclusion lock.
> - *
> - * \ingroup mutex
> - */
>  typedef pthread_mutex_t vlc_mutex_t;
> -
> -/**
> - * Static initializer for (static) mutex.
> - *
> - * \ingroup mutex
> - */
>  #define VLC_STATIC_MUTEX PTHREAD_MUTEX_INITIALIZER
> +#define LIBVLC_DONT_WANT_MUTEX
>  
>  /**
>   * Read/write lock.
> @@ -343,6 +335,43 @@ typedef struct vlc_timer *vlc_timer_t;
>   * \defgroup mutex Mutual exclusion locks
>   * @{
>   */
> +#ifndef LIBVLC_DONT_WANT_MUTEX
> +/**
> + * Mutex.
> + *
> + * Storage space for a mutual exclusion lock.
> + */
> +typedef struct
> +{
> +    union {
> +#ifndef __cplusplus
> +        struct {
> +            atomic_uint value;
> +            atomic_uint recursion;
> +            const void *_Atomic owner;
> +        };
> +#endif
> +        struct {
> +            unsigned int value;
> +            unsigned int recursion;
> +            const void *owner;
> +        } dummy;
> +    };
> +} vlc_mutex_t;
> +
> +/**
> + * Static initializer for (static) mutex.
> + *
> + * \note This only works in C code.
> + * In C++, consider using a global \ref vlc::threads::mutex instance instead.
> + */
> +#define VLC_STATIC_MUTEX { \
> +    .value = ATOMIC_VAR_INIT(0), \
> +    .recursion = ATOMIC_VAR_INIT(0), \
> +    .owner = ATOMIC_VAR_INIT(NULL), \
> +}
> +#endif
> +
>  /**
>   * Initializes a fast mutex.
>   *
> diff --git a/src/misc/threads.c b/src/misc/threads.c
> index bdb3c9d687..357687407d 100644
> --- a/src/misc/threads.c
> +++ b/src/misc/threads.c
> @@ -201,6 +201,130 @@ void (vlc_tick_sleep)(vlc_tick_t delay)
>  }
>  #endif
>  
> +#ifndef LIBVLC_DONT_WANT_MUTEX
> +static void vlc_mutex_init_common(vlc_mutex_t *mtx, bool recursive)
> +{
> +    atomic_init(&mtx->value, 0);
> +    atomic_init(&mtx->recursion, recursive);
> +    atomic_init(&mtx->owner, NULL);
> +}
> +
> +void vlc_mutex_init(vlc_mutex_t *mtx)
> +{
> +    vlc_mutex_init_common(mtx, false);
> +}
> +
> +void vlc_mutex_init_recursive(vlc_mutex_t *mtx)
> +{
> +    vlc_mutex_init_common(mtx, true);
> +}
> +
> +void vlc_mutex_destroy(vlc_mutex_t *mtx)
> +{
> +    assert(atomic_load_explicit(&mtx->value, memory_order_relaxed) == 0);
> +    assert(atomic_load_explicit(&mtx->recursion, memory_order_relaxed) <= 1);
> +    assert(atomic_load_explicit(&mtx->owner, memory_order_relaxed) == NULL);
> +    (void) mtx;
> +}
> +
> +static _Thread_local char thread_self[1];
> +#define THREAD_SELF ((const void *)thread_self)
> +
> +static bool vlc_mutex_held(const vlc_mutex_t *mtx)
> +{
> +    /* This comparison is thread-safe:
> +     * Even though other threads may modify the owner field at any time,
> +     * they will never make ti compare equal to the calling thread.
> +     */
> +    return THREAD_SELF == atomic_load_explicit(&mtx->owner,
> +                                               memory_order_relaxed);
> +}
> +
> +void vlc_mutex_lock(vlc_mutex_t *mtx)
> +{
> +    unsigned value;
> +
> +    /* This is the Drepper (non-recursive) mutex algorithm
> +     * from his "Futexes are tricky" paper. The mutex can value be:
> +     * - 0: the mutex is free
> +     * - 1: the mutex is locked and uncontended
> +     * - 2: the mutex is contended (i.e., unlock needs to wake up a waiter)
> +     */
> +    if (vlc_mutex_trylock(mtx) == 0)
> +        return;
> +
> +    int canc = vlc_savecancel(); /* locking is never a cancellation point */
> +
> +    while ((value = atomic_exchange_explicit(&mtx->value, 2,
> +                                             memory_order_acquire)) != 0)
> +        vlc_atomic_wait(&mtx->value, 2);
> +
> +    vlc_restorecancel(canc);
> +    atomic_store_explicit(&mtx->owner, THREAD_SELF, memory_order_relaxed);
> +    vlc_mutex_mark(mtx);
> +}
> +
> +int vlc_mutex_trylock(vlc_mutex_t *mtx)
> +{
> +    /* Check the recursion counter:
> +     * - 0: mutex is not recursive.
> +     * - 1: mutex is recursive but free or locked non-recursively.
> +     * - n > 1: mutex is recursive and locked n time(s).
> +     */
> +    unsigned recursion = atomic_load_explicit(&mtx->recursion,
> +                                              memory_order_relaxed);
> +    if (unlikely(recursion) && vlc_mutex_held(mtx)) {
> +        /* This thread already owns the mutex, locks recursively.
> +         * Other threads shall not have modified the recursion or owner fields.
> +         */
> +        atomic_store_explicit(&mtx->recursion, recursion + 1,
> +                              memory_order_relaxed);
> +        vlc_mutex_mark(mtx);
> +        return 0;
> +    } else
> +        assert(!vlc_mutex_held(mtx));
> +
> +    unsigned value = 0;
> +
> +    if (atomic_compare_exchange_strong_explicit(&mtx->value, &value, 1,
> +                                                memory_order_acquire,
> +                                                memory_order_relaxed)) {
> +        atomic_store_explicit(&mtx->owner, THREAD_SELF, memory_order_relaxed);
> +        vlc_mutex_mark(mtx);
> +        return 0;
> +    }
> +
> +    return EBUSY;
> +}
> +
> +void vlc_mutex_unlock(vlc_mutex_t *mtx)
> +{
> +    assert(vlc_mutex_held(mtx));
> +
> +    unsigned recursion = atomic_load_explicit(&mtx->recursion,
> +                                              memory_order_relaxed);
> +    if (unlikely(recursion > 1)) {
> +        /* Non-last recursive unlocking. */
> +        atomic_store_explicit(&mtx->recursion, recursion - 1,
> +                              memory_order_relaxed);
> +        vlc_mutex_unmark(mtx);
> +        return;
> +    }
> +
> +    atomic_store_explicit(&mtx->owner, NULL, memory_order_relaxed);
> +    vlc_mutex_unmark(mtx);
> +
> +    switch (atomic_exchange_explicit(&mtx->value, 0, memory_order_release)) {
> +        case 2:
> +            vlc_atomic_notify_one(&mtx->value);
> +        case 1:
> +            break;
> +        default:
> +            vlc_assert_unreachable();
> +    }
> +}
> +#endif
> +
>  void vlc_cond_init(vlc_cond_t *cond)
>  {
>      cond->head = NULL;
> 
> _______________________________________________
> vlc-commits mailing list
> vlc-commits at videolan.org
> https://mailman.videolan.org/listinfo/vlc-commits
>

-- 
Jean-Baptiste Kempf -  President
+33 672 704 734


More information about the vlc-devel mailing list