mirror of https://github.com/ARMmbed/mbed-os.git
Add Chrono and predicate support to ConditionVariable
parent
f4e0ea2c75
commit
fdc697ee85
|
@ -24,6 +24,7 @@
|
||||||
#define CONDITIONVARIABLE_H
|
#define CONDITIONVARIABLE_H
|
||||||
|
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
|
#include <utility>
|
||||||
#include "rtos/mbed_rtos_types.h"
|
#include "rtos/mbed_rtos_types.h"
|
||||||
#include "rtos/Mutex.h"
|
#include "rtos/Mutex.h"
|
||||||
#include "rtos/Semaphore.h"
|
#include "rtos/Semaphore.h"
|
||||||
|
@ -36,6 +37,11 @@ namespace rtos {
|
||||||
/** \addtogroup rtos-public-api */
|
/** \addtogroup rtos-public-api */
|
||||||
/** @{*/
|
/** @{*/
|
||||||
|
|
||||||
|
enum class cv_status {
|
||||||
|
no_timeout,
|
||||||
|
timeout
|
||||||
|
};
|
||||||
|
|
||||||
struct Waiter;
|
struct Waiter;
|
||||||
/**
|
/**
|
||||||
* \defgroup rtos_ConditionVariable ConditionVariable class
|
* \defgroup rtos_ConditionVariable ConditionVariable class
|
||||||
|
@ -178,7 +184,7 @@ public:
|
||||||
* should check to make sure the condition the caller is waiting on has
|
* should check to make sure the condition the caller is waiting on has
|
||||||
* been met.
|
* been met.
|
||||||
*
|
*
|
||||||
* @note - The current thread releases the lock while inside the wait
|
* @note - The current thread releases the mutex while inside the wait
|
||||||
* function and reacquires it upon exiting the function.
|
* function and reacquires it upon exiting the function.
|
||||||
*
|
*
|
||||||
* Example:
|
* Example:
|
||||||
|
@ -198,6 +204,43 @@ public:
|
||||||
*/
|
*/
|
||||||
void wait();
|
void wait();
|
||||||
|
|
||||||
|
/** Wait for a predicate.
|
||||||
|
*
|
||||||
|
* Wait causes the current thread to block until the predicate is
|
||||||
|
* true.
|
||||||
|
*
|
||||||
|
* @param pred A function-like object such that `pred()` is convertible to bool
|
||||||
|
*
|
||||||
|
* @note - The thread calling this function must be the owner of the
|
||||||
|
* ConditionVariable's mutex, and it must be locked exactly once.
|
||||||
|
*
|
||||||
|
* @note - The current thread releases the mutex while inside the wait
|
||||||
|
* function and reacquires it upon exiting the function.
|
||||||
|
*
|
||||||
|
* Example:
|
||||||
|
* @code
|
||||||
|
* extern bool data_available();
|
||||||
|
*
|
||||||
|
* mutex.lock();
|
||||||
|
*
|
||||||
|
* cond.wait(data_available);
|
||||||
|
*
|
||||||
|
* function_to_handle_data();
|
||||||
|
*
|
||||||
|
* mutex.unlock();
|
||||||
|
* @endcode
|
||||||
|
*
|
||||||
|
* @note You cannot call this function from ISR context.
|
||||||
|
*/
|
||||||
|
template <typename Predicate>
|
||||||
|
void wait(Predicate pred)
|
||||||
|
{
|
||||||
|
while (!pred()) {
|
||||||
|
wait();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/** Wait for a notification until the specified time.
|
/** Wait for a notification until the specified time.
|
||||||
*
|
*
|
||||||
* Wait until causes the current thread to block until the condition
|
* Wait until causes the current thread to block until the condition
|
||||||
|
@ -236,9 +279,94 @@ public:
|
||||||
* @endcode
|
* @endcode
|
||||||
*
|
*
|
||||||
* @note You cannot call this function from ISR context.
|
* @note You cannot call this function from ISR context.
|
||||||
|
* @deprecated Pass a chrono time_point, not an integer millisecond count. For example use `Kernel::Clock::now() + 5s`
|
||||||
|
* rather than `Kernel::get_ms_count() + 5000`.
|
||||||
*/
|
*/
|
||||||
|
MBED_DEPRECATED_SINCE("mbed-os-6.0.0", "Pass a chrono time_point, not an integer millisecond count. For example use `Kernel::Clock::now() + 5s` rather than `Kernel::get_ms_count() + 5000`.")
|
||||||
bool wait_until(uint64_t millisec);
|
bool wait_until(uint64_t millisec);
|
||||||
|
|
||||||
|
/** Wait for a notification until the 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 abs_time Absolute end time referenced to `Kernel::Clock`
|
||||||
|
* @return `cv_status::timeout` if a timeout occurred, `cv_status::no_timeout` otherwise.
|
||||||
|
*
|
||||||
|
* @note - The thread calling this function must be the owner of the
|
||||||
|
* 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 the caller is waiting on has
|
||||||
|
* been met.
|
||||||
|
*
|
||||||
|
* @note - The current thread releases the lock while inside the wait
|
||||||
|
* function and reacquires it upon exiting the function.
|
||||||
|
*
|
||||||
|
* Example:
|
||||||
|
* @code
|
||||||
|
* mutex.lock();
|
||||||
|
* Kernel::Clock::time_point end_time = Kernel::Clock::now() + 2s;
|
||||||
|
*
|
||||||
|
* while (!condition_met) {
|
||||||
|
* if (cond.wait_until(end_time) == cv_status::timeout) {
|
||||||
|
* break;
|
||||||
|
* }
|
||||||
|
* }
|
||||||
|
*
|
||||||
|
* if (condition_met) {
|
||||||
|
* function_to_handle_condition();
|
||||||
|
* }
|
||||||
|
*
|
||||||
|
* mutex.unlock();
|
||||||
|
* @endcode
|
||||||
|
*
|
||||||
|
* @note You cannot call this function from ISR context.
|
||||||
|
*/
|
||||||
|
cv_status wait_until(Kernel::Clock::time_point abs_time);
|
||||||
|
|
||||||
|
/** Wait for a predicate until the specified time.
|
||||||
|
*
|
||||||
|
* Wait until causes the current thread to block until the predicate is true,
|
||||||
|
* or a specific time given by abs_time parameter is reached.
|
||||||
|
*
|
||||||
|
* @param abs_time Absolute end time referenced to `Kernel::Clock`
|
||||||
|
* @param pred A function-like object such that `pred()` is convertible to bool
|
||||||
|
* @return The state of the predicate
|
||||||
|
*
|
||||||
|
* @note - The thread calling this function must be the owner of the
|
||||||
|
* ConditionVariable's mutex, and it must be locked exactly once.
|
||||||
|
*
|
||||||
|
* @note - The current thread releases the mutex while inside the wait
|
||||||
|
* function and reacquires it upon exiting the function.
|
||||||
|
*
|
||||||
|
* Example:
|
||||||
|
* @code
|
||||||
|
* extern bool data_available();
|
||||||
|
*
|
||||||
|
* mutex.lock();
|
||||||
|
*
|
||||||
|
* if (cond.wait_until(Kernel::Clock::now() + 2s, data_available)) {
|
||||||
|
* function_to_handle_data();
|
||||||
|
* }
|
||||||
|
*
|
||||||
|
* mutex.unlock();
|
||||||
|
* @endcode
|
||||||
|
*
|
||||||
|
* @note You cannot call this function from ISR context.
|
||||||
|
*/
|
||||||
|
template <class Predicate>
|
||||||
|
bool wait_until(Kernel::Clock::time_point abs_time, Predicate pred)
|
||||||
|
{
|
||||||
|
while (!pred()) {
|
||||||
|
if (wait_until(abs_time) == cv_status::timeout) {
|
||||||
|
return pred();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
/** Wait for a notification or timeout.
|
/** Wait for a notification or timeout.
|
||||||
*
|
*
|
||||||
* `Wait for` causes the current thread to block until the condition
|
* `Wait for` causes the current thread to block until the condition
|
||||||
|
@ -277,9 +405,88 @@ public:
|
||||||
* @endcode
|
* @endcode
|
||||||
*
|
*
|
||||||
* @note You cannot call this function from ISR context.
|
* @note You cannot call this function from ISR context.
|
||||||
|
* @deprecated Pass a chrono duration, not an integer millisecond count. For example use `5s` rather than `5000`.
|
||||||
*/
|
*/
|
||||||
|
MBED_DEPRECATED_SINCE("mbed-os-6.0.0", "Pass a chrono duration, not an integer millisecond count. For example use `5s` rather than `5000`.")
|
||||||
bool wait_for(uint32_t millisec);
|
bool wait_for(uint32_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 rel_time Timeout value.
|
||||||
|
* @return `cv_status::timeout` if a timeout occurred, `cv_status::no_timeout` otherwise.
|
||||||
|
*
|
||||||
|
* @note - The thread calling this function must be the owner of the
|
||||||
|
* 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 the caller is waiting on has
|
||||||
|
* been met.
|
||||||
|
*
|
||||||
|
* @note - The current thread releases the lock while inside the wait
|
||||||
|
* function and reacquire it upon exiting the function.
|
||||||
|
*
|
||||||
|
* Example:
|
||||||
|
* @code
|
||||||
|
* mutex.lock();
|
||||||
|
*
|
||||||
|
* while (!condition_met) {
|
||||||
|
* cond.wait_for(MAX_SLEEP_TIME);
|
||||||
|
* if (!condition_met) {
|
||||||
|
* do_other_work_while_condition_false();
|
||||||
|
* }
|
||||||
|
* }
|
||||||
|
*
|
||||||
|
* if (condition_met) {
|
||||||
|
* function_to_handle_condition();
|
||||||
|
* }
|
||||||
|
*
|
||||||
|
* mutex.unlock();
|
||||||
|
* @endcode
|
||||||
|
*
|
||||||
|
* @note You cannot call this function from ISR context.
|
||||||
|
*/
|
||||||
|
cv_status wait_for(Kernel::Clock::duration_u32 rel_time);
|
||||||
|
|
||||||
|
/** Wait for a predicate or timeout.
|
||||||
|
*
|
||||||
|
* `Wait for` causes the current thread to block until the predicate
|
||||||
|
* is true, or the timeout specified by the rel_time parameter is reached.
|
||||||
|
*
|
||||||
|
* @param rel_time Timeout value.
|
||||||
|
* @param pred a function-like object such that `pred()` is convertible to bool
|
||||||
|
* @return The state of the predicate
|
||||||
|
*
|
||||||
|
* @note - The thread calling this function must be the owner of the
|
||||||
|
* ConditionVariable's mutex, and it must be locked exactly once.
|
||||||
|
*
|
||||||
|
* @note - The current thread releases the mutex while inside the wait
|
||||||
|
* function and reacquire it upon exiting the function.
|
||||||
|
*
|
||||||
|
* Example:
|
||||||
|
* @code
|
||||||
|
* extern bool data_available();
|
||||||
|
*
|
||||||
|
* mutex.lock();
|
||||||
|
*
|
||||||
|
* if (cond.wait_for(2s, data_available)) {
|
||||||
|
* function_to_handle_data();
|
||||||
|
* }
|
||||||
|
*
|
||||||
|
* mutex.unlock();
|
||||||
|
* @endcode
|
||||||
|
*
|
||||||
|
* @note You cannot call this function from ISR context.
|
||||||
|
*/
|
||||||
|
template <class Predicate>
|
||||||
|
bool wait_for(Kernel::Clock::duration rel_time, Predicate pred)
|
||||||
|
{
|
||||||
|
return wait_until(Kernel::Clock::now() + rel_time, std::move(pred));
|
||||||
|
}
|
||||||
|
|
||||||
/** Notify one waiter on this condition variable that a condition changed.
|
/** Notify one waiter on this condition variable that a condition changed.
|
||||||
*
|
*
|
||||||
* This function unblocks one of the threads waiting for the condition
|
* This function unblocks one of the threads waiting for the condition
|
||||||
|
|
|
@ -29,6 +29,9 @@
|
||||||
|
|
||||||
#if MBED_CONF_RTOS_PRESENT
|
#if MBED_CONF_RTOS_PRESENT
|
||||||
|
|
||||||
|
using std::chrono::duration;
|
||||||
|
using std::milli;
|
||||||
|
|
||||||
namespace rtos {
|
namespace rtos {
|
||||||
|
|
||||||
ConditionVariable::Waiter::Waiter(): sem(0), prev(nullptr), next(nullptr), in_list(false)
|
ConditionVariable::Waiter::Waiter(): sem(0), prev(nullptr), next(nullptr), in_list(false)
|
||||||
|
@ -43,10 +46,15 @@ ConditionVariable::ConditionVariable(Mutex &mutex): _mutex(mutex), _wait_list(nu
|
||||||
|
|
||||||
void ConditionVariable::wait()
|
void ConditionVariable::wait()
|
||||||
{
|
{
|
||||||
wait_for(osWaitForever);
|
wait_for(Kernel::wait_for_u32_forever);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ConditionVariable::wait_for(uint32_t millisec)
|
bool ConditionVariable::wait_for(uint32_t millisec)
|
||||||
|
{
|
||||||
|
return wait_for(duration<uint32_t, milli>(millisec)) == cv_status::timeout;
|
||||||
|
}
|
||||||
|
|
||||||
|
cv_status ConditionVariable::wait_for(Kernel::Clock::duration_u32 rel_time)
|
||||||
{
|
{
|
||||||
Waiter current_thread;
|
Waiter current_thread;
|
||||||
MBED_ASSERT(_mutex.get_owner() == ThisThread::get_id());
|
MBED_ASSERT(_mutex.get_owner() == ThisThread::get_id());
|
||||||
|
@ -55,7 +63,7 @@ bool ConditionVariable::wait_for(uint32_t millisec)
|
||||||
|
|
||||||
_mutex.unlock();
|
_mutex.unlock();
|
||||||
|
|
||||||
bool timeout = !current_thread.sem.try_acquire_for(millisec);
|
cv_status status = current_thread.sem.try_acquire_for(rel_time) ? cv_status::no_timeout : cv_status::timeout;
|
||||||
|
|
||||||
_mutex.lock();
|
_mutex.lock();
|
||||||
|
|
||||||
|
@ -63,24 +71,29 @@ bool ConditionVariable::wait_for(uint32_t millisec)
|
||||||
_remove_wait_list(&_wait_list, ¤t_thread);
|
_remove_wait_list(&_wait_list, ¤t_thread);
|
||||||
}
|
}
|
||||||
|
|
||||||
return timeout;
|
return status;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ConditionVariable::wait_until(uint64_t millisec)
|
bool ConditionVariable::wait_until(uint64_t millisec)
|
||||||
{
|
{
|
||||||
uint64_t now = Kernel::get_ms_count();
|
return wait_until(Kernel::Clock::time_point(duration<uint64_t, milli>(millisec))) == cv_status::timeout;
|
||||||
|
}
|
||||||
|
|
||||||
if (now >= millisec) {
|
cv_status ConditionVariable::wait_until(Kernel::Clock::time_point abs_time)
|
||||||
|
{
|
||||||
|
Kernel::Clock::time_point now = Kernel::Clock::now();
|
||||||
|
|
||||||
|
if (now >= abs_time) {
|
||||||
// Time has already passed - standard behaviour is to
|
// Time has already passed - standard behaviour is to
|
||||||
// treat as a "try".
|
// treat as a "try".
|
||||||
return wait_for(0);
|
return wait_for(Kernel::Clock::duration_u32::zero());
|
||||||
} else if (millisec - now >= osWaitForever) {
|
} else if (abs_time - now > Kernel::wait_for_u32_max) {
|
||||||
// Exceeds maximum delay of underlying wait_for -
|
// Exceeds maximum delay of underlying wait_for -
|
||||||
// spuriously wake after 49 days, indicating no timeout.
|
// spuriously wake after 49 days, indicating no timeout.
|
||||||
wait_for(osWaitForever - 1);
|
wait_for(Kernel::wait_for_u32_max);
|
||||||
return false;
|
return cv_status::no_timeout;
|
||||||
} else {
|
} else {
|
||||||
return wait_for(millisec - now);
|
return wait_for(abs_time - now);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue