aboutsummaryrefslogtreecommitdiff
path: root/embassy-time
diff options
context:
space:
mode:
authorDario Nieuwenhuis <[email protected]>2022-08-17 23:40:16 +0200
committerDario Nieuwenhuis <[email protected]>2022-08-18 01:22:30 +0200
commit5daa173ce4b153a532b4daa9e94c7a248231f25b (patch)
tree2ef0b4d6f9b1c02dac2589e7b57982c20cbc0e66 /embassy-time
parent1c5b54a4823d596db730eb476c3ab78110557214 (diff)
Split embassy-time from embassy-executor.
Diffstat (limited to 'embassy-time')
-rw-r--r--embassy-time/Cargo.toml54
-rw-r--r--embassy-time/src/delay.rs98
-rw-r--r--embassy-time/src/driver.rs174
-rw-r--r--embassy-time/src/driver_std.rs208
-rw-r--r--embassy-time/src/driver_wasm.rs134
-rw-r--r--embassy-time/src/duration.rs184
-rw-r--r--embassy-time/src/fmt.rs225
-rw-r--r--embassy-time/src/instant.rs159
-rw-r--r--embassy-time/src/lib.rs99
-rw-r--r--embassy-time/src/timer.rs158
10 files changed, 1493 insertions, 0 deletions
diff --git a/embassy-time/Cargo.toml b/embassy-time/Cargo.toml
new file mode 100644
index 000000000..161c101fe
--- /dev/null
+++ b/embassy-time/Cargo.toml
@@ -0,0 +1,54 @@
1[package]
2name = "embassy-time"
3version = "0.1.0"
4edition = "2021"
5
6
7[package.metadata.embassy_docs]
8src_base = "https://github.com/embassy-rs/embassy/blob/embassy-time-v$VERSION/embassy-time/src/"
9src_base_git = "https://github.com/embassy-rs/embassy/blob/$COMMIT/embassy-time/src/"
10features = ["nightly", "defmt", "unstable-traits", "std"]
11target = "x86_64-unknown-linux-gnu"
12
13[features]
14std = ["tick-1mhz"]
15wasm = ["dep:wasm-bindgen", "dep:js-sys", "dep:wasm-timer", "tick-1mhz"]
16
17# Enable nightly-only features
18nightly = ["embedded-hal-async"]
19
20# Implement embedded-hal 1.0 alpha and embedded-hal-async traits.
21# Implement embedded-hal-async traits if `nightly` is set as well.
22unstable-traits = ["embedded-hal-1"]
23
24# Display a timestamp of the number of seconds since startup next to defmt log messages
25# To use this you must have a time driver provided.
26defmt-timestamp-uptime = ["defmt"]
27
28# Set the `embassy_time` tick rate.
29# NOTE: This feature is only intended to be enabled by crates providing the time driver implementation.
30# If you're not writing your own driver, check the driver documentation to customize the tick rate.
31# If you're writing a driver and your tick rate is not listed here, please add it and send a PR!
32tick-32768hz = []
33tick-1000hz = []
34tick-1mhz = []
35tick-16mhz = []
36
37[dependencies]
38defmt = { version = "0.3", optional = true }
39log = { version = "0.4.14", optional = true }
40
41embedded-hal-02 = { package = "embedded-hal", version = "0.2.6" }
42embedded-hal-1 = { package = "embedded-hal", version = "1.0.0-alpha.8", optional = true}
43embedded-hal-async = { version = "0.1.0-alpha.1", optional = true}
44
45futures-util = { version = "0.3.17", default-features = false }
46embassy-macros = { version = "0.1.0", path = "../embassy-macros"}
47atomic-polyfill = "1.0.1"
48critical-section = "1.1"
49cfg-if = "1.0.0"
50
51# WASM dependencies
52wasm-bindgen = { version = "0.2.76", features = ["nightly"], optional = true }
53js-sys = { version = "0.3", optional = true }
54wasm-timer = { version = "0.2.5", optional = true } \ No newline at end of file
diff --git a/embassy-time/src/delay.rs b/embassy-time/src/delay.rs
new file mode 100644
index 000000000..d010fff98
--- /dev/null
+++ b/embassy-time/src/delay.rs
@@ -0,0 +1,98 @@
1use super::{Duration, Instant};
2
3/// Blocks for at least `duration`.
4pub fn block_for(duration: Duration) {
5 let expires_at = Instant::now() + duration;
6 while Instant::now() < expires_at {}
7}
8
9/// Type implementing async delays and blocking `embedded-hal` delays.
10///
11/// The delays are implemented in a "best-effort" way, meaning that the cpu will block for at least
12/// the amount provided, but accuracy can be affected by many factors, including interrupt usage.
13/// Make sure to use a suitable tick rate for your use case. The tick rate is defined by the currently
14/// active driver.
15pub struct Delay;
16
17#[cfg(feature = "unstable-traits")]
18mod eh1 {
19 use super::*;
20
21 impl embedded_hal_1::delay::blocking::DelayUs for Delay {
22 type Error = core::convert::Infallible;
23
24 fn delay_us(&mut self, us: u32) -> Result<(), Self::Error> {
25 Ok(block_for(Duration::from_micros(us as u64)))
26 }
27
28 fn delay_ms(&mut self, ms: u32) -> Result<(), Self::Error> {
29 Ok(block_for(Duration::from_millis(ms as u64)))
30 }
31 }
32}
33
34cfg_if::cfg_if! {
35 if #[cfg(all(feature = "unstable-traits", feature = "nightly"))] {
36 use crate::Timer;
37 use core::future::Future;
38 use futures_util::FutureExt;
39
40 impl embedded_hal_async::delay::DelayUs for Delay {
41 type Error = core::convert::Infallible;
42
43 type DelayUsFuture<'a> = impl Future<Output = Result<(), Self::Error>> + 'a where Self: 'a;
44
45 fn delay_us(&mut self, micros: u32) -> Self::DelayUsFuture<'_> {
46 Timer::after(Duration::from_micros(micros as _)).map(Ok)
47 }
48
49 type DelayMsFuture<'a> = impl Future<Output = Result<(), Self::Error>> + 'a where Self: 'a;
50
51 fn delay_ms(&mut self, millis: u32) -> Self::DelayMsFuture<'_> {
52 Timer::after(Duration::from_millis(millis as _)).map(Ok)
53 }
54 }
55 }
56}
57
58mod eh02 {
59 use embedded_hal_02::blocking::delay::{DelayMs, DelayUs};
60
61 use super::*;
62
63 impl DelayMs<u8> for Delay {
64 fn delay_ms(&mut self, ms: u8) {
65 block_for(Duration::from_millis(ms as u64))
66 }
67 }
68
69 impl DelayMs<u16> for Delay {
70 fn delay_ms(&mut self, ms: u16) {
71 block_for(Duration::from_millis(ms as u64))
72 }
73 }
74
75 impl DelayMs<u32> for Delay {
76 fn delay_ms(&mut self, ms: u32) {
77 block_for(Duration::from_millis(ms as u64))
78 }
79 }
80
81 impl DelayUs<u8> for Delay {
82 fn delay_us(&mut self, us: u8) {
83 block_for(Duration::from_micros(us as u64))
84 }
85 }
86
87 impl DelayUs<u16> for Delay {
88 fn delay_us(&mut self, us: u16) {
89 block_for(Duration::from_micros(us as u64))
90 }
91 }
92
93 impl DelayUs<u32> for Delay {
94 fn delay_us(&mut self, us: u32) {
95 block_for(Duration::from_micros(us as u64))
96 }
97 }
98}
diff --git a/embassy-time/src/driver.rs b/embassy-time/src/driver.rs
new file mode 100644
index 000000000..216b27408
--- /dev/null
+++ b/embassy-time/src/driver.rs
@@ -0,0 +1,174 @@
1//! Time driver interface
2//!
3//! This module defines the interface a driver needs to implement to power the `embassy_time` module.
4//!
5//! # Implementing a driver
6//!
7//! - Define a struct `MyDriver`
8//! - Implement [`Driver`] for it
9//! - Register it as the global driver with [`time_driver_impl`].
10//! - Enable the Cargo features `embassy-executor/time` and one of `embassy-time/tick-*` corresponding to the
11//! tick rate of your driver.
12//!
13//! If you wish to make the tick rate configurable by the end user, you should do so by exposing your own
14//! Cargo features and having each enable the corresponding `embassy-time/tick-*`.
15//!
16//! # Linkage details
17//!
18//! Instead of the usual "trait + generic params" approach, calls from embassy to the driver are done via `extern` functions.
19//!
20//! `embassy` internally defines the driver functions as `extern "Rust" { fn _embassy_time_now() -> u64; }` and calls them.
21//! The driver crate defines the functions as `#[no_mangle] fn _embassy_time_now() -> u64`. The linker will resolve the
22//! calls from the `embassy` crate to call into the driver crate.
23//!
24//! If there is none or multiple drivers in the crate tree, linking will fail.
25//!
26//! This method has a few key advantages for something as foundational as timekeeping:
27//!
28//! - The time driver is available everywhere easily, without having to thread the implementation
29//! through generic parameters. This is especially helpful for libraries.
30//! - It means comparing `Instant`s will always make sense: if there were multiple drivers
31//! active, one could compare an `Instant` from driver A to an `Instant` from driver B, which
32//! would yield incorrect results.
33//!
34//! # Example
35//!
36//! ```
37//! use embassy_time::driver::{Driver, AlarmHandle};
38//!
39//! struct MyDriver{}; // not public!
40//! embassy_time::time_driver_impl!(static DRIVER: MyDriver = MyDriver{});
41//!
42//! impl Driver for MyDriver {
43//! fn now(&self) -> u64 {
44//! todo!()
45//! }
46//! unsafe fn allocate_alarm(&self) -> Option<AlarmHandle> {
47//! todo!()
48//! }
49//! fn set_alarm_callback(&self, alarm: AlarmHandle, callback: fn(*mut ()), ctx: *mut ()) {
50//! todo!()
51//! }
52//! fn set_alarm(&self, alarm: AlarmHandle, timestamp: u64) {
53//! todo!()
54//! }
55//! }
56//! ```
57
58/// Alarm handle, assigned by the driver.
59#[derive(Clone, Copy)]
60pub struct AlarmHandle {
61 id: u8,
62}
63
64impl AlarmHandle {
65 /// Create an AlarmHandle
66 ///
67 /// Safety: May only be called by the current global Driver impl.
68 /// The impl is allowed to rely on the fact that all `AlarmHandle` instances
69 /// are created by itself in unsafe code (e.g. indexing operations)
70 pub unsafe fn new(id: u8) -> Self {
71 Self { id }
72 }
73
74 /// Get the ID of the AlarmHandle.
75 pub fn id(&self) -> u8 {
76 self.id
77 }
78}
79
80/// Time driver
81pub trait Driver: Send + Sync + 'static {
82 /// Return the current timestamp in ticks.
83 ///
84 /// Implementations MUST ensure that:
85 /// - This is guaranteed to be monotonic, i.e. a call to now() will always return
86 /// a greater or equal value than earler calls. Time can't "roll backwards".
87 /// - It "never" overflows. It must not overflow in a sufficiently long time frame, say
88 /// in 10_000 years (Human civilization is likely to already have self-destructed
89 /// 10_000 years from now.). This means if your hardware only has 16bit/32bit timers
90 /// you MUST extend them to 64-bit, for example by counting overflows in software,
91 /// or chaining multiple timers together.
92 fn now(&self) -> u64;
93
94 /// Try allocating an alarm handle. Returns None if no alarms left.
95 /// Initially the alarm has no callback set, and a null `ctx` pointer.
96 ///
97 /// # Safety
98 /// It is UB to make the alarm fire before setting a callback.
99 unsafe fn allocate_alarm(&self) -> Option<AlarmHandle>;
100
101 /// Sets the callback function to be called when the alarm triggers.
102 /// The callback may be called from any context (interrupt or thread mode).
103 fn set_alarm_callback(&self, alarm: AlarmHandle, callback: fn(*mut ()), ctx: *mut ());
104
105 /// Sets an alarm at the given timestamp. When the current timestamp reaches the alarm
106 /// timestamp, the provided callback function will be called.
107 ///
108 /// If `timestamp` is already in the past, the alarm callback must be immediately fired.
109 /// In this case, it is allowed (but not mandatory) to call the alarm callback synchronously from `set_alarm`.
110 ///
111 /// When callback is called, it is guaranteed that now() will return a value greater or equal than timestamp.
112 ///
113 /// Only one alarm can be active at a time for each AlarmHandle. This overwrites any previously-set alarm if any.
114 fn set_alarm(&self, alarm: AlarmHandle, timestamp: u64);
115}
116
117extern "Rust" {
118 fn _embassy_time_now() -> u64;
119 fn _embassy_time_allocate_alarm() -> Option<AlarmHandle>;
120 fn _embassy_time_set_alarm_callback(alarm: AlarmHandle, callback: fn(*mut ()), ctx: *mut ());
121 fn _embassy_time_set_alarm(alarm: AlarmHandle, timestamp: u64);
122}
123
124/// See [`Driver::now`]
125pub fn now() -> u64 {
126 unsafe { _embassy_time_now() }
127}
128
129/// See [`Driver::allocate_alarm`]
130///
131/// Safety: it is UB to make the alarm fire before setting a callback.
132pub unsafe fn allocate_alarm() -> Option<AlarmHandle> {
133 _embassy_time_allocate_alarm()
134}
135
136/// See [`Driver::set_alarm_callback`]
137pub fn set_alarm_callback(alarm: AlarmHandle, callback: fn(*mut ()), ctx: *mut ()) {
138 unsafe { _embassy_time_set_alarm_callback(alarm, callback, ctx) }
139}
140
141/// See [`Driver::set_alarm`]
142pub fn set_alarm(alarm: AlarmHandle, timestamp: u64) {
143 unsafe { _embassy_time_set_alarm(alarm, timestamp) }
144}
145
146/// Set the time Driver implementation.
147///
148/// See the module documentation for an example.
149#[macro_export]
150macro_rules! time_driver_impl {
151 (static $name:ident: $t: ty = $val:expr) => {
152 static $name: $t = $val;
153
154 #[no_mangle]
155 fn _embassy_time_now() -> u64 {
156 <$t as $crate::driver::Driver>::now(&$name)
157 }
158
159 #[no_mangle]
160 unsafe fn _embassy_time_allocate_alarm() -> Option<$crate::driver::AlarmHandle> {
161 <$t as $crate::driver::Driver>::allocate_alarm(&$name)
162 }
163
164 #[no_mangle]
165 fn _embassy_time_set_alarm_callback(alarm: $crate::driver::AlarmHandle, callback: fn(*mut ()), ctx: *mut ()) {
166 <$t as $crate::driver::Driver>::set_alarm_callback(&$name, alarm, callback, ctx)
167 }
168
169 #[no_mangle]
170 fn _embassy_time_set_alarm(alarm: $crate::driver::AlarmHandle, timestamp: u64) {
171 <$t as $crate::driver::Driver>::set_alarm(&$name, alarm, timestamp)
172 }
173 };
174}
diff --git a/embassy-time/src/driver_std.rs b/embassy-time/src/driver_std.rs
new file mode 100644
index 000000000..2ddb2e604
--- /dev/null
+++ b/embassy-time/src/driver_std.rs
@@ -0,0 +1,208 @@
1use std::cell::UnsafeCell;
2use std::mem::MaybeUninit;
3use std::sync::{Condvar, Mutex, Once};
4use std::time::{Duration as StdDuration, Instant as StdInstant};
5use std::{mem, ptr, thread};
6
7use atomic_polyfill::{AtomicU8, Ordering};
8
9use crate::driver::{AlarmHandle, Driver};
10
11const ALARM_COUNT: usize = 4;
12
13struct AlarmState {
14 timestamp: u64,
15
16 // This is really a Option<(fn(*mut ()), *mut ())>
17 // but fn pointers aren't allowed in const yet
18 callback: *const (),
19 ctx: *mut (),
20}
21
22unsafe impl Send for AlarmState {}
23
24impl AlarmState {
25 const fn new() -> Self {
26 Self {
27 timestamp: u64::MAX,
28 callback: ptr::null(),
29 ctx: ptr::null_mut(),
30 }
31 }
32}
33
34struct TimeDriver {
35 alarm_count: AtomicU8,
36
37 once: Once,
38 alarms: UninitCell<Mutex<[AlarmState; ALARM_COUNT]>>,
39 zero_instant: UninitCell<StdInstant>,
40 signaler: UninitCell<Signaler>,
41}
42
43const ALARM_NEW: AlarmState = AlarmState::new();
44crate::time_driver_impl!(static DRIVER: TimeDriver = TimeDriver {
45 alarm_count: AtomicU8::new(0),
46
47 once: Once::new(),
48 alarms: UninitCell::uninit(),
49 zero_instant: UninitCell::uninit(),
50 signaler: UninitCell::uninit(),
51});
52
53impl TimeDriver {
54 fn init(&self) {
55 self.once.call_once(|| unsafe {
56 self.alarms.write(Mutex::new([ALARM_NEW; ALARM_COUNT]));
57 self.zero_instant.write(StdInstant::now());
58 self.signaler.write(Signaler::new());
59
60 thread::spawn(Self::alarm_thread);
61 });
62 }
63
64 fn alarm_thread() {
65 let zero = unsafe { DRIVER.zero_instant.read() };
66 loop {
67 let now = DRIVER.now();
68
69 let mut next_alarm = u64::MAX;
70 {
71 let alarms = &mut *unsafe { DRIVER.alarms.as_ref() }.lock().unwrap();
72 for alarm in alarms {
73 if alarm.timestamp <= now {
74 alarm.timestamp = u64::MAX;
75
76 // Call after clearing alarm, so the callback can set another alarm.
77
78 // safety:
79 // - we can ignore the possiblity of `f` being unset (null) because of the safety contract of `allocate_alarm`.
80 // - other than that we only store valid function pointers into alarm.callback
81 let f: fn(*mut ()) = unsafe { mem::transmute(alarm.callback) };
82 f(alarm.ctx);
83 } else {
84 next_alarm = next_alarm.min(alarm.timestamp);
85 }
86 }
87 }
88
89 // Ensure we don't overflow
90 let until = zero
91 .checked_add(StdDuration::from_micros(next_alarm))
92 .unwrap_or_else(|| StdInstant::now() + StdDuration::from_secs(1));
93
94 unsafe { DRIVER.signaler.as_ref() }.wait_until(until);
95 }
96 }
97}
98
99impl Driver for TimeDriver {
100 fn now(&self) -> u64 {
101 self.init();
102
103 let zero = unsafe { self.zero_instant.read() };
104 StdInstant::now().duration_since(zero).as_micros() as u64
105 }
106
107 unsafe fn allocate_alarm(&self) -> Option<AlarmHandle> {
108 let id = self.alarm_count.fetch_update(Ordering::AcqRel, Ordering::Acquire, |x| {
109 if x < ALARM_COUNT as u8 {
110 Some(x + 1)
111 } else {
112 None
113 }
114 });
115
116 match id {
117 Ok(id) => Some(AlarmHandle::new(id)),
118 Err(_) => None,
119 }
120 }
121
122 fn set_alarm_callback(&self, alarm: AlarmHandle, callback: fn(*mut ()), ctx: *mut ()) {
123 self.init();
124 let mut alarms = unsafe { self.alarms.as_ref() }.lock().unwrap();
125 let alarm = &mut alarms[alarm.id() as usize];
126 alarm.callback = callback as *const ();
127 alarm.ctx = ctx;
128 }
129
130 fn set_alarm(&self, alarm: AlarmHandle, timestamp: u64) {
131 self.init();
132 let mut alarms = unsafe { self.alarms.as_ref() }.lock().unwrap();
133 let alarm = &mut alarms[alarm.id() as usize];
134 alarm.timestamp = timestamp;
135 unsafe { self.signaler.as_ref() }.signal();
136 }
137}
138
139struct Signaler {
140 mutex: Mutex<bool>,
141 condvar: Condvar,
142}
143
144impl Signaler {
145 fn new() -> Self {
146 Self {
147 mutex: Mutex::new(false),
148 condvar: Condvar::new(),
149 }
150 }
151
152 fn wait_until(&self, until: StdInstant) {
153 let mut signaled = self.mutex.lock().unwrap();
154 while !*signaled {
155 let now = StdInstant::now();
156
157 if now >= until {
158 break;
159 }
160
161 let dur = until - now;
162 let (signaled2, timeout) = self.condvar.wait_timeout(signaled, dur).unwrap();
163 signaled = signaled2;
164 if timeout.timed_out() {
165 break;
166 }
167 }
168 *signaled = false;
169 }
170
171 fn signal(&self) {
172 let mut signaled = self.mutex.lock().unwrap();
173 *signaled = true;
174 self.condvar.notify_one();
175 }
176}
177
178pub(crate) struct UninitCell<T>(MaybeUninit<UnsafeCell<T>>);
179unsafe impl<T> Send for UninitCell<T> {}
180unsafe impl<T> Sync for UninitCell<T> {}
181
182impl<T> UninitCell<T> {
183 pub const fn uninit() -> Self {
184 Self(MaybeUninit::uninit())
185 }
186
187 pub unsafe fn as_ptr(&self) -> *const T {
188 (*self.0.as_ptr()).get()
189 }
190
191 pub unsafe fn as_mut_ptr(&self) -> *mut T {
192 (*self.0.as_ptr()).get()
193 }
194
195 pub unsafe fn as_ref(&self) -> &T {
196 &*self.as_ptr()
197 }
198
199 pub unsafe fn write(&self, val: T) {
200 ptr::write(self.as_mut_ptr(), val)
201 }
202}
203
204impl<T: Copy> UninitCell<T> {
205 pub unsafe fn read(&self) -> T {
206 ptr::read(self.as_mut_ptr())
207 }
208}
diff --git a/embassy-time/src/driver_wasm.rs b/embassy-time/src/driver_wasm.rs
new file mode 100644
index 000000000..e4497e6a2
--- /dev/null
+++ b/embassy-time/src/driver_wasm.rs
@@ -0,0 +1,134 @@
1use std::cell::UnsafeCell;
2use std::mem::MaybeUninit;
3use std::ptr;
4use std::sync::{Mutex, Once};
5
6use atomic_polyfill::{AtomicU8, Ordering};
7use wasm_bindgen::prelude::*;
8use wasm_timer::Instant as StdInstant;
9
10use crate::driver::{AlarmHandle, Driver};
11
12const ALARM_COUNT: usize = 4;
13
14struct AlarmState {
15 token: Option<f64>,
16 closure: Option<Closure<dyn FnMut() + 'static>>,
17}
18
19unsafe impl Send for AlarmState {}
20
21impl AlarmState {
22 const fn new() -> Self {
23 Self {
24 token: None,
25 closure: None,
26 }
27 }
28}
29
30#[wasm_bindgen]
31extern "C" {
32 fn setTimeout(closure: &Closure<dyn FnMut()>, millis: u32) -> f64;
33 fn clearTimeout(token: f64);
34}
35
36struct TimeDriver {
37 alarm_count: AtomicU8,
38
39 once: Once,
40 alarms: UninitCell<Mutex<[AlarmState; ALARM_COUNT]>>,
41 zero_instant: UninitCell<StdInstant>,
42}
43
44const ALARM_NEW: AlarmState = AlarmState::new();
45crate::time_driver_impl!(static DRIVER: TimeDriver = TimeDriver {
46 alarm_count: AtomicU8::new(0),
47 once: Once::new(),
48 alarms: UninitCell::uninit(),
49 zero_instant: UninitCell::uninit(),
50});
51
52impl TimeDriver {
53 fn init(&self) {
54 self.once.call_once(|| unsafe {
55 self.alarms.write(Mutex::new([ALARM_NEW; ALARM_COUNT]));
56 self.zero_instant.write(StdInstant::now());
57 });
58 }
59}
60
61impl Driver for TimeDriver {
62 fn now(&self) -> u64 {
63 self.init();
64
65 let zero = unsafe { self.zero_instant.read() };
66 StdInstant::now().duration_since(zero).as_micros() as u64
67 }
68
69 unsafe fn allocate_alarm(&self) -> Option<AlarmHandle> {
70 let id = self.alarm_count.fetch_update(Ordering::AcqRel, Ordering::Acquire, |x| {
71 if x < ALARM_COUNT as u8 {
72 Some(x + 1)
73 } else {
74 None
75 }
76 });
77
78 match id {
79 Ok(id) => Some(AlarmHandle::new(id)),
80 Err(_) => None,
81 }
82 }
83
84 fn set_alarm_callback(&self, alarm: AlarmHandle, callback: fn(*mut ()), ctx: *mut ()) {
85 self.init();
86 let mut alarms = unsafe { self.alarms.as_ref() }.lock().unwrap();
87 let alarm = &mut alarms[alarm.id() as usize];
88 alarm.closure.replace(Closure::new(move || {
89 callback(ctx);
90 }));
91 }
92
93 fn set_alarm(&self, alarm: AlarmHandle, timestamp: u64) {
94 self.init();
95 let mut alarms = unsafe { self.alarms.as_ref() }.lock().unwrap();
96 let alarm = &mut alarms[alarm.id() as usize];
97 let timeout = (timestamp - self.now()) as u32;
98 if let Some(token) = alarm.token {
99 clearTimeout(token);
100 }
101 alarm.token = Some(setTimeout(alarm.closure.as_ref().unwrap(), timeout / 1000));
102 }
103}
104
105pub(crate) struct UninitCell<T>(MaybeUninit<UnsafeCell<T>>);
106unsafe impl<T> Send for UninitCell<T> {}
107unsafe impl<T> Sync for UninitCell<T> {}
108
109impl<T> UninitCell<T> {
110 pub const fn uninit() -> Self {
111 Self(MaybeUninit::uninit())
112 }
113 unsafe fn as_ptr(&self) -> *const T {
114 (*self.0.as_ptr()).get()
115 }
116
117 pub unsafe fn as_mut_ptr(&self) -> *mut T {
118 (*self.0.as_ptr()).get()
119 }
120
121 pub unsafe fn as_ref(&self) -> &T {
122 &*self.as_ptr()
123 }
124
125 pub unsafe fn write(&self, val: T) {
126 ptr::write(self.as_mut_ptr(), val)
127 }
128}
129
130impl<T: Copy> UninitCell<T> {
131 pub unsafe fn read(&self) -> T {
132 ptr::read(self.as_mut_ptr())
133 }
134}
diff --git a/embassy-time/src/duration.rs b/embassy-time/src/duration.rs
new file mode 100644
index 000000000..dc4f16bd4
--- /dev/null
+++ b/embassy-time/src/duration.rs
@@ -0,0 +1,184 @@
1use core::fmt;
2use core::ops::{Add, AddAssign, Div, DivAssign, Mul, MulAssign, Sub, SubAssign};
3
4use super::{GCD_1K, GCD_1M, TICKS_PER_SECOND};
5
6#[derive(Debug, Default, Copy, Clone, PartialEq, Eq, PartialOrd, Ord)]
7#[cfg_attr(feature = "defmt", derive(defmt::Format))]
8/// Represents the difference between two [Instant](struct.Instant.html)s
9pub struct Duration {
10 pub(crate) ticks: u64,
11}
12
13impl Duration {
14 /// The smallest value that can be represented by the `Duration` type.
15 pub const MIN: Duration = Duration { ticks: u64::MIN };
16 /// The largest value that can be represented by the `Duration` type.
17 pub const MAX: Duration = Duration { ticks: u64::MAX };
18
19 /// Tick count of the `Duration`.
20 pub const fn as_ticks(&self) -> u64 {
21 self.ticks
22 }
23
24 /// Convert the `Duration` to seconds, rounding down.
25 pub const fn as_secs(&self) -> u64 {
26 self.ticks / TICKS_PER_SECOND
27 }
28
29 /// Convert the `Duration` to milliseconds, rounding down.
30 pub const fn as_millis(&self) -> u64 {
31 self.ticks * (1000 / GCD_1K) / (TICKS_PER_SECOND / GCD_1K)
32 }
33
34 /// Convert the `Duration` to microseconds, rounding down.
35 pub const fn as_micros(&self) -> u64 {
36 self.ticks * (1_000_000 / GCD_1M) / (TICKS_PER_SECOND / GCD_1M)
37 }
38
39 /// Creates a duration from the specified number of clock ticks
40 pub const fn from_ticks(ticks: u64) -> Duration {
41 Duration { ticks }
42 }
43
44 /// Creates a duration from the specified number of seconds, rounding up.
45 pub const fn from_secs(secs: u64) -> Duration {
46 Duration {
47 ticks: secs * TICKS_PER_SECOND,
48 }
49 }
50
51 /// Creates a duration from the specified number of milliseconds, rounding up.
52 pub const fn from_millis(millis: u64) -> Duration {
53 Duration {
54 ticks: div_ceil(millis * (TICKS_PER_SECOND / GCD_1K), 1000 / GCD_1K),
55 }
56 }
57
58 /// Creates a duration from the specified number of microseconds, rounding up.
59 /// NOTE: Delays this small may be inaccurate.
60 pub const fn from_micros(micros: u64) -> Duration {
61 Duration {
62 ticks: div_ceil(micros * (TICKS_PER_SECOND / GCD_1M), 1_000_000 / GCD_1M),
63 }
64 }
65
66 /// Creates a duration from the specified number of seconds, rounding down.
67 pub const fn from_secs_floor(secs: u64) -> Duration {
68 Duration {
69 ticks: secs * TICKS_PER_SECOND,
70 }
71 }
72
73 /// Creates a duration from the specified number of milliseconds, rounding down.
74 pub const fn from_millis_floor(millis: u64) -> Duration {
75 Duration {
76 ticks: millis * (TICKS_PER_SECOND / GCD_1K) / (1000 / GCD_1K),
77 }
78 }
79
80 /// Creates a duration from the specified number of microseconds, rounding down.
81 /// NOTE: Delays this small may be inaccurate.
82 pub const fn from_micros_floor(micros: u64) -> Duration {
83 Duration {
84 ticks: micros * (TICKS_PER_SECOND / GCD_1M) / (1_000_000 / GCD_1M),
85 }
86 }
87
88 /// Adds one Duration to another, returning a new Duration or None in the event of an overflow.
89 pub fn checked_add(self, rhs: Duration) -> Option<Duration> {
90 self.ticks.checked_add(rhs.ticks).map(|ticks| Duration { ticks })
91 }
92
93 /// Subtracts one Duration to another, returning a new Duration or None in the event of an overflow.
94 pub fn checked_sub(self, rhs: Duration) -> Option<Duration> {
95 self.ticks.checked_sub(rhs.ticks).map(|ticks| Duration { ticks })
96 }
97
98 /// Multiplies one Duration by a scalar u32, returning a new Duration or None in the event of an overflow.
99 pub fn checked_mul(self, rhs: u32) -> Option<Duration> {
100 self.ticks.checked_mul(rhs as _).map(|ticks| Duration { ticks })
101 }
102
103 /// Divides one Duration a scalar u32, returning a new Duration or None in the event of an overflow.
104 pub fn checked_div(self, rhs: u32) -> Option<Duration> {
105 self.ticks.checked_div(rhs as _).map(|ticks| Duration { ticks })
106 }
107}
108
109impl Add for Duration {
110 type Output = Duration;
111
112 fn add(self, rhs: Duration) -> Duration {
113 self.checked_add(rhs).expect("overflow when adding durations")
114 }
115}
116
117impl AddAssign for Duration {
118 fn add_assign(&mut self, rhs: Duration) {
119 *self = *self + rhs;
120 }
121}
122
123impl Sub for Duration {
124 type Output = Duration;
125
126 fn sub(self, rhs: Duration) -> Duration {
127 self.checked_sub(rhs).expect("overflow when subtracting durations")
128 }
129}
130
131impl SubAssign for Duration {
132 fn sub_assign(&mut self, rhs: Duration) {
133 *self = *self - rhs;
134 }
135}
136
137impl Mul<u32> for Duration {
138 type Output = Duration;
139
140 fn mul(self, rhs: u32) -> Duration {
141 self.checked_mul(rhs)
142 .expect("overflow when multiplying duration by scalar")
143 }
144}
145
146impl Mul<Duration> for u32 {
147 type Output = Duration;
148
149 fn mul(self, rhs: Duration) -> Duration {
150 rhs * self
151 }
152}
153
154impl MulAssign<u32> for Duration {
155 fn mul_assign(&mut self, rhs: u32) {
156 *self = *self * rhs;
157 }
158}
159
160impl Div<u32> for Duration {
161 type Output = Duration;
162
163 fn div(self, rhs: u32) -> Duration {
164 self.checked_div(rhs)
165 .expect("divide by zero error when dividing duration by scalar")
166 }
167}
168
169impl DivAssign<u32> for Duration {
170 fn div_assign(&mut self, rhs: u32) {
171 *self = *self / rhs;
172 }
173}
174
175impl<'a> fmt::Display for Duration {
176 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
177 write!(f, "{} ticks", self.ticks)
178 }
179}
180
181#[inline]
182const fn div_ceil(num: u64, den: u64) -> u64 {
183 (num + den - 1) / den
184}
diff --git a/embassy-time/src/fmt.rs b/embassy-time/src/fmt.rs
new file mode 100644
index 000000000..066970813
--- /dev/null
+++ b/embassy-time/src/fmt.rs
@@ -0,0 +1,225 @@
1#![macro_use]
2#![allow(unused_macros)]
3
4#[cfg(all(feature = "defmt", feature = "log"))]
5compile_error!("You may not enable both `defmt` and `log` features.");
6
7macro_rules! assert {
8 ($($x:tt)*) => {
9 {
10 #[cfg(not(feature = "defmt"))]
11 ::core::assert!($($x)*);
12 #[cfg(feature = "defmt")]
13 ::defmt::assert!($($x)*);
14 }
15 };
16}
17
18macro_rules! assert_eq {
19 ($($x:tt)*) => {
20 {
21 #[cfg(not(feature = "defmt"))]
22 ::core::assert_eq!($($x)*);
23 #[cfg(feature = "defmt")]
24 ::defmt::assert_eq!($($x)*);
25 }
26 };
27}
28
29macro_rules! assert_ne {
30 ($($x:tt)*) => {
31 {
32 #[cfg(not(feature = "defmt"))]
33 ::core::assert_ne!($($x)*);
34 #[cfg(feature = "defmt")]
35 ::defmt::assert_ne!($($x)*);
36 }
37 };
38}
39
40macro_rules! debug_assert {
41 ($($x:tt)*) => {
42 {
43 #[cfg(not(feature = "defmt"))]
44 ::core::debug_assert!($($x)*);
45 #[cfg(feature = "defmt")]
46 ::defmt::debug_assert!($($x)*);
47 }
48 };
49}
50
51macro_rules! debug_assert_eq {
52 ($($x:tt)*) => {
53 {
54 #[cfg(not(feature = "defmt"))]
55 ::core::debug_assert_eq!($($x)*);
56 #[cfg(feature = "defmt")]
57 ::defmt::debug_assert_eq!($($x)*);
58 }
59 };
60}
61
62macro_rules! debug_assert_ne {
63 ($($x:tt)*) => {
64 {
65 #[cfg(not(feature = "defmt"))]
66 ::core::debug_assert_ne!($($x)*);
67 #[cfg(feature = "defmt")]
68 ::defmt::debug_assert_ne!($($x)*);
69 }
70 };
71}
72
73macro_rules! todo {
74 ($($x:tt)*) => {
75 {
76 #[cfg(not(feature = "defmt"))]
77 ::core::todo!($($x)*);
78 #[cfg(feature = "defmt")]
79 ::defmt::todo!($($x)*);
80 }
81 };
82}
83
84macro_rules! unreachable {
85 ($($x:tt)*) => {
86 {
87 #[cfg(not(feature = "defmt"))]
88 ::core::unreachable!($($x)*);
89 #[cfg(feature = "defmt")]
90 ::defmt::unreachable!($($x)*);
91 }
92 };
93}
94
95macro_rules! panic {
96 ($($x:tt)*) => {
97 {
98 #[cfg(not(feature = "defmt"))]
99 ::core::panic!($($x)*);
100 #[cfg(feature = "defmt")]
101 ::defmt::panic!($($x)*);
102 }
103 };
104}
105
106macro_rules! trace {
107 ($s:literal $(, $x:expr)* $(,)?) => {
108 {
109 #[cfg(feature = "log")]
110 ::log::trace!($s $(, $x)*);
111 #[cfg(feature = "defmt")]
112 ::defmt::trace!($s $(, $x)*);
113 #[cfg(not(any(feature = "log", feature="defmt")))]
114 let _ = ($( & $x ),*);
115 }
116 };
117}
118
119macro_rules! debug {
120 ($s:literal $(, $x:expr)* $(,)?) => {
121 {
122 #[cfg(feature = "log")]
123 ::log::debug!($s $(, $x)*);
124 #[cfg(feature = "defmt")]
125 ::defmt::debug!($s $(, $x)*);
126 #[cfg(not(any(feature = "log", feature="defmt")))]
127 let _ = ($( & $x ),*);
128 }
129 };
130}
131
132macro_rules! info {
133 ($s:literal $(, $x:expr)* $(,)?) => {
134 {
135 #[cfg(feature = "log")]
136 ::log::info!($s $(, $x)*);
137 #[cfg(feature = "defmt")]
138 ::defmt::info!($s $(, $x)*);
139 #[cfg(not(any(feature = "log", feature="defmt")))]
140 let _ = ($( & $x ),*);
141 }
142 };
143}
144
145macro_rules! warn {
146 ($s:literal $(, $x:expr)* $(,)?) => {
147 {
148 #[cfg(feature = "log")]
149 ::log::warn!($s $(, $x)*);
150 #[cfg(feature = "defmt")]
151 ::defmt::warn!($s $(, $x)*);
152 #[cfg(not(any(feature = "log", feature="defmt")))]
153 let _ = ($( & $x ),*);
154 }
155 };
156}
157
158macro_rules! error {
159 ($s:literal $(, $x:expr)* $(,)?) => {
160 {
161 #[cfg(feature = "log")]
162 ::log::error!($s $(, $x)*);
163 #[cfg(feature = "defmt")]
164 ::defmt::error!($s $(, $x)*);
165 #[cfg(not(any(feature = "log", feature="defmt")))]
166 let _ = ($( & $x ),*);
167 }
168 };
169}
170
171#[cfg(feature = "defmt")]
172macro_rules! unwrap {
173 ($($x:tt)*) => {
174 ::defmt::unwrap!($($x)*)
175 };
176}
177
178#[cfg(not(feature = "defmt"))]
179macro_rules! unwrap {
180 ($arg:expr) => {
181 match $crate::fmt::Try::into_result($arg) {
182 ::core::result::Result::Ok(t) => t,
183 ::core::result::Result::Err(e) => {
184 ::core::panic!("unwrap of `{}` failed: {:?}", ::core::stringify!($arg), e);
185 }
186 }
187 };
188 ($arg:expr, $($msg:expr),+ $(,)? ) => {
189 match $crate::fmt::Try::into_result($arg) {
190 ::core::result::Result::Ok(t) => t,
191 ::core::result::Result::Err(e) => {
192 ::core::panic!("unwrap of `{}` failed: {}: {:?}", ::core::stringify!($arg), ::core::format_args!($($msg,)*), e);
193 }
194 }
195 }
196}
197
198#[derive(Debug, Copy, Clone, Eq, PartialEq)]
199pub struct NoneError;
200
201pub trait Try {
202 type Ok;
203 type Error;
204 fn into_result(self) -> Result<Self::Ok, Self::Error>;
205}
206
207impl<T> Try for Option<T> {
208 type Ok = T;
209 type Error = NoneError;
210
211 #[inline]
212 fn into_result(self) -> Result<T, NoneError> {
213 self.ok_or(NoneError)
214 }
215}
216
217impl<T, E> Try for Result<T, E> {
218 type Ok = T;
219 type Error = E;
220
221 #[inline]
222 fn into_result(self) -> Self {
223 self
224 }
225}
diff --git a/embassy-time/src/instant.rs b/embassy-time/src/instant.rs
new file mode 100644
index 000000000..6a4925f47
--- /dev/null
+++ b/embassy-time/src/instant.rs
@@ -0,0 +1,159 @@
1use core::fmt;
2use core::ops::{Add, AddAssign, Sub, SubAssign};
3
4use super::{driver, Duration, GCD_1K, GCD_1M, TICKS_PER_SECOND};
5
6#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord)]
7#[cfg_attr(feature = "defmt", derive(defmt::Format))]
8/// An Instant in time, based on the MCU's clock ticks since startup.
9pub struct Instant {
10 ticks: u64,
11}
12
13impl Instant {
14 /// The smallest (earliest) value that can be represented by the `Instant` type.
15 pub const MIN: Instant = Instant { ticks: u64::MIN };
16 /// The largest (latest) value that can be represented by the `Instant` type.
17 pub const MAX: Instant = Instant { ticks: u64::MAX };
18
19 /// Returns an Instant representing the current time.
20 pub fn now() -> Instant {
21 Instant { ticks: driver::now() }
22 }
23
24 /// Create an Instant from a tick count since system boot.
25 pub const fn from_ticks(ticks: u64) -> Self {
26 Self { ticks }
27 }
28
29 /// Create an Instant from a microsecond count since system boot.
30 pub const fn from_micros(micros: u64) -> Self {
31 Self {
32 ticks: micros * (TICKS_PER_SECOND / GCD_1M) / (1_000_000 / GCD_1M),
33 }
34 }
35
36 /// Create an Instant from a millisecond count since system boot.
37 pub const fn from_millis(millis: u64) -> Self {
38 Self {
39 ticks: millis * (TICKS_PER_SECOND / GCD_1K) / (1000 / GCD_1K),
40 }
41 }
42
43 /// Create an Instant from a second count since system boot.
44 pub const fn from_secs(seconds: u64) -> Self {
45 Self {
46 ticks: seconds * TICKS_PER_SECOND,
47 }
48 }
49
50 /// Tick count since system boot.
51 pub const fn as_ticks(&self) -> u64 {
52 self.ticks
53 }
54
55 /// Seconds since system boot.
56 pub const fn as_secs(&self) -> u64 {
57 self.ticks / TICKS_PER_SECOND
58 }
59
60 /// Milliseconds since system boot.
61 pub const fn as_millis(&self) -> u64 {
62 self.ticks * (1000 / GCD_1K) / (TICKS_PER_SECOND / GCD_1K)
63 }
64
65 /// Microseconds since system boot.
66 pub const fn as_micros(&self) -> u64 {
67 self.ticks * (1_000_000 / GCD_1M) / (TICKS_PER_SECOND / GCD_1M)
68 }
69
70 /// Duration between this Instant and another Instant
71 /// Panics on over/underflow.
72 pub fn duration_since(&self, earlier: Instant) -> Duration {
73 Duration {
74 ticks: self.ticks.checked_sub(earlier.ticks).unwrap(),
75 }
76 }
77
78 /// Duration between this Instant and another Instant
79 pub fn checked_duration_since(&self, earlier: Instant) -> Option<Duration> {
80 if self.ticks < earlier.ticks {
81 None
82 } else {
83 Some(Duration {
84 ticks: self.ticks - earlier.ticks,
85 })
86 }
87 }
88
89 /// Returns the duration since the "earlier" Instant.
90 /// If the "earlier" instant is in the future, the duration is set to zero.
91 pub fn saturating_duration_since(&self, earlier: Instant) -> Duration {
92 Duration {
93 ticks: if self.ticks < earlier.ticks {
94 0
95 } else {
96 self.ticks - earlier.ticks
97 },
98 }
99 }
100
101 /// Duration elapsed since this Instant.
102 pub fn elapsed(&self) -> Duration {
103 Instant::now() - *self
104 }
105
106 /// Adds one Duration to self, returning a new `Instant` or None in the event of an overflow.
107 pub fn checked_add(&self, duration: Duration) -> Option<Instant> {
108 self.ticks.checked_add(duration.ticks).map(|ticks| Instant { ticks })
109 }
110
111 /// Subtracts one Duration to self, returning a new `Instant` or None in the event of an overflow.
112 pub fn checked_sub(&self, duration: Duration) -> Option<Instant> {
113 self.ticks.checked_sub(duration.ticks).map(|ticks| Instant { ticks })
114 }
115}
116
117impl Add<Duration> for Instant {
118 type Output = Instant;
119
120 fn add(self, other: Duration) -> Instant {
121 self.checked_add(other)
122 .expect("overflow when adding duration to instant")
123 }
124}
125
126impl AddAssign<Duration> for Instant {
127 fn add_assign(&mut self, other: Duration) {
128 *self = *self + other;
129 }
130}
131
132impl Sub<Duration> for Instant {
133 type Output = Instant;
134
135 fn sub(self, other: Duration) -> Instant {
136 self.checked_sub(other)
137 .expect("overflow when subtracting duration from instant")
138 }
139}
140
141impl SubAssign<Duration> for Instant {
142 fn sub_assign(&mut self, other: Duration) {
143 *self = *self - other;
144 }
145}
146
147impl Sub<Instant> for Instant {
148 type Output = Duration;
149
150 fn sub(self, other: Instant) -> Duration {
151 self.duration_since(other)
152 }
153}
154
155impl<'a> fmt::Display for Instant {
156 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
157 write!(f, "{} ticks", self.ticks)
158 }
159}
diff --git a/embassy-time/src/lib.rs b/embassy-time/src/lib.rs
new file mode 100644
index 000000000..a6454d55e
--- /dev/null
+++ b/embassy-time/src/lib.rs
@@ -0,0 +1,99 @@
1#![cfg_attr(not(any(feature = "std", feature = "wasm")), no_std)]
2#![cfg_attr(feature = "nightly", feature(generic_associated_types, type_alias_impl_trait))]
3#![allow(clippy::new_without_default)]
4#![warn(missing_docs)]
5
6//! Timekeeping, delays and timeouts.
7//!
8//! Timekeeping is done with elapsed time since system boot. Time is represented in
9//! ticks, where the tick rate is defined by the current driver, usually to match
10//! the tick rate of the hardware.
11//!
12//! Tick counts are 64 bits. At the highest supported tick rate of 1Mhz this supports
13//! representing time spans of up to ~584558 years, which is big enough for all practical
14//! purposes and allows not having to worry about overflows.
15//!
16//! [`Instant`] represents a given instant of time (relative to system boot), and [`Duration`]
17//! represents the duration of a span of time. They implement the math operations you'd expect,
18//! like addition and substraction.
19//!
20//! # Delays and timeouts
21//!
22//! [`Timer`] allows performing async delays. [`Ticker`] allows periodic delays without drifting over time.
23//!
24//! An implementation of the `embedded-hal` delay traits is provided by [`Delay`], for compatibility
25//! with libraries from the ecosystem.
26//!
27//! # Wall-clock time
28//!
29//! The `time` module deals exclusively with a monotonically increasing tick count.
30//! Therefore it has no direct support for wall-clock time ("real life" datetimes
31//! like `2021-08-24 13:33:21`).
32//!
33//! If persistence across reboots is not needed, support can be built on top of
34//! `embassy_time` by storing the offset between "seconds elapsed since boot"
35//! and "seconds since unix epoch".
36//!
37//! # Time driver
38//!
39//! The `time` module is backed by a global "time driver" specified at build time.
40//! Only one driver can be active in a program.
41//!
42//! All methods and structs transparently call into the active driver. This makes it
43//! possible for libraries to use `embassy_time` in a driver-agnostic way without
44//! requiring generic parameters.
45//!
46//! For more details, check the [`driver`] module.
47
48// This mod MUST go first, so that the others see its macros.
49pub(crate) mod fmt;
50
51mod delay;
52pub mod driver;
53mod duration;
54mod instant;
55mod timer;
56
57#[cfg(feature = "std")]
58mod driver_std;
59#[cfg(feature = "wasm")]
60mod driver_wasm;
61
62pub use delay::{block_for, Delay};
63pub use duration::Duration;
64pub use instant::Instant;
65pub use timer::{with_timeout, Ticker, TimeoutError, Timer};
66
67#[cfg(feature = "tick-1000hz")]
68const TPS: u64 = 1_000;
69
70#[cfg(feature = "tick-32768hz")]
71const TPS: u64 = 32_768;
72
73#[cfg(feature = "tick-1mhz")]
74const TPS: u64 = 1_000_000;
75
76#[cfg(feature = "tick-16mhz")]
77const TPS: u64 = 16_000_000;
78
79/// Ticks per second of the global timebase.
80///
81/// This value is specified by the `tick-*` Cargo features, which
82/// should be set by the time driver. Some drivers support a fixed tick rate, others
83/// allow you to choose a tick rate with Cargo features of their own. You should not
84/// set the `tick-*` features for embassy yourself as an end user.
85pub const TICKS_PER_SECOND: u64 = TPS;
86
87const fn gcd(a: u64, b: u64) -> u64 {
88 if b == 0 {
89 a
90 } else {
91 gcd(b, a % b)
92 }
93}
94
95pub(crate) const GCD_1K: u64 = gcd(TICKS_PER_SECOND, 1_000);
96pub(crate) const GCD_1M: u64 = gcd(TICKS_PER_SECOND, 1_000_000);
97
98#[cfg(feature = "defmt-timestamp-uptime")]
99defmt::timestamp! {"{=u64:us}", Instant::now().as_micros() }
diff --git a/embassy-time/src/timer.rs b/embassy-time/src/timer.rs
new file mode 100644
index 000000000..bd791b817
--- /dev/null
+++ b/embassy-time/src/timer.rs
@@ -0,0 +1,158 @@
1use core::future::Future;
2use core::pin::Pin;
3use core::task::{Context, Poll, Waker};
4
5use futures_util::future::{select, Either};
6use futures_util::{pin_mut, Stream};
7
8use crate::{Duration, Instant};
9
10/// Error returned by [`with_timeout`] on timeout.
11#[derive(Debug, Clone, PartialEq, Eq)]
12#[cfg_attr(feature = "defmt", derive(defmt::Format))]
13pub struct TimeoutError;
14
15/// Runs a given future with a timeout.
16///
17/// If the future completes before the timeout, its output is returned. Otherwise, on timeout,
18/// work on the future is stopped (`poll` is no longer called), the future is dropped and `Err(TimeoutError)` is returned.
19pub async fn with_timeout<F: Future>(timeout: Duration, fut: F) -> Result<F::Output, TimeoutError> {
20 let timeout_fut = Timer::after(timeout);
21 pin_mut!(fut);
22 match select(fut, timeout_fut).await {
23 Either::Left((r, _)) => Ok(r),
24 Either::Right(_) => Err(TimeoutError),
25 }
26}
27
28/// A future that completes at a specified [Instant](struct.Instant.html).
29pub struct Timer {
30 expires_at: Instant,
31 yielded_once: bool,
32}
33
34impl Timer {
35 /// Expire at specified [Instant](struct.Instant.html)
36 pub fn at(expires_at: Instant) -> Self {
37 Self {
38 expires_at,
39 yielded_once: false,
40 }
41 }
42
43 /// Expire after specified [Duration](struct.Duration.html).
44 /// This can be used as a `sleep` abstraction.
45 ///
46 /// Example:
47 /// ``` no_run
48 /// # #![feature(type_alias_impl_trait)]
49 /// #
50 /// # fn foo() {}
51 /// use embassy_time::{Duration, Timer};
52 ///
53 /// #[embassy_executor::task]
54 /// async fn demo_sleep_seconds() {
55 /// // suspend this task for one second.
56 /// Timer::after(Duration::from_secs(1)).await;
57 /// }
58 /// ```
59 pub fn after(duration: Duration) -> Self {
60 Self {
61 expires_at: Instant::now() + duration,
62 yielded_once: false,
63 }
64 }
65}
66
67impl Unpin for Timer {}
68
69impl Future for Timer {
70 type Output = ();
71 fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
72 if self.yielded_once && self.expires_at <= Instant::now() {
73 Poll::Ready(())
74 } else {
75 schedule_wake(self.expires_at, cx.waker());
76 self.yielded_once = true;
77 Poll::Pending
78 }
79 }
80}
81
82/// Asynchronous stream that yields every Duration, indefinitely.
83///
84/// This stream will tick at uniform intervals, even if blocking work is performed between ticks.
85///
86/// For instance, consider the following code fragment.
87/// ``` no_run
88/// # #![feature(type_alias_impl_trait)]
89/// #
90/// use embassy_time::{Duration, Timer};
91/// # fn foo() {}
92///
93/// #[embassy_executor::task]
94/// async fn ticker_example_0() {
95/// loop {
96/// foo();
97/// Timer::after(Duration::from_secs(1)).await;
98/// }
99/// }
100/// ```
101///
102/// This fragment will not call `foo` every second.
103/// Instead, it will call it every second + the time it took to previously call `foo`.
104///
105/// Example using ticker, which will consistently call `foo` once a second.
106///
107/// ``` no_run
108/// # #![feature(type_alias_impl_trait)]
109/// #
110/// use embassy_time::{Duration, Ticker};
111/// use futures::StreamExt;
112/// # fn foo(){}
113///
114/// #[embassy_executor::task]
115/// async fn ticker_example_1() {
116/// let mut ticker = Ticker::every(Duration::from_secs(1));
117/// loop {
118/// foo();
119/// ticker.next().await;
120/// }
121/// }
122/// ```
123pub struct Ticker {
124 expires_at: Instant,
125 duration: Duration,
126}
127
128impl Ticker {
129 /// Creates a new ticker that ticks at the specified duration interval.
130 pub fn every(duration: Duration) -> Self {
131 let expires_at = Instant::now() + duration;
132 Self { expires_at, duration }
133 }
134}
135
136impl Unpin for Ticker {}
137
138impl Stream for Ticker {
139 type Item = ();
140 fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
141 if self.expires_at <= Instant::now() {
142 let dur = self.duration;
143 self.expires_at += dur;
144 Poll::Ready(Some(()))
145 } else {
146 schedule_wake(self.expires_at, cx.waker());
147 Poll::Pending
148 }
149 }
150}
151
152extern "Rust" {
153 fn _embassy_time_schedule_wake(at: Instant, waker: &Waker);
154}
155
156fn schedule_wake(at: Instant, waker: &Waker) {
157 unsafe { _embassy_time_schedule_wake(at, waker) }
158}