Add documentation for kernel condition variables API

Change-Id: Ib747c0b4559dba9c2985771447ea95c1a430e085
Reviewed-on: https://review.haiku-os.org/c/haiku/+/9676
Reviewed-by: waddlesplash <[email protected]>
This commit is contained in:
PulkoMandy
2025-10-14 14:08:14 +00:00
committed by Adrien Destugues
parent 92cbc8ab0d
commit f6195731ea
5 changed files with 259 additions and 10 deletions
+2
View File
@@ -913,6 +913,7 @@ INPUT = . \
drivers \ drivers \
game \ game \
interface \ interface \
kernel \
keyboard \ keyboard \
locale \ locale \
mail \ mail \
@@ -937,6 +938,7 @@ INPUT = . \
../../headers/os/game \ ../../headers/os/game \
../../headers/os/interface \ ../../headers/os/interface \
../../headers/private/interface/ToolTip.h \ ../../headers/private/interface/ToolTip.h \
../../headers/private/kernel/condition_variable.h \
../../headers/os/locale \ ../../headers/os/locale \
../../headers/os/mail \ ../../headers/os/mail \
../../headers/os/media \ ../../headers/os/media \
+1 -1
View File
@@ -6,7 +6,7 @@
* Adrien Destugues, [email protected] * Adrien Destugues, [email protected]
* *
* Corresponds to: * Corresponds to:
* headers/os/drivers/KernelExport;h rev 57477 * headers/os/drivers/KernelExport.h rev 57477
*/ */
/*! /*!
@@ -54,9 +54,9 @@
\section semaphores Semaphores \section semaphores Semaphores
Semaphores are the historical way to signal events in BeOS and Haiku. A semaphore has a counter Semaphores are the historical way to signal events in BeOS and Haiku. A semaphore has a counter
that can be incremented (release_sem) and decremented (acquire_sem). The counter is not allowed that can be incremented (\ref release_sem) and decremented (\ref acquire_sem). The counter is
to go below 0, if a thread attempts to acquire an empty semaphore, it will be blocked until not allowed to go below 0, if a thread attempts to acquire an empty semaphore, it will be
someone else releases it. blocked until someone else releases it.
Seaphores can be used to implement mutexes: by creating a semaphore with a count of 1, threads Seaphores can be used to implement mutexes: by creating a semaphore with a count of 1, threads
can enter the critical section by acquiring, and exit it by releasing the semaphore. Additional can enter the critical section by acquiring, and exit it by releasing the semaphore. Additional
@@ -78,8 +78,24 @@
\section condition_variables Condition Variables \section condition_variables Condition Variables
A more recent addition to Haiku synchronization primitives is condition variables. They allow A more recent addition to Haiku synchronization primitives is \ref ConditionVariable.
a thread to wait for a specific condition, which is notified by another thread. This is Condition variables allow a thread to wait for a specific condition, which is notified by
similar to the use of semaphores outlined above, but provides an easier way to handle race another thread. This is similar to the use of semaphores outlined above, but provides an easier
conditions, spurious wakeups, and so on. way to handle race conditions, spurious wakeups, and so on.
In user-space programming, condition variables are used for thread synchronization in
conjunction with a locking system. One thread will acquire a lock and verify (atomically) that
some condition is not satisfied, it will then wait on the condition variable and unlock the
lock. Another thread then notifies the condition variable - waking up the thread - when the
condition is satisfied.
The kernel implementation in Haiku is a bit more flexible, which allows it to be used also from
interrupt handlers. An interrupt may notify a condition variable. Since interrupts usually
cannot be locked with mutexes, the synchronization is provided by other means. In some cases,
no synchronization is needed at all: the driver main code can create a ConditionVariableEnty
and add it to the condition variable before accessing the hardware in a way that will later
schedule an interrupt. In that case, the order of operations is guaranteed: the thread will
be watching the condition variable before the interrupt can be triggered, and the interrupt
will wake up the thread. In cases where this is possible, condition variables provide a very
simple and efficient synchronization primitive and makes writing drivers much safer and simpler.
*/ */
+233
View File
@@ -0,0 +1,233 @@
/*
* Copyright 2025 Haiku, Inc. All rights reserved.
* Distributed under the terms of the MIT License.
*
* Authors:
* Adrien Destugues, [email protected]
*
* Corresponds to:
* headers/private/kernel/condition_variable.h hrev58325
*/
/*!
\file condition_variable.h
\ingroup kernel
\brief Kernel condition variables used for thread and interrupt synchronization
\attention This API is experimental and may change in later versions of Haiku.
*/
/*!
\struct ConditionVariableEntry
\ingroup kernel
\brief Waiting on a condition variable.
Waiting on a condition variable is implemented by creating a ConditionVariableEntry,
associating it to a condition variable (using either Add(const void*) or
ConditionVariable::Add(ConditionVariableEntry*) ), and then waiting for the condition
variable to be notified using Wait(uint32, bigtime_t).
*/
/*!
\fn bool ConditionVariableEntry::Add(const void* object)
\brief Watch the published condition variable associated with the \a object.
This method is used to watch a condition variable previously published using
ConditionVariable::Publish(const cvoid*, const char*). The condition variable corresponding to
the object will be located and the condition variable entry will be added to it.
A ConditionVariableEntry can only be added to one single ConditionVariable.
It is safe to delete a ConditionVariableEntry without having waited for the corresponding event.
\return false if the entry could not be added. In that case, calling Wait() will immediately
fail and return the corresponding error code B_ENTRY_NOT_FOUND.
*/
/*!
\fn status_t ConditionVariableEntry::Wait(uint32 flags, bigtime_t timeout)
\brief Wait for the condition variable to be notified
\param flags timeout and semaphore flags to use
\param timeout in microseconds if enabled by flags
\return The status code passed to Notify to wake up the thread
Wait for the previously added condition variable to be notified. The thread is blocked until
then, or until a timeout occurs. The entry is then removed from the condition variable waiting
list. It can be reused by adding it to the same or another condition variable.
If the condition variable was already notified since this entry was added to it, the function
returns immediately and does not block the thread.
The \a flags can be used to:
- Limit the wait to a timeout using B_RELATIVE_TIMEOUT or B_ABSOLUTE_TIMEOUT
- Control semaphore behavior (B_CAN_INTERRUPT, B_KILL_CAN_INTERRUPT)
*/
/*!
\fn status_t ConditionVariableEntry::Wait(const void* object, uint32 flags, bigtime_t timeout)
\brief Convenience method to add a condition variable and immediately wait on it
This combines the effect of the Add and Wait methods. You can use this if you are waiting on
a published condition variable, and do not need to perform any other work between adding and
waiting (such as releasing other locks to perform a "lock switch").
*/
/*!
\fn ConditionVariable* ConditionVariableEntry::Variable() const
\brief Get a pointer to the added condition variable object
This allows to access the ConditionVariable object previously added to the condition variable.
It is especially useful if you need to access a published condition variable, which you
cannot access directly until adding it to a ConditionVariableEntry.
This returns NULL if the condition variable entry is not attached attached to any condition
variable, including when the condition variable has already notified the entry.
*/
/*!
\struct ConditionVariable
\ingroup kernel
\brief Condition variable for thread and interrupt synchronization
Condition variables implement the wait/notify pattern. They are usually associated with an
"object" (for the condition variable's own purposes, that is just an opaque pointer).
One thread will use the Wait functions or a ConditionVariableEntry to wait on the condition
variable. When another thread or an interrupt needs to wake up one such thread, it does so
by notifying the condition variable.
Condition variables are usually used in combination with a mutex, recursive_lock, or some other
direct use of the ConditionVariableEntry struct with its two-step interface (Add() then Wait())
allows usage of any other locking design, or even with no locking at all if the order of
operations is already guaranteed by the code structure.
There are no restrictions on the destruction order: a condition variable may be destructed
while there are condition variable entries waiting on it, or the condition variable entries
may be destructed before they are notified.
*/
/*!
\fn void ConditionVariable::Init(const void* object, const char* objectType)
\brief Initialize an anonymous (unpublished) condition variable.
\param object Object that the condition variable is associated with.
\param objectType String describing the associated object, for debugging purposes.
Anonymous condition variables cannot be used with ConditionVariableEntry::Add() and will not
appear in the kernel debugger list of condition variables (it can still be examined by the dump
command if you have a pointer to it). The other methods can be used without restrictions.
*/
/*!
\fn void ConditionVariable::Publish(const void* object, const char* objectType)
\brief Initialize and publish a condition variable associated with an \a object.
The condition variable is published: ConditionVariableEntry::Add() can be used on the
associated object, and the condition variable will be present in the kernel debugger's
condition variables list.
Publication allows to associate a condition variable with another object that is accessed from
multiple drivers or kernel modules. For example, this can be used to notify some events from
low level drivers to higher level ones (interrupts, hot-pluggable device being removed, ...)
by publishing condition variables associated to device tree nodes.
*/
/*!
\fn void ConditionVariable::Unpublish()
\brief Unpublish a previously published condition variable.
*/
/*!
\fn inline int32 ConditionVariable::NotifyOne(status_t result)
\brief Notify one of the threads waiting on the condition variable.
\param result Result value that will be returned by the Wait call in the notified thread.
\return Number of threads that will receive the result value (0 if no ConditionVariableEntry
was attached, or 1 if there were)
If the thread was already blocked by calling Wait, its execution is resumed.
If the thread had added a ConditionVariableEntry to the condition variable but is not yet
waiting on it, the call to Wait will not block, and will immediately return the \a result
value.
*/
/*!
\fn inline int32 ConditionVariable::NotifyAll(status_t result)
\brief Notify all the threads waiting on the condition variable.
\param result Result value that will be returned by the Wait call in the notified thread.
\return Number of threads that will the result value
*/
/*!
\fn static int32 ConditionVariable::NotifyOne(const void* object, status_t result)
\brief Notify one thread waiting on the published condition variable for \a object.
\return The number of threads that were notified (0 or 1)
*/
/*!
\fn static int32 ConditionVariable::NotifyAll(const void* object, status_t result)
\brief Notify all threads waiting on the published condition variable for \a object.
\return The number of threads that were notified
*/
/*!
\fn void ConditionVariable::Add(ConditionVariableEntry* entry)
\brief Add a ConditionVariableEntry to the waiting list for this condition variable.
*/
/*!
\fn int32 ConditionVariable::EntriesCount()
\brief Return the number of ConditionVariableEntries that are currently in the
ConditionVariable.
The threads associated with such entries may already be waiting, or still executing other code
between their Add and Wait steps.
*/
/*!
\fn status_t ConditionVariable::Wait(uint32 flags, bigtime_t timeout)
\brief Convenience method for waiting without an explicit ConditionVariableEntry.
Create and add a temporary ConditionVariableEntry to the condition variable, then wait on it.
*/
/*!
\fn status_t ConditionVariable::Wait(mutex* lock, uint32 flags, bigtime_t timeout)
\brief Convenience method for atomically unlocking a mutex and waiting on a condition variable.
Create and add a temporary ConditionVariableEntry to the condition variable, unlock the mutex,
wait on the condition variable, and re-acquire the mutex.
This allows to use the condition variable safely for inter-thread synchronization with mutex.
*/
/*!
\fn status_t ConditionVariable::Wait(recursive_lock* lock, uint32 flags, bigtime_t timeout)
\brief Convenience method for atomically unlocking a recursive_lock and waiting on a condition
variable.
Create and add a temporary ConditionVariableEntry to the condition variable, unlock the
recursive_lock, wait on the condition variable, and re-acquire the recursive_lock.
The recursion count of the recursive lock is preserved.
This allows to use the condition variable safely for inter-thread synchronization with
a recursive_lock.
*/
-2
View File
@@ -225,8 +225,6 @@ ConditionVariableEntry::Wait(const void* object, uint32 flags,
// #pragma mark - ConditionVariable // #pragma mark - ConditionVariable
/*! Initialization method for anonymous (unpublished) condition variables.
*/
void void
ConditionVariable::Init(const void* object, const char* objectType) ConditionVariable::Init(const void* object, const char* objectType)
{ {