From 190d49d874f2986888970fdca570316729a057b7 Mon Sep 17 00:00:00 2001 From: Wim Taymans Date: Tue, 10 Jun 2025 18:00:01 +0200 Subject: [PATCH] loop: add docs about the locking --- spa/include/spa/support/loop.h | 25 +++++++++++++++++++++---- src/pipewire/thread-loop.h | 16 +++++++++++----- 2 files changed, 32 insertions(+), 9 deletions(-) diff --git a/spa/include/spa/support/loop.h b/spa/include/spa/support/loop.h index a1e6a9a3b..1aec922de 100644 --- a/spa/include/spa/support/loop.h +++ b/spa/include/spa/support/loop.h @@ -105,6 +105,7 @@ struct spa_loop_methods { /** Invoke a function in the context of this loop. * May be called from any thread and multiple threads at the same time. + * * If called from the loop's thread, all callbacks previously queued with * invoke() will be run synchronously, which might cause unexpected * reentrancy problems. @@ -274,7 +275,7 @@ struct spa_loop_control_methods { * This function should be called before calling iterate and is * typically used to capture the thread that this loop will run in. * It should ideally be called once from the thread that will run - * the loop. + * the loop. This function will lock the loop. */ void (*enter) (void *object); /** Leave a loop @@ -282,6 +283,8 @@ struct spa_loop_control_methods { * * It should ideally be called once after calling iterate when the loop * will no longer be iterated from the thread that called enter(). + * + * This function will unlock the loop. */ void (*leave) (void *object); @@ -290,8 +293,10 @@ struct spa_loop_control_methods { * \param timeout an optional timeout in milliseconds. * 0 for no timeout, -1 for infinite timeout. * - * This function will block - * up to \a timeout milliseconds and then dispatch the fds with activity. + * This function will first unlock the loop and then block + * up to \a timeout milliseconds, lock the loop again and then + * dispatch the fds with activity. + * * The number of dispatched fds is returned. */ int (*iterate) (void *object, int timeout); @@ -327,11 +332,17 @@ struct spa_loop_control_methods { /** get the absolute time * Get the current time with \ref timeout that can be used in wait. * Since version 2:2 + * + * This function can be called from any thread. */ int (*get_time) (void *object, struct timespec *abstime, int64_t timeout); + /** Wait for a signal * Wait until a thread performs signal. Since version 2:2 * + * This function must be called with the loop lock. Because this is a + * blocking call, it should not be performed from a realtime thread. + * * \param[in] object the control * \param[in] abstime the maximum time to wait for the signal or NULL * \return 0 on success or a negative return value on error. @@ -343,16 +354,22 @@ struct spa_loop_control_methods { * When wait_for_accept is set, this functions blocks until all * threads performed accept. * + * This function must be called with the loop lock and is safe to + * call from a realtime thread source dispatch functions when + * wait_for_accept is false. + * * \param[in] object the control * \param[in] wait_for_accept block for accept * \return 0 on success or a negative return value on error. */ int (*signal) (void *object, bool wait_for_accept); - /** Accept signalers * Resume the thread that signaled with wait_for accept. * + * This function must be called with the loop lock and is safe to + * call from a realtime thread source dispatch functions. + * * \param[in] object the control * \return 0 on success or a negative return value on error. */ diff --git a/src/pipewire/thread-loop.h b/src/pipewire/thread-loop.h index 0d8dbdcea..d40e47b7a 100644 --- a/src/pipewire/thread-loop.h +++ b/src/pipewire/thread-loop.h @@ -126,29 +126,35 @@ void pw_thread_loop_lock(struct pw_thread_loop *loop); /** Unlock the loop */ void pw_thread_loop_unlock(struct pw_thread_loop *loop); -/** Release the lock and wait until some thread calls \ref pw_thread_loop_signal */ +/** Release the lock and wait until some thread calls \ref pw_thread_loop_signal. + * This must be called with the loop locked. */ void pw_thread_loop_wait(struct pw_thread_loop *loop); /** Release the lock and wait a maximum of 'wait_max_sec' seconds - * until some thread calls \ref pw_thread_loop_signal or time out */ + * until some thread calls \ref pw_thread_loop_signal or time out. + * This must be called with the loop locked. */ int pw_thread_loop_timed_wait(struct pw_thread_loop *loop, int wait_max_sec); /** Get a struct timespec suitable for \ref pw_thread_loop_timed_wait_full. + * This function may be called from any thread. * Since: 0.3.7 */ int pw_thread_loop_get_time(struct pw_thread_loop *loop, struct timespec *abstime, int64_t timeout); /** Release the lock and wait up to \a abstime until some thread calls * \ref pw_thread_loop_signal. Use \ref pw_thread_loop_get_time to make a timeout. + * This must be called with the loop locked. * Since: 0.3.7 */ int pw_thread_loop_timed_wait_full(struct pw_thread_loop *loop, const struct timespec *abstime); -/** Signal all threads waiting with \ref pw_thread_loop_wait */ +/** Signal all threads waiting with \ref pw_thread_loop_wait + * This must be called with the loop locked. */ void pw_thread_loop_signal(struct pw_thread_loop *loop, bool wait_for_accept); -/** Signal all threads executing \ref pw_thread_loop_signal with wait_for_accept */ +/** Signal all threads executing \ref pw_thread_loop_signal with wait_for_accept. + * This must be called with the loop locked. */ void pw_thread_loop_accept(struct pw_thread_loop *loop); -/** Check if inside the thread */ +/** Check if inside the thread. This can be called from any thread. */ bool pw_thread_loop_in_thread(struct pw_thread_loop *loop); /**