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
|
||||
|
||||
#include <stdint.h>
|
||||
#include <utility>
|
||||
#include "rtos/mbed_rtos_types.h"
|
||||
#include "rtos/Mutex.h"
|
||||
#include "rtos/Semaphore.h"
|
||||
|
@ -36,6 +37,11 @@ namespace rtos {
|
|||
/** \addtogroup rtos-public-api */
|
||||
/** @{*/
|
||||
|
||||
enum class cv_status {
|
||||
no_timeout,
|
||||
timeout
|
||||
};
|
||||
|
||||
struct Waiter;
|
||||
/**
|
||||
* \defgroup rtos_ConditionVariable ConditionVariable class
|
||||
|
@ -178,7 +184,7 @@ public:
|
|||
* 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
|
||||
* @note - The current thread releases the mutex while inside the wait
|
||||
* function and reacquires it upon exiting the function.
|
||||
*
|
||||
* Example:
|
||||
|
@ -198,6 +204,43 @@ public:
|
|||
*/
|
||||
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 until causes the current thread to block until the condition
|
||||
|
@ -236,9 +279,94 @@ public:
|
|||
* @endcode
|
||||
*
|
||||
* @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);
|
||||
|
||||
/** 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` causes the current thread to block until the condition
|
||||
|
@ -277,9 +405,88 @@ public:
|
|||
* @endcode
|
||||
*
|
||||
* @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);
|
||||
|
||||
/** 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.
|
||||
*
|
||||
* This function unblocks one of the threads waiting for the condition
|
||||
|
|
|
@ -29,6 +29,9 @@
|
|||
|
||||
#if MBED_CONF_RTOS_PRESENT
|
||||
|
||||
using std::chrono::duration;
|
||||
using std::milli;
|
||||
|
||||
namespace rtos {
|
||||
|
||||
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()
|
||||
{
|
||||
wait_for(osWaitForever);
|
||||
wait_for(Kernel::wait_for_u32_forever);
|
||||
}
|
||||
|
||||
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;
|
||||
MBED_ASSERT(_mutex.get_owner() == ThisThread::get_id());
|
||||
|
@ -55,7 +63,7 @@ bool ConditionVariable::wait_for(uint32_t millisec)
|
|||
|
||||
_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();
|
||||
|
||||
|
@ -63,24 +71,29 @@ bool ConditionVariable::wait_for(uint32_t millisec)
|
|||
_remove_wait_list(&_wait_list, ¤t_thread);
|
||||
}
|
||||
|
||||
return timeout;
|
||||
return status;
|
||||
}
|
||||
|
||||
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
|
||||
// treat as a "try".
|
||||
return wait_for(0);
|
||||
} else if (millisec - now >= osWaitForever) {
|
||||
return wait_for(Kernel::Clock::duration_u32::zero());
|
||||
} else if (abs_time - now > Kernel::wait_for_u32_max) {
|
||||
// Exceeds maximum delay of underlying wait_for -
|
||||
// spuriously wake after 49 days, indicating no timeout.
|
||||
wait_for(osWaitForever - 1);
|
||||
return false;
|
||||
wait_for(Kernel::wait_for_u32_max);
|
||||
return cv_status::no_timeout;
|
||||
} else {
|
||||
return wait_for(millisec - now);
|
||||
return wait_for(abs_time - now);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue