diff --git a/rtos/ConditionVariable.h b/rtos/ConditionVariable.h index 1a1c4576ea..18181adeab 100644 --- a/rtos/ConditionVariable.h +++ b/rtos/ConditionVariable.h @@ -35,26 +35,44 @@ namespace rtos { struct Waiter; -/** This class provides a safe way to wait for or send notifications of condition changes +/** The ConditionVariable class is a synchronization primitive that allows + * threads to wait until a particular condition occurs. * - * This class is used in conjunction with a mutex to safely wait for or - * notify waiters of condition changes to a resource accessible by multiple + * The condition variable is used in conjunction with a mutex to safely wait for + * or notify waiters of condition changes to a resource accessible by multiple * threads. * - * # Defined behavior - * - All threads waiting on the condition variable wake when - * ConditionVariable::notify_all is called. - * - If one or more threads are waiting on the condition variable at least - * one of them wakes when ConditionVariable::notify is called. + * The thread that intends to wait on a ConditionVariable must: + * - Acquire a lock on a mutex + * - Execute `wait`, `wait_for`, or `wait_until`. While the thread is waiting, + * the mutex will be unlocked. + * - When the condition variable has been notified, or in the case of `wait_for` + * and `wait_until` the timeout expires the thread is awakened. * - * # Undefined behavior + * The thread that intends to notify a ConditionVariable must: + * - Acquire a lock on the mutex used to construct the condition variable. + * - Execute `notify_one` or `notify_all` on the condition variable. + * + * ## Defined behavior + * - All threads that are waiting on the condition variable will wake when + * ConditionVariable::notify_all is called. + * - At least one thread that is waiting on the condition variable will wake + * when ConditionVariable::notify_one is called. + * - While waiting for a thread is waiting for notification of a + * ConditionVariable it will release the lock held on the mutex. + * - The ConditionVariable will reacquire the mutex lock before exiting the wait + * function. + * + * ## Undefined behavior * - The thread which is unblocked on ConditionVariable::notify_one is * undefined if there are multiple waiters. - * - The order which in which waiting threads acquire the condition variable's + * - Calling wait if the mutex is not locked by the current thread is undefined + * behavior. + * - The order in which waiting threads acquire the condition variable's * mutex after ConditionVariable::notify_all is called is undefined. * - When ConditionVariable::notify_one or ConditionVariable::notify_all is - * called and there are one or more waiters and one or more threads attempting - * to acquire the condition variable's mutex the order in which the mutex is + * called and there are one or more waiters, and one or more threads + * attempting to acquire the condition variable's mutex the order in which the mutex is * acquired is undefined. * - The behavior of ConditionVariable::wait and ConditionVariable::wait_for * is undefined if the condition variable's mutex is locked more than once by @@ -65,60 +83,74 @@ struct Waiter; * @note Synchronization level: Thread safe * * Example: + * * @code * #include "mbed.h" * * Mutex mutex; - * ConditionVariable cond(mutex); + * ConditionVariable cv(mutex); * * // These variables are protected by locking mutex - * uint32_t count = 0; + * uint32_t work_count = 0; * bool done = false; * * void worker_thread() * { - * mutex.lock(); - * do { - * printf("Worker: Count %lu\r\n", count); + * // Acquire lock on mutex before accessing protected variables and waiting. + * mutex.lock(); * - * // Wait for a condition to change - * cond.wait(); + * while (done == false) { + * printf("Worker thread: Count: %lu\r\n", work_count); * - * } while (!done); - * printf("Worker: Exiting\r\n"); - * mutex.unlock(); + * // Wait for main thread to notify the condition variable + * printf("Worker thread: Waiting\r\n"); + * cv.wait(); + * } + * + * printf("Worker: Exiting\r\n"); + * + * // The condition variable acquires the lock when exiting the `wait` function. + * // Unlock mutex when exiting the thread. + * mutex.unlock(); * } * - * int main() { - * Thread thread; - * thread.start(worker_thread); - * - * for (int i = 0; i < 5; i++) { - * - * mutex.lock(); - * // Change count and notify waiters of this - * count++; - * printf("Main: Set count to %lu\r\n", count); - * cond.notify_all(); - * mutex.unlock(); - * - * wait(1.0); - * } + * int main() + * { + * Thread thread; + * thread.start(worker_thread); * + * for (int i = 0; i < 5; i++) { + * // Acquire lock on mutex before modifying variables and notifying. * mutex.lock(); - * // Change done and notify waiters of this - * done = true; - * printf("Main: Set done\r\n"); - * cond.notify_all(); + * + * // Change count and notify waiters of this + * work_count++; + * printf("Main thread: Set count to: %lu\r\n", work_count); + * printf("Main thread: Notifying worker thread\r\n"); + * cv.notify_all(); + * + * // Mutex must be unlocked before the worker thread can acquire it. * mutex.unlock(); * - * thread.join(); + * wait(1.0); + * } + * + * // Change done and notify waiters of this + * mutex.lock(); + * done = true; + * cv.notify_all(); + * mutex.unlock(); + * + * thread.join(); + * + * printf("Main: Exiting\r\n"); * } * @endcode */ + class ConditionVariable : private mbed::NonCopyable { public: - /** Create and Initialize a ConditionVariable object + /** Create and initialize a ConditionVariable object * * @note You cannot call this function from ISR context. */ @@ -126,17 +158,23 @@ public: /** Wait for a notification * - * Wait until a notification occurs. + * Wait causes the current thread to block until the condition variable + * receives a notification from another thread. * * @note - The thread calling this function must be the owner of the - * ConditionVariable's mutex and it must be locked exactly once + * ConditionVariable's mutex and it must be locked exactly once. + * * @note - Spurious notifications can occur so the caller of this API * should check to make sure the condition they are waiting on has - * been met + * been met. + * + * @note - The current thread will release the lock while inside the wait + * function and reacquire it upon exiting the function. * * Example: * @code * mutex.lock(); + * * while (!condition_met) { * cond.wait(); * } @@ -151,15 +189,23 @@ public: void wait(); /** Wait for a notification until specified time + * + * Wait until causes the current thread to block until the condition + * variable is notified, or a specific time given by millisec parameter is + * reached. * * @param millisec absolute end time referenced to Kernel::get_ms_count() * @return true if a timeout occurred, false otherwise. * * @note - The thread calling this function must be the owner of the - * ConditionVariable's mutex and it must be locked exactly once + * ConditionVariable's mutex and it must be locked exactly once. + * * @note - Spurious notifications can occur so the caller of this API * should check to make sure the condition they are waiting on has - * been met + * been met. + * + * @note - The current thread will release the lock while inside the wait + * function and reacquire it upon exiting the function. * * Example: * @code @@ -184,15 +230,23 @@ public: bool wait_until(uint64_t millisec); /** Wait for a notification or timeout + * + * `Wait for` causes the current thread to block until the condition + * variable receives a notification from another thread, or the timeout + * specified by the millisec parameter is reached. * * @param millisec timeout value or osWaitForever in case of no time-out. * @return true if a timeout occurred, false otherwise. * * @note - The thread calling this function must be the owner of the - * ConditionVariable's mutex and it must be locked exactly once + * ConditionVariable's mutex and it must be locked exactly once. + * * @note - Spurious notifications can occur so the caller of this API * should check to make sure the condition they are waiting on has - * been met + * been met. + * + * @note - The current thread will release the lock while inside the wait + * function and reacquire it upon exiting the function. * * Example: * @code @@ -218,7 +272,14 @@ public: /** Notify one waiter on this condition variable that a condition changed. * - * @note - The thread calling this function must be the owner of the ConditionVariable's mutex + * This function will unblock one of the threads waiting for the condition + * variable. + * + * @note - The thread calling this function must be the owner of the + * ConditionVariable's mutex + * + * @note - The thread which is unblocked on ConditionVariable::notify_one is + * undefined if there are multiple waiters. * * @note You cannot call this function from ISR context. */ @@ -226,7 +287,15 @@ public: /** Notify all waiters on this condition variable that a condition changed. * - * @note - The thread calling this function must be the owner of the ConditionVariable's mutex + * This function will unblock all of the threads waiting for the condition + * variable. + * + * @note - The thread calling this function must be the owner of the + * ConditionVariable's mutex + * + * @note - If there are one or more waiters and one or more threads + * attempting to acquire the condition variable's mutex the order in which + * the mutex is acquired is undefined. * * @note You cannot call this function from ISR context. */ @@ -238,6 +307,7 @@ public: */ ~ConditionVariable(); +#if !defined(DOXYGEN_ONLY) protected: struct Waiter { Waiter(); @@ -247,10 +317,12 @@ protected: bool in_list; }; +private: static void _add_wait_list(Waiter **wait_list, Waiter *waiter); static void _remove_wait_list(Waiter **wait_list, Waiter *waiter); Mutex &_mutex; Waiter *_wait_list; +#endif // !defined(DOXYGEN_ONLY) }; }