AccurateWaiter allows threaded applications to have threads wait for a precise amount of time, without sending the CPU into a permanent spinlock.
Dependents: AccurateWaiter-Test
AccurateWaiter
...your order, sir?
AccurateWaiter allows threaded applications to have threads wait for a precise amount of time, without sending the CPU into a permanent spinlock.
In standard Mbed OS, certain types of threaded applications are limited by the precision of ThisThread::sleep_for(), which only allows sleeping for a whole number of milliseconds. This limitation comes from the underlying RTX RTOS, which uses a 1 ms scheduling interrupt.
What a lot of people don't know is that there is actually a way around this limitation, using the RTOS's EventFlags objects. Unlike most other RTOS objects, EventFlags are usable from interrupts. By configuring the Mbed us ticker interrupt to set EventFlags, a waiting thread can be woken immediately after an exact number of ticks on the us ticker.
The result is something that combines the best features of wait_us() and ThisThread::sleep_for(). Other threads may run during the wait period (like ThisThread::sleep_for()), but the wait time can be specified to the microsecond (like wait_us()). The only downside is a bit more overhead when starting the wait operation (to trigger the timer interrupt).
I tested this code on my NUCLEO_F429ZI board, and found that it was able to handle any time duration from 1us-1s accurately with exactly 14us of delay between the set time and the time the wait function returns. Overhead of calling the wait function also is around the 15-25us range.
Note 1: Since it uses the Mbed us ticker, this class allows waiting for >250,000 years before rollover.
Note 2: Each AccurateWaiter object may only be safely used by one thread at a time.
AccurateWaiter.cpp@0:85e56bb98f6e, 2020-11-12 (annotated)
- Committer:
- Jamie Smith
- Date:
- Thu Nov 12 22:18:14 2020 -0800
- Revision:
- 0:85e56bb98f6e
Initial commit
Who changed what in which revision?
User | Revision | Line number | New contents of line |
---|---|---|---|
Jamie Smith |
0:85e56bb98f6e | 1 | // |
Jamie Smith |
0:85e56bb98f6e | 2 | // Created by jamie on 11/12/2020. |
Jamie Smith |
0:85e56bb98f6e | 3 | // |
Jamie Smith |
0:85e56bb98f6e | 4 | |
Jamie Smith |
0:85e56bb98f6e | 5 | #include "AccurateWaiter.h" |
Jamie Smith |
0:85e56bb98f6e | 6 | |
Jamie Smith |
0:85e56bb98f6e | 7 | AccurateWaiter::AccurateWaiter(): |
Jamie Smith |
0:85e56bb98f6e | 8 | TimerEvent(get_us_ticker_data()) |
Jamie Smith |
0:85e56bb98f6e | 9 | { |
Jamie Smith |
0:85e56bb98f6e | 10 | |
Jamie Smith |
0:85e56bb98f6e | 11 | } |
Jamie Smith |
0:85e56bb98f6e | 12 | |
Jamie Smith |
0:85e56bb98f6e | 13 | |
Jamie Smith |
0:85e56bb98f6e | 14 | void AccurateWaiter::handler() |
Jamie Smith |
0:85e56bb98f6e | 15 | { |
Jamie Smith |
0:85e56bb98f6e | 16 | // This signals the RTOS that the waiting thread is ready to wake up. |
Jamie Smith |
0:85e56bb98f6e | 17 | flags.set(1); |
Jamie Smith |
0:85e56bb98f6e | 18 | } |
Jamie Smith |
0:85e56bb98f6e | 19 | |
Jamie Smith |
0:85e56bb98f6e | 20 | void AccurateWaiter::wait_for(std::chrono::microseconds duration) |
Jamie Smith |
0:85e56bb98f6e | 21 | { |
Jamie Smith |
0:85e56bb98f6e | 22 | // set up timer event to occur |
Jamie Smith |
0:85e56bb98f6e | 23 | insert(duration); |
Jamie Smith |
0:85e56bb98f6e | 24 | |
Jamie Smith |
0:85e56bb98f6e | 25 | // wait for event flag and then clear it |
Jamie Smith |
0:85e56bb98f6e | 26 | flags.wait_all(1); |
Jamie Smith |
0:85e56bb98f6e | 27 | } |
Jamie Smith |
0:85e56bb98f6e | 28 | |
Jamie Smith |
0:85e56bb98f6e | 29 | void AccurateWaiter::wait_until(TickerDataClock::time_point timePoint) |
Jamie Smith |
0:85e56bb98f6e | 30 | { |
Jamie Smith |
0:85e56bb98f6e | 31 | // set up timer event to occur |
Jamie Smith |
0:85e56bb98f6e | 32 | insert_absolute(timePoint); |
Jamie Smith |
0:85e56bb98f6e | 33 | |
Jamie Smith |
0:85e56bb98f6e | 34 | // wait for event flag and then clear it |
Jamie Smith |
0:85e56bb98f6e | 35 | flags.wait_all(1); |
Jamie Smith |
0:85e56bb98f6e | 36 | } |