[vlc-commits] [Git][videolan/vlc][master] 3 commits: threads: add the latch synchronisation object

Rémi Denis-Courmont (@Courmisch) gitlab at videolan.org
Mon May 30 07:23:14 UTC 2022



Rémi Denis-Courmont pushed to branch master at VideoLAN / VLC


Commits:
2d7478e1 by Rémi Denis-Courmont at 2022-05-30T07:09:00+00:00
threads: add the latch synchronisation object

The semantics and names mimick the C/C++2X latch. Latches are a
generalisation of barriers, whence counting down and waiting are
(or rather, can be) done separately.

Note that the C2X uses ptrdiff_t but with does not define negative
values. This uses size_t instead, not squandering the sign bit.

- - - - -
9586e34a by Rémi Denis-Courmont at 2022-05-30T07:09:00+00:00
test/thread: add test case for latch

- - - - -
6b9336b5 by Rémi Denis-Courmont at 2022-05-30T07:09:00+00:00
threads: optimise latch

If there are no waiters, skip the notification system call.

- - - - -


4 changed files:

- include/vlc_threads.h
- src/libvlccore.sym
- src/misc/threads.c
- src/test/thread.c


Changes:

=====================================
include/vlc_threads.h
=====================================
@@ -481,6 +481,86 @@ VLC_API int vlc_sem_timedwait(vlc_sem_t *sem, vlc_tick_t deadline) VLC_USED;
 /** @} */
 
 #ifndef __cplusplus
+/**
+ * \defgroup latch Latches
+ *
+ * The latch is a downward counter used to synchronise threads.
+ *
+ * @{
+ */
+/**
+ * Latch.
+ *
+ * Storage space for a thread-safe latch.
+ */
+typedef struct
+{
+    atomic_size_t value;
+    atomic_uint ready;
+} vlc_latch_t;
+
+/**
+ * Initializes a latch.
+ *
+ * @param value initial latch value (typically 1)
+ */
+VLC_API void vlc_latch_init(vlc_latch_t *, size_t value);
+
+/**
+ * Decrements the value of a latch.
+ *
+ * This function atomically decrements the value of a latch by the given
+ * quantity. If the result is zero, then any thread waiting on the latch is
+ * woken up.
+ *
+ * \warning If the result is (arithmetically) strictly negative, the behaviour
+ * is undefined.
+ *
+ * \param n quantity to subtract from the latch value (typically 1)
+ *
+ * \note This function is not a cancellation point.
+ */
+VLC_API void vlc_latch_count_down(vlc_latch_t *, size_t n);
+
+/**
+ * Decrements the value of a latch and waits on it.
+ *
+ * This function atomically decrements the value of a latch by the given
+ * quantity. Then, if the result of the subtraction is strictly positive,
+ * it waits until the value reaches zero.
+ *
+ * This function is equivalent to the succession of vlc_latch_count_down()
+ * then vlc_latch_wait(), and is only an optimisation to combine the two..
+ *
+ * \warning If the result is strictly negative, the behaviour is undefined.
+ *
+ * @param n number of times to decrement the value (typically 1)
+ *
+ * \note This function may be a cancellation point.
+ */
+VLC_API void vlc_latch_count_down_and_wait(vlc_latch_t *, size_t n);
+
+/**
+ * Checks if a latch is ready.
+ *
+ * This function compares the value of a latch with zero.
+ *
+ * \retval false if the latch value is non-zero
+ * \retval true if the latch value equals zero
+ */
+VLC_API bool vlc_latch_is_ready(const vlc_latch_t *latch) VLC_USED;
+
+/**
+ * Waits on a latch.
+ *
+ * This function waits until the value of the latch reaches zero.
+ *
+ * \note This function may be a point of cancellation.
+ */
+VLC_API void vlc_latch_wait(vlc_latch_t *);
+
+/** @} */
+
 /**
  * One-time initialization.
  *


=====================================
src/libvlccore.sym
=====================================
@@ -586,6 +586,12 @@ vlc_keystore_find
 vlc_keystore_remove
 vlc_keystore_store
 vlc_keystore_release_entries
+vlc_latch_init
+vlc_latch_is_ready
+vlc_latch_count_down
+vlc_latch_count_down_and_wait
+vlc_latch_is_ready
+vlc_latch_wait
 vlc_ml_instance_get
 vlc_ml_get
 vlc_ml_control


=====================================
src/misc/threads.c
=====================================
@@ -402,6 +402,67 @@ int vlc_sem_trywait(vlc_sem_t *sem)
     return 0;
 }
 
+enum { VLC_LATCH_READY, VLC_LATCH_CONTEND, VLC_LATCH_PENDING };
+
+void vlc_latch_init(vlc_latch_t *latch, size_t value)
+{
+    atomic_init(&latch->ready, value ? VLC_LATCH_PENDING : VLC_LATCH_READY);
+    atomic_init(&latch->value, value);
+}
+
+static bool vlc_latch_count_down_ready(vlc_latch_t *latch, size_t n)
+{
+    size_t value;
+
+    value = atomic_fetch_sub_explicit(&latch->value, n, memory_order_acq_rel);
+    assert(value >= n);
+
+    if (value != n)
+        return false;
+
+    if (atomic_exchange_explicit(&latch->ready, VLC_LATCH_READY,
+                                 memory_order_release) == VLC_LATCH_CONTEND)
+        vlc_atomic_notify_all(&latch->ready);
+
+    return true;
+}
+
+void vlc_latch_count_down(vlc_latch_t *latch, size_t n)
+{
+    (void) vlc_latch_count_down_ready(latch, n);
+}
+
+void vlc_latch_count_down_and_wait(vlc_latch_t *latch, size_t n)
+{
+    if (!vlc_latch_count_down_ready(latch, n))
+        vlc_latch_wait(latch);
+}
+
+bool vlc_latch_is_ready(const vlc_latch_t *latch)
+{
+    return atomic_load_explicit(&latch->ready,
+                                memory_order_acquire) == VLC_LATCH_READY;
+}
+
+void vlc_latch_wait(vlc_latch_t *latch)
+{
+    unsigned int expected = VLC_LATCH_PENDING;
+
+    if (!atomic_compare_exchange_strong_explicit(&latch->ready, &expected,
+                                                 VLC_LATCH_CONTEND,
+                                                 memory_order_acquire,
+                                                 memory_order_acquire)) {
+        if (expected == VLC_LATCH_READY)
+            return;
+
+        assert(expected == VLC_LATCH_CONTEND);
+    }
+
+    do
+        vlc_atomic_wait(&latch->ready, VLC_LATCH_CONTEND);
+    while (!vlc_latch_is_ready(latch));
+}
+
 enum { VLC_ONCE_UNDONE, VLC_ONCE_DOING, VLC_ONCE_CONTEND, VLC_ONCE_DONE };
 
 static_assert (VLC_ONCE_DONE == 3, "Check vlc_once in header file");


=====================================
src/test/thread.c
=====================================
@@ -284,6 +284,44 @@ static void test__cond_wait_timeout()
     TEST_THREAD_JOIN_MAGIC(th);
 }
 
+/*
+ * Test latch synchronisation
+ */
+static void *thread_latch(void *data)
+{
+    vlc_latch_t *l = data;
+
+    assert(!vlc_latch_is_ready(l));
+    vlc_latch_count_down(l, 1);
+    vlc_latch_wait(l);
+    assert(vlc_latch_is_ready(l));
+    return NULL;
+}
+
+static void test__latch(void)
+{
+    vlc_latch_t l;
+    vlc_thread_t th;
+
+    vlc_latch_init(&l, 0);
+    assert(vlc_latch_is_ready(&l));
+    vlc_latch_wait(&l);
+    assert(vlc_latch_is_ready(&l));
+
+    vlc_latch_init(&l, 5);
+    assert(!vlc_latch_is_ready(&l));
+    vlc_latch_count_down(&l, 3);
+    assert(!vlc_latch_is_ready(&l));
+    vlc_latch_count_down_and_wait(&l, 2);
+    assert(vlc_latch_is_ready(&l));
+    vlc_latch_wait(&l);
+    assert(vlc_latch_is_ready(&l));
+
+    vlc_latch_init(&l, 2);
+    TEST_THREAD_CLONE(&th, thread_latch, &l);
+    vlc_latch_count_down_and_wait(&l, 1);
+    TEST_THREAD_JOIN(th, NULL);
+}
 
 /*
  * Test cancelling vlc_tick_sleep
@@ -330,6 +368,7 @@ int main(void)
     test__cond_wait();
     test__cond_wait_timeout();
     test__cond_broadcast();
+    test__latch();
     test__vlc_tick_sleep_cancelation();
     test__vlc_tick_sleep();
 



View it on GitLab: https://code.videolan.org/videolan/vlc/-/compare/39434b48db47420cbfde36516cd8473ca72086d3...6b9336b5dacde19d5d48672d3d666744729fc373

-- 
View it on GitLab: https://code.videolan.org/videolan/vlc/-/compare/39434b48db47420cbfde36516cd8473ca72086d3...6b9336b5dacde19d5d48672d3d666744729fc373
You're receiving this email because of your account on code.videolan.org.


VideoLAN code repository instance


More information about the vlc-commits mailing list