[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