[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