Expand description
ztimer,where z stands for zero-overhead, provides an implementation of a near O(1) timer. It operates under the assumption that, in normal application usage, the number of timer expirations across different scenarios is typically limited. Therefore, there’s no need to support a huge number of different expires.
§Concept
The main concepts introduced in ztimer are AutoDropTimer
, Timer
, and Clock
.
-
An instance of
Timer
held in the application is an indirect reference to the real timer instance stored in theClock
. It holds a callback provided by the application, which will be invoked after a specified duration. ATimer
instance can be canceled before it expires. -
If a
Timer
instance is not canceled by the application but instead is dropped before expiring, nothing will happen. TheClock
will still call the callback when the underlying timer instance expires. The application needs to handle this case correctly. -
AutoDropTimer
is a wrapper ofTimer
with an additional benefit: when theAutoDropTimer
instance is dropped, it automatically cancels the underlying timer if it is not expired, preventing further callback invocations. -
The
Clock
class is used to group timers. An instance ofClock
can hold multiple or even all the timers within one process. The number of clocks in one process follows these restrictions:- At least one clock instance is required.
- Up to CPUs multiplied by 4 for collocated clocks.
- For non-collocated clocks, application can create as many as it wants, the library will return new or reused clock instances accordingly, it is transparent to the application.
Application shall create at least one Clock
instance before using the timer. A suggestion is to allocate several clocks based on functional groups.
§Example of use AutoDropTimer:
use std::thread::sleep;
use std::time::Duration;
use ztimer::{AutoDropTimer, Clock} ;// Only one of AutoDropTimer or Timer is needed in general
let clock = Clock::new(None).unwrap(); // One process can have one or a few number of clock instances
let t1 = AutoDropTimer::new(clock, Duration::from_secs(1), ||{println!("T1 expired");}, "t1".to_string()).unwrap();
let t2 = AutoDropTimer::new(clock, Duration::from_secs(10), ||{println!("T2 expired");}, "t2".to_string()).unwrap();
let mut t3 = AutoDropTimer::new(clock, Duration::from_secs(10), ||{println!("T3 expired");}, "t2".to_string()).unwrap();
assert_eq!(clock.len(), 3);
sleep(Duration::from_secs(2));// T1 expired
assert_eq!(clock.len(), 2);
{
let _t4 = t2;
}
// The timer t2 is moved to _t4, then dropped automatically without invoking timeout function.
assert_eq!(clock.len(), 1);
t3.cancel().unwrap();
assert_eq!(clock.len(), 0);
If an application needs to wait for the underlying timer thread before terminate the process, here is what the application shall execute #Example of waiting for underlying timer thread
use ztimer::Clock;
let _ = Clock::new(None);
//Before terminating:
let tjh = Clock::take_thread_handle();
Clock::terminate_for_process_exit();
let _ = tjh.unwrap().join();
Re-exports§
pub use error::MyError;
Modules§
Structs§
Type Aliases§
- NonBlock
Tick Bridge - NonBlockTickBridge: a call back to pass the Tick to the instance of clock. Application must provide one when colocated_timer is enabled At the end, normally it’s the receiving portion of application thread, application needs to call Clock::on_tick() to transfer the Tick event. As indicated by the name, the function for TickBridge CANNOT have any blocking operations.