Ok

Dependencies:   mbed_rtos_types Mutex mbed_rtos_storage mbed Semaphore

Embed: (wiki syntax)

« Back to documentation index

Show/hide line numbers ConditionVariable.h Source File

ConditionVariable.h

00001 /* Mbed Microcontroller Library
00002  * Copyright (c) 2017-2018 ARM Limited
00003  *
00004  * Permission is hereby granted, free of charge, to any person obtaining a copy
00005  * of this software and associated documentation files (the "Software"), to deal
00006  * in the Software without restriction, including without limitation the rights
00007  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
00008  * copies of the Software, and to permit persons to whom the Software is
00009  * furnished to do so, subject to the following conditions:
00010  *
00011  * The above copyright notice and this permission notice shall be included in
00012  * all copies or substantial portions of the Software.
00013  *
00014  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
00015  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
00016  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
00017  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
00018  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
00019  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
00020  * SOFTWARE.
00021  */
00022 #ifndef CONDITIONVARIABLE_H
00023 #define CONDITIONVARIABLE_H
00024 
00025 #include <stdint.h>
00026 #include "rtos/mbed_rtos_types.h"
00027 #include "rtos/Mutex.h"
00028 #include "rtos/Semaphore.h"
00029 
00030 #include "platform/NonCopyable.h"
00031 
00032 #if MBED_CONF_RTOS_PRESENT || defined(DOXYGEN_ONLY)
00033 
00034 namespace rtos {
00035 /** \addtogroup rtos */
00036 /** @{*/
00037 
00038 struct Waiter;
00039 
00040 /** The ConditionVariable class is a synchronization primitive that allows
00041  *   threads to wait until a particular condition occurs.
00042  *
00043  * Use the condition variable in conjunction with a mutex to safely wait for
00044  * or notify waiters of condition changes to a resource accessible by multiple
00045  * threads.
00046  *
00047  * The thread that intends to wait on a ConditionVariable must:
00048  * - Acquire a lock on a mutex.
00049  * - Execute `wait`, `wait_for` or `wait_until`. While the thread is waiting,
00050  *   the mutex is unlocked.
00051  * - When the condition variable has been notified, or in the case of `wait_for`
00052  *   and `wait_until` the timeout expires, the thread is awakened.
00053  *
00054  * The thread that intends to notify a ConditionVariable must:
00055  * - Acquire a lock on the mutex used to construct the condition variable.
00056  * - Execute `notify_one` or `notify_all` on the condition variable.
00057  *
00058  * All threads waiting on the condition variable wake when
00059  *   `ConditionVariable::notify_all` is called.
00060  * At least one thread waiting on the condition variable wakes
00061  *   when `ConditionVariable::notify_one` is called.
00062  *
00063  * While a thread is waiting for notification of a
00064  *   ConditionVariable, it releases the lock held on the mutex.
00065  * The ConditionVariable reacquires the mutex lock before exiting the wait
00066  *   function.
00067  *
00068  * #### Unspecified behavior
00069  * - The thread that is unblocked on `ConditionVariable::notify_one` is
00070  *   unspecified if there are multiple waiters.
00071  * - When `ConditionVariable::notify_one` or `ConditionVariable::notify_all` is
00072  *   called and there are one or more waiters, and one or more threads
00073  *   attempting to acquire the condition variable's mutex, the order in which the mutex is
00074  *   acquired is unspecified.
00075  * - Spurious notifications (not triggered by the application) can occur.
00076  *
00077  * #### Undefined behavior
00078  * - Calling wait if the mutex is not locked by the current thread is undefined
00079  *   behavior.
00080  * - The order in which waiting threads acquire the condition variable's
00081  *   mutex after `ConditionVariable::notify_all` is called is undefined.
00082  * - The behavior of `ConditionVariable::wait` and `ConditionVariable::wait_for`
00083  *   is undefined if the condition variable's mutex is locked more than once by
00084  *   the calling thread.
00085  *
00086  * @note Synchronization level: Thread safe
00087  *
00088  * Example:
00089  *
00090  * @code
00091  * #include "mbed.h"
00092  *
00093  * Mutex mutex;
00094  * ConditionVariable cv(mutex);
00095  *
00096  * // These variables are protected by locking the mutex.
00097  * uint32_t work_count = 0;
00098  * bool done = false;
00099  *
00100  * void worker_thread()
00101  * {
00102  *   // Acquire lock on mutex before accessing protected variables and waiting.
00103  *   mutex.lock();
00104  *
00105  *   while (done == false) {
00106  *     printf("Worker thread: Count: %lu\r\n", work_count);
00107  *
00108  *     // Wait for main thread to notify the condition variable.
00109  *     printf("Worker thread: Waiting\r\n");
00110  *     cv.wait();
00111  *   }
00112  *
00113  *   printf("Worker: Exiting\r\n");
00114  *
00115  *   // The condition variable acquires the lock when exiting the `wait` function.
00116  *   // Unlock mutex when exiting the thread.
00117  *   mutex.unlock();
00118  * }
00119  *
00120  * int main()
00121  * {
00122  *   Thread thread;
00123  *   thread.start(worker_thread);
00124  *
00125  *   for (int i = 0; i < 5; i++) {
00126  *     // Acquire lock on mutex before modifying variables and notifying.
00127  *     mutex.lock();
00128  *
00129  *     // Change count and notify waiters.
00130  *     work_count++;
00131  *     printf("Main thread: Set count to: %lu\r\n", work_count);
00132  *     printf("Main thread: Notifying worker thread\r\n");
00133  *     cv.notify_all();
00134  *
00135  *     // Mutex must be unlocked before the worker thread can acquire it.
00136  *     mutex.unlock();
00137  *
00138  *     wait(1.0);
00139  *   }
00140  *
00141  *   // Change done and notify waiters of this.
00142  *   mutex.lock();
00143  *   done = true;
00144  *   cv.notify_all();
00145  *   mutex.unlock();
00146  *
00147  *   thread.join();
00148  *
00149  *   printf("Main: Exiting\r\n");
00150  * }
00151  * @endcode
00152  */
00153 
00154 class ConditionVariable : private mbed::NonCopyable<ConditionVariable> {
00155 public:
00156     /** Create and initialize a ConditionVariable object.
00157      *
00158      * @note You cannot call this function from ISR context.
00159     */
00160     ConditionVariable(Mutex &mutex);
00161 
00162     /** Wait for a notification.
00163      *
00164      * Wait causes the current thread to block until the condition variable
00165      * receives a notification from another thread.
00166      *
00167      * @note - The thread calling this function must be the owner of the
00168      * ConditionVariable's mutex, and it must be locked exactly once.
00169      *
00170      * @note - Spurious notifications can occur, so the caller of this API
00171      * should check to make sure the condition the caller is waiting on has
00172      * been met.
00173      *
00174      * @note - The current thread releases the lock while inside the wait
00175      * function and reacquires it upon exiting the function.
00176      *
00177      * Example:
00178      * @code
00179      * mutex.lock();
00180      *
00181      * while (!condition_met) {
00182      *     cond.wait();
00183      * }
00184      *
00185      * function_to_handle_condition();
00186      *
00187      * mutex.unlock();
00188      * @endcode
00189      *
00190      * @note You cannot call this function from ISR context.
00191      */
00192     void wait();
00193 
00194     /** Wait for a notification until the specified time.
00195      *
00196      * Wait until causes the current thread to block until the condition
00197      * variable is notified, or a specific time given by millisec parameter is
00198      * reached.
00199      *
00200      * @param   millisec  Absolute end time referenced to `Kernel::get_ms_count()`
00201      * @return  `true` if a timeout occurred, `false` otherwise.
00202      *
00203      * @note - The thread calling this function must be the owner of the
00204      * ConditionVariable's mutex, and it must be locked exactly once.
00205      *
00206      * @note - Spurious notifications can occur, so the caller of this API
00207      * should check to make sure the condition the caller is waiting on has
00208      * been met.
00209      *
00210      * @note - The current thread releases the lock while inside the wait
00211      * function and reacquires it upon exiting the function.
00212      *
00213      * Example:
00214      * @code
00215      * mutex.lock();
00216      * uint64_t end_time = Kernel::get_ms_count() + COND_WAIT_TIMEOUT;
00217      *
00218      * while (!condition_met) {
00219      *     if (cond.wait_until(end_time)) {
00220      *         break;
00221      *     }
00222      * }
00223      *
00224      * if (condition_met) {
00225      *     function_to_handle_condition();
00226      * }
00227      *
00228      * mutex.unlock();
00229      * @endcode
00230      *
00231      * @note You cannot call this function from ISR context.
00232      */
00233     bool wait_until(uint64_t millisec);
00234 
00235     /** Wait for a notification or timeout.
00236      *
00237      * `Wait for` causes the current thread to block until the condition
00238      * variable receives a notification from another thread, or the timeout
00239      * specified by the millisec parameter is reached.
00240      *
00241      * @param   millisec  Timeout value or osWaitForever in case of no timeout.
00242      * @return  `true` if a timeout occurred, `false` otherwise.
00243      *
00244      * @note - The thread calling this function must be the owner of the
00245      * ConditionVariable's mutex, and it must be locked exactly once.
00246      *
00247      * @note - Spurious notifications can occur, so the caller of this API
00248      * should check to make sure the condition the caller is waiting on has
00249      * been met.
00250      *
00251      * @note - The current thread releases the lock while inside the wait
00252      * function and reacquire it upon exiting the function.
00253      *
00254      * Example:
00255      * @code
00256      * mutex.lock();
00257      *
00258      * while (!condition_met) {
00259      *     cond.wait_for(MAX_SLEEP_TIME);
00260      *     if (!condition_met) {
00261      *         do_other_work_while_condition_false();
00262      *     }
00263      * }
00264      *
00265      * if (condition_met) {
00266      *     function_to_handle_condition();
00267      * }
00268      *
00269      * mutex.unlock();
00270      * @endcode
00271      *
00272      * @note You cannot call this function from ISR context.
00273      */
00274     bool wait_for(uint32_t millisec);
00275 
00276     /** Notify one waiter on this condition variable that a condition changed.
00277      *
00278      * This function unblocks one of the threads waiting for the condition
00279      * variable.
00280      *
00281      * @note - The thread calling this function must be the owner of the
00282      * ConditionVariable's mutex.
00283      *
00284      * @note - The thread that is unblocked on ConditionVariable::notify_one is
00285      * undefined if there are multiple waiters.
00286      *
00287      * @note You cannot call this function from ISR context.
00288      */
00289     void notify_one();
00290 
00291     /** Notify all waiters on this condition variable that a condition changed.
00292      *
00293      * This function unblocks all of the threads waiting for the condition
00294      * variable.
00295      *
00296      * @note - The thread calling this function must be the owner of the
00297      * ConditionVariable's mutex.
00298      *
00299      * @note - If there are one or more waiters and one or more threads
00300      * attempting to acquire the condition variable's mutex the order in which
00301      * the mutex is acquired is undefined.
00302      *
00303      * @note You cannot call this function from ISR context.
00304      */
00305     void notify_all();
00306 
00307     /** ConditionVariable destructor.
00308      *
00309      * @note You cannot call this function from ISR context.
00310      */
00311     ~ConditionVariable();
00312 
00313 #if !defined(DOXYGEN_ONLY)
00314 protected:
00315     struct Waiter {
00316         Waiter();
00317         Semaphore sem;
00318         Waiter *prev;
00319         Waiter *next;
00320         bool in_list;
00321     };
00322 
00323     static void _add_wait_list(Waiter **wait_list, Waiter *waiter);
00324     static void _remove_wait_list(Waiter **wait_list, Waiter *waiter);
00325     Mutex &_mutex;
00326     Waiter *_wait_list;
00327 #endif // !defined(DOXYGEN_ONLY)
00328 };
00329 
00330 }
00331 #endif
00332 
00333 #endif
00334 
00335 /** @}*/
00336