Struct r3::kernel::Timer [−][src]
#[repr(transparent)]pub struct Timer<System>(_, _);
Expand description
Represents a single timer in a system.
This type is ABI-compatible with Id
.
Relation to Other Specifications: A similar concept exists in almost every operating system.
Timer States
A timer may be in one of the following states:
-
Dormant — The timer is not running and can be started.
-
Active — The timer is running and can be stopped.
Timer Scheduling
The scheduling of a timer is determined by two state variables:
-
The delay is an optional non-negative duration value (
Option<Duration>
) that specifies the minimum period of time before the callback function gets called.If the delay is
None
, it’s treated as infinity and the function will never execute.While a timer is active, this value decreases at a steady rate. If the system can’t process a timer for an extended period of time, this value might temporarily fall negative.
-
The period is an optional non-negative duration value. On expiration, the system adds this value to the timer’s delay.
Overdue Timers
When scheduling a next tick, the system takes the observed timer handling latency into account and makes the new delay shorter than the period as needed to ensure that the callback function is called in a steady rate. This behavior is illustrated by the above figure. This is accomplished by adding the specified period to the timer’s absolute arrival time instead of recalculating the arrival time based on the current system time. The delay is a difference between the current system time and the arrival time.
Note that the system does not impose any limit on the extent of this
behavior. To put this simply, if one second elapses, the system makes one
second worth of calls no matter what.
If a periodic timer’s callback function couldn’t complete within the
timer’s period, the timer latency would steadily increase until it reaches
the point where various internal assumptions (such as
TIME_HARD_HEADROOM
) get broken. While the system is processing overdue
calls, the timer interrupt handler will not return. Some port timer drivers
(most notably the Arm-M tickful SysTick driver) have much lower tolerance
for this.
To avoid this catastrophic situation, an application should take the
precautions shown below:
-
Don’t perform an operation that might take an unbounded time in a timer callback function.
-
Off-load time-consuming operations to a task, which is activated or unparked by a timer callback function.
-
Don’t specify zero as period unless you know what you are doing.
-
Keep your target platform’s performance characteristics in your mind.
Start/Stop
When a timer is stopped, the timer will not fire anymore and the delay remains stationary at the captured value. If the captured value is negative, it’s rounded to zero. This means that if there are more than one outstanding call at the moment of stopping, they will be dropped.
Another way to stop a timer is to set the delay or the period to None
(infinity).
Dynamic Period
The period can be changed anytime. The system reads it before calling a timer callback function and adds it to the timer’s current delay value.
It might be tricky to understand the outcome of changing the period when there are overdue calls. It could be explained in this way: If there are one second worth of calls pending, there will still be one second worth of calls pending after changing the period.
Infinite Delay and/or Period
If delay
is set to None
(infinity), the timer will stop firing. Note
that the timer is still in the Active state, and the correct way to restart
this timer is to reset the delay to a finite value.
If period
is set to None
instead, the timer will stop firing after the
next tick.
Examples
Periodic Timer
use r3::{kernel::{cfg::CfgBuilder, Timer, Kernel}, time::Duration};
const fn configure<System: Kernel>(b: &mut CfgBuilder<System>) -> Timer<System> {
Timer::build()
.delay(Duration::from_millis(70))
.period(Duration::from_millis(40))
.active(true)
.start(|_| dbg!())
.finish(b)
}
One-Shot Timer
use r3::{kernel::{cfg::CfgBuilder, Timer, Kernel}, time::Duration};
const fn configure<System: Kernel>(b: &mut CfgBuilder<System>) -> Timer<System> {
Timer::build()
.active(true)
.start(|_| dbg!())
.finish(b)
}
Reset the delay to schedule a call.
use r3::{kernel::{Timer, Kernel}, time::Duration};
fn sched<System: Kernel>(timer: Timer<System>) {
timer.set_delay(Some(Duration::from_millis(40))).unwrap();
}
Implementations
Construct a CfgTimerBuilder
to define a timer in a configuration
function.
Construct a Timer
from Id
.
Safety
The kernel can handle invalid IDs without a problem. However, the
constructed Timer
may point to an object that is not intended to be
manipulated except by its creator. This is usually prevented by making
Timer
an opaque handle, but this safeguard can be circumvented by
this method.
Start the timer (transition it into the Active state).
This method has no effect if the timer is already in the Active state.
Stop the timer (transition it into the Dormant state).
This method has no effect if the timer is already in the Dormant state.
Set the duration before the next tick.
If the timer is currently in the Dormant state, this method specifies the duration between the next activation and the first tick following the activation.
None
means infinity (the timer will never fire).
Set the timer period, which is a quantity to be added to the timer’s absolute arrival time on every tick.
None
means infinity.