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.

Committer:
Jamie Smith
Date:
Thu Nov 12 22:18:14 2020 -0800
Revision:
0:85e56bb98f6e
Initial commit

Who changed what in which revision?

UserRevisionLine numberNew 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 }