aboutsummaryrefslogtreecommitdiff
path: root/embassy-time/src
diff options
context:
space:
mode:
authorDion Dokter <[email protected]>2025-11-20 13:22:38 +0100
committerDion Dokter <[email protected]>2025-11-20 13:22:38 +0100
commit4f2c36e447455e8d33607d586859d3d075cabf1d (patch)
tree003cd822d688acd7c074dd229663b4648d100f71 /embassy-time/src
parent663732d85abbae400f2dbab2c411802a5b60e9b1 (diff)
parent661874d11de7d93ed52e08e020a9d4c7ee11122d (diff)
Merge branch 'main' into u0-lcd
Diffstat (limited to 'embassy-time/src')
-rw-r--r--embassy-time/src/delay.rs17
-rw-r--r--embassy-time/src/driver_mock.rs148
-rw-r--r--embassy-time/src/driver_std.rs211
-rw-r--r--embassy-time/src/driver_wasm.rs148
-rw-r--r--embassy-time/src/duration.rs102
-rw-r--r--embassy-time/src/fmt.rs31
-rw-r--r--embassy-time/src/instant.rs69
-rw-r--r--embassy-time/src/lib.rs14
-rw-r--r--embassy-time/src/queue_generic.rs348
-rw-r--r--embassy-time/src/timer.rs103
10 files changed, 422 insertions, 769 deletions
diff --git a/embassy-time/src/delay.rs b/embassy-time/src/delay.rs
index f77859d4a..11b54b098 100644
--- a/embassy-time/src/delay.rs
+++ b/embassy-time/src/delay.rs
@@ -1,3 +1,5 @@
1use core::future::Future;
2
1use super::{Duration, Instant}; 3use super::{Duration, Instant};
2use crate::Timer; 4use crate::Timer;
3 5
@@ -13,7 +15,8 @@ pub fn block_for(duration: Duration) {
13/// the amount provided, but accuracy can be affected by many factors, including interrupt usage. 15/// the amount provided, but accuracy can be affected by many factors, including interrupt usage.
14/// Make sure to use a suitable tick rate for your use case. The tick rate is defined by the currently 16/// Make sure to use a suitable tick rate for your use case. The tick rate is defined by the currently
15/// active driver. 17/// active driver.
16#[derive(Clone)] 18#[derive(Clone, Debug)]
19#[cfg_attr(feature = "defmt", derive(defmt::Format))]
17pub struct Delay; 20pub struct Delay;
18 21
19impl embedded_hal_1::delay::DelayNs for Delay { 22impl embedded_hal_1::delay::DelayNs for Delay {
@@ -31,16 +34,16 @@ impl embedded_hal_1::delay::DelayNs for Delay {
31} 34}
32 35
33impl embedded_hal_async::delay::DelayNs for Delay { 36impl embedded_hal_async::delay::DelayNs for Delay {
34 async fn delay_ns(&mut self, ns: u32) { 37 fn delay_ns(&mut self, ns: u32) -> impl Future<Output = ()> {
35 Timer::after_nanos(ns as _).await 38 Timer::after_nanos(ns as _)
36 } 39 }
37 40
38 async fn delay_us(&mut self, us: u32) { 41 fn delay_us(&mut self, us: u32) -> impl Future<Output = ()> {
39 Timer::after_micros(us as _).await 42 Timer::after_micros(us as _)
40 } 43 }
41 44
42 async fn delay_ms(&mut self, ms: u32) { 45 fn delay_ms(&mut self, ms: u32) -> impl Future<Output = ()> {
43 Timer::after_millis(ms as _).await 46 Timer::after_millis(ms as _)
44 } 47 }
45} 48}
46 49
diff --git a/embassy-time/src/driver_mock.rs b/embassy-time/src/driver_mock.rs
index 8587f9172..bcde2a6c9 100644
--- a/embassy-time/src/driver_mock.rs
+++ b/embassy-time/src/driver_mock.rs
@@ -1,7 +1,9 @@
1use core::cell::RefCell; 1use core::cell::RefCell;
2use core::task::Waker;
2 3
3use critical_section::Mutex as CsMutex; 4use critical_section::Mutex as CsMutex;
4use embassy_time_driver::{AlarmHandle, Driver}; 5use embassy_time_driver::Driver;
6use embassy_time_queue_utils::Queue;
5 7
6use crate::{Duration, Instant}; 8use crate::{Duration, Instant};
7 9
@@ -26,6 +28,7 @@ use crate::{Duration, Instant};
26/// assert_eq!(true, has_a_second_passed(reference)); 28/// assert_eq!(true, has_a_second_passed(reference));
27/// } 29/// }
28/// ``` 30/// ```
31#[derive(Debug)]
29pub struct MockDriver(CsMutex<RefCell<InnerMockDriver>>); 32pub struct MockDriver(CsMutex<RefCell<InnerMockDriver>>);
30 33
31embassy_time_driver::time_driver_impl!(static DRIVER: MockDriver = MockDriver::new()); 34embassy_time_driver::time_driver_impl!(static DRIVER: MockDriver = MockDriver::new());
@@ -52,29 +55,13 @@ impl MockDriver {
52 /// Advances the time by the specified [`Duration`]. 55 /// Advances the time by the specified [`Duration`].
53 /// Calling any alarm callbacks that are due. 56 /// Calling any alarm callbacks that are due.
54 pub fn advance(&self, duration: Duration) { 57 pub fn advance(&self, duration: Duration) {
55 let notify = { 58 critical_section::with(|cs| {
56 critical_section::with(|cs| { 59 let inner = &mut *self.0.borrow_ref_mut(cs);
57 let mut inner = self.0.borrow_ref_mut(cs);
58
59 inner.now += duration;
60
61 let now = inner.now.as_ticks();
62
63 inner
64 .alarm
65 .as_mut()
66 .filter(|alarm| alarm.timestamp <= now)
67 .map(|alarm| {
68 alarm.timestamp = u64::MAX;
69
70 (alarm.callback, alarm.ctx)
71 })
72 })
73 };
74 60
75 if let Some((callback, ctx)) = notify { 61 inner.now += duration;
76 (callback)(ctx); 62 // wake expired tasks.
77 } 63 inner.queue.next_expiration(inner.now.as_ticks());
64 })
78 } 65 }
79} 66}
80 67
@@ -83,87 +70,38 @@ impl Driver for MockDriver {
83 critical_section::with(|cs| self.0.borrow_ref(cs).now).as_ticks() 70 critical_section::with(|cs| self.0.borrow_ref(cs).now).as_ticks()
84 } 71 }
85 72
86 unsafe fn allocate_alarm(&self) -> Option<AlarmHandle> { 73 fn schedule_wake(&self, at: u64, waker: &Waker) {
87 critical_section::with(|cs| { 74 critical_section::with(|cs| {
88 let mut inner = self.0.borrow_ref_mut(cs); 75 let inner = &mut *self.0.borrow_ref_mut(cs);
89 76 // enqueue it
90 if inner.alarm.is_some() { 77 inner.queue.schedule_wake(at, waker);
91 None 78 // wake it if it's in the past.
92 } else { 79 inner.queue.next_expiration(inner.now.as_ticks());
93 inner.alarm.replace(AlarmState::new());
94
95 Some(AlarmHandle::new(0))
96 }
97 })
98 }
99
100 fn set_alarm_callback(&self, _alarm: AlarmHandle, callback: fn(*mut ()), ctx: *mut ()) {
101 critical_section::with(|cs| {
102 let mut inner = self.0.borrow_ref_mut(cs);
103
104 let Some(alarm) = inner.alarm.as_mut() else {
105 panic!("Alarm not allocated");
106 };
107
108 alarm.callback = callback;
109 alarm.ctx = ctx;
110 });
111 }
112
113 fn set_alarm(&self, _alarm: AlarmHandle, timestamp: u64) -> bool {
114 critical_section::with(|cs| {
115 let mut inner = self.0.borrow_ref_mut(cs);
116
117 if timestamp <= inner.now.as_ticks() {
118 false
119 } else {
120 let Some(alarm) = inner.alarm.as_mut() else {
121 panic!("Alarm not allocated");
122 };
123
124 alarm.timestamp = timestamp;
125 true
126 }
127 }) 80 })
128 } 81 }
129} 82}
130 83
84#[derive(Debug)]
131struct InnerMockDriver { 85struct InnerMockDriver {
132 now: Instant, 86 now: Instant,
133 alarm: Option<AlarmState>, 87 queue: Queue,
134} 88}
135 89
136impl InnerMockDriver { 90impl InnerMockDriver {
137 const fn new() -> Self { 91 const fn new() -> Self {
138 Self { 92 Self {
139 now: Instant::from_ticks(0), 93 now: Instant::from_ticks(0),
140 alarm: None, 94 queue: Queue::new(),
141 }
142 }
143}
144
145struct AlarmState {
146 timestamp: u64,
147 callback: fn(*mut ()),
148 ctx: *mut (),
149}
150
151impl AlarmState {
152 const fn new() -> Self {
153 Self {
154 timestamp: u64::MAX,
155 callback: Self::noop,
156 ctx: core::ptr::null_mut(),
157 } 95 }
158 } 96 }
159
160 fn noop(_ctx: *mut ()) {}
161} 97}
162 98
163unsafe impl Send for AlarmState {}
164
165#[cfg(test)] 99#[cfg(test)]
166mod tests { 100mod tests {
101 use core::sync::atomic::{AtomicBool, Ordering};
102 use std::sync::Arc;
103 use std::task::Wake;
104
167 use serial_test::serial; 105 use serial_test::serial;
168 106
169 use super::*; 107 use super::*;
@@ -185,37 +123,25 @@ mod tests {
185 123
186 #[test] 124 #[test]
187 #[serial] 125 #[serial]
188 fn test_set_alarm_not_in_future() { 126 fn test_schedule_wake() {
189 setup(); 127 setup();
190 128
191 let driver = MockDriver::get(); 129 static CALLBACK_CALLED: AtomicBool = AtomicBool::new(false);
192 let alarm = unsafe { AlarmHandle::new(0) };
193 assert_eq!(false, driver.set_alarm(alarm, driver.now()));
194 }
195 130
196 #[test] 131 struct MockWaker;
197 #[serial]
198 fn test_alarm() {
199 setup();
200
201 let driver = MockDriver::get();
202 let alarm = unsafe { driver.allocate_alarm() }.expect("No alarms available");
203 static mut CALLBACK_CALLED: bool = false;
204 let ctx = &mut () as *mut ();
205 driver.set_alarm_callback(alarm, |_| unsafe { CALLBACK_CALLED = true }, ctx);
206 driver.set_alarm(alarm, driver.now() + 1);
207 assert_eq!(false, unsafe { CALLBACK_CALLED });
208 driver.advance(Duration::from_secs(1));
209 assert_eq!(true, unsafe { CALLBACK_CALLED });
210 }
211 132
212 #[test] 133 impl Wake for MockWaker {
213 #[serial] 134 fn wake(self: Arc<Self>) {
214 fn test_allocate_alarm() { 135 CALLBACK_CALLED.store(true, Ordering::Relaxed);
215 setup(); 136 }
137 }
138 let waker = Arc::new(MockWaker).into();
216 139
217 let driver = MockDriver::get(); 140 let driver = MockDriver::get();
218 assert!(unsafe { driver.allocate_alarm() }.is_some()); 141
219 assert!(unsafe { driver.allocate_alarm() }.is_none()); 142 driver.schedule_wake(driver.now() + 1, &waker);
143 assert_eq!(false, CALLBACK_CALLED.load(Ordering::Relaxed));
144 driver.advance(Duration::from_secs(1));
145 assert_eq!(true, CALLBACK_CALLED.load(Ordering::Relaxed));
220 } 146 }
221} 147}
diff --git a/embassy-time/src/driver_std.rs b/embassy-time/src/driver_std.rs
index d182f8331..0cdb8f4ac 100644
--- a/embassy-time/src/driver_std.rs
+++ b/embassy-time/src/driver_std.rs
@@ -1,170 +1,79 @@
1use core::sync::atomic::{AtomicU8, Ordering}; 1use std::sync::{Condvar, Mutex};
2use std::cell::{RefCell, UnsafeCell}; 2use std::thread;
3use std::mem::MaybeUninit;
4use std::sync::{Condvar, Mutex, Once};
5use std::time::{Duration as StdDuration, Instant as StdInstant}; 3use std::time::{Duration as StdDuration, Instant as StdInstant};
6use std::{mem, ptr, thread};
7 4
8use critical_section::Mutex as CsMutex; 5use embassy_time_driver::Driver;
9use embassy_time_driver::{AlarmHandle, Driver}; 6use embassy_time_queue_utils::Queue;
10 7
11const ALARM_COUNT: usize = 4; 8#[derive(Debug)]
12 9struct TimeDriver {
13struct AlarmState { 10 signaler: Signaler,
14 timestamp: u64, 11 inner: Mutex<Inner>,
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} 12}
21 13
22unsafe impl Send for AlarmState {} 14#[derive(Debug)]
23 15struct Inner {
24impl AlarmState { 16 zero_instant: Option<StdInstant>,
25 const fn new() -> Self { 17 queue: Queue,
26 Self {
27 timestamp: u64::MAX,
28 callback: ptr::null(),
29 ctx: ptr::null_mut(),
30 }
31 }
32} 18}
33 19
34struct TimeDriver {
35 alarm_count: AtomicU8,
36
37 once: Once,
38 // The STD Driver implementation requires the alarms' mutex to be reentrant, which the STD Mutex isn't
39 // Fortunately, mutexes based on the `critical-section` crate are reentrant, because the critical sections
40 // themselves are reentrant
41 alarms: UninitCell<CsMutex<RefCell<[AlarmState; ALARM_COUNT]>>>,
42 zero_instant: UninitCell<StdInstant>,
43 signaler: UninitCell<Signaler>,
44}
45
46const ALARM_NEW: AlarmState = AlarmState::new();
47embassy_time_driver::time_driver_impl!(static DRIVER: TimeDriver = TimeDriver { 20embassy_time_driver::time_driver_impl!(static DRIVER: TimeDriver = TimeDriver {
48 alarm_count: AtomicU8::new(0), 21 inner: Mutex::new(Inner{
49 22 zero_instant: None,
50 once: Once::new(), 23 queue: Queue::new(),
51 alarms: UninitCell::uninit(), 24 }),
52 zero_instant: UninitCell::uninit(), 25 signaler: Signaler::new(),
53 signaler: UninitCell::uninit(),
54}); 26});
55 27
56impl TimeDriver { 28impl Inner {
57 fn init(&self) { 29 fn init(&mut self) -> StdInstant {
58 self.once.call_once(|| unsafe { 30 *self.zero_instant.get_or_insert_with(|| {
59 self.alarms.write(CsMutex::new(RefCell::new([ALARM_NEW; ALARM_COUNT]))); 31 thread::spawn(alarm_thread);
60 self.zero_instant.write(StdInstant::now()); 32 StdInstant::now()
61 self.signaler.write(Signaler::new()); 33 })
62
63 thread::spawn(Self::alarm_thread);
64 });
65 }
66
67 fn alarm_thread() {
68 let zero = unsafe { DRIVER.zero_instant.read() };
69 loop {
70 let now = DRIVER.now();
71
72 let next_alarm = critical_section::with(|cs| {
73 let alarms = unsafe { DRIVER.alarms.as_ref() }.borrow(cs);
74 loop {
75 let pending = alarms
76 .borrow_mut()
77 .iter_mut()
78 .find(|alarm| alarm.timestamp <= now)
79 .map(|alarm| {
80 alarm.timestamp = u64::MAX;
81
82 (alarm.callback, alarm.ctx)
83 });
84
85 if let Some((callback, ctx)) = pending {
86 // safety:
87 // - we can ignore the possiblity of `f` being unset (null) because of the safety contract of `allocate_alarm`.
88 // - other than that we only store valid function pointers into alarm.callback
89 let f: fn(*mut ()) = unsafe { mem::transmute(callback) };
90 f(ctx);
91 } else {
92 // No alarm due
93 break;
94 }
95 }
96
97 alarms
98 .borrow()
99 .iter()
100 .map(|alarm| alarm.timestamp)
101 .min()
102 .unwrap_or(u64::MAX)
103 });
104
105 // Ensure we don't overflow
106 let until = zero
107 .checked_add(StdDuration::from_micros(next_alarm))
108 .unwrap_or_else(|| StdInstant::now() + StdDuration::from_secs(1));
109
110 unsafe { DRIVER.signaler.as_ref() }.wait_until(until);
111 }
112 } 34 }
113} 35}
114 36
115impl Driver for TimeDriver { 37impl Driver for TimeDriver {
116 fn now(&self) -> u64 { 38 fn now(&self) -> u64 {
117 self.init(); 39 let mut inner = self.inner.lock().unwrap();
118 40 let zero = inner.init();
119 let zero = unsafe { self.zero_instant.read() };
120 StdInstant::now().duration_since(zero).as_micros() as u64 41 StdInstant::now().duration_since(zero).as_micros() as u64
121 } 42 }
122 43
123 unsafe fn allocate_alarm(&self) -> Option<AlarmHandle> { 44 fn schedule_wake(&self, at: u64, waker: &core::task::Waker) {
124 let id = self.alarm_count.fetch_update(Ordering::AcqRel, Ordering::Acquire, |x| { 45 let mut inner = self.inner.lock().unwrap();
125 if x < ALARM_COUNT as u8 { 46 inner.init();
126 Some(x + 1) 47 if inner.queue.schedule_wake(at, waker) {
127 } else { 48 self.signaler.signal();
128 None
129 }
130 });
131
132 match id {
133 Ok(id) => Some(AlarmHandle::new(id)),
134 Err(_) => None,
135 } 49 }
136 } 50 }
51}
137 52
138 fn set_alarm_callback(&self, alarm: AlarmHandle, callback: fn(*mut ()), ctx: *mut ()) { 53fn alarm_thread() {
139 self.init(); 54 let zero = DRIVER.inner.lock().unwrap().zero_instant.unwrap();
140 critical_section::with(|cs| { 55 loop {
141 let mut alarms = unsafe { self.alarms.as_ref() }.borrow_ref_mut(cs); 56 let now = DRIVER.now();
142 let alarm = &mut alarms[alarm.id() as usize]; 57
143 alarm.callback = callback as *const (); 58 let next_alarm = DRIVER.inner.lock().unwrap().queue.next_expiration(now);
144 alarm.ctx = ctx;
145 });
146 }
147 59
148 fn set_alarm(&self, alarm: AlarmHandle, timestamp: u64) -> bool { 60 // Ensure we don't overflow
149 self.init(); 61 let until = zero
150 critical_section::with(|cs| { 62 .checked_add(StdDuration::from_micros(next_alarm))
151 let mut alarms = unsafe { self.alarms.as_ref() }.borrow_ref_mut(cs); 63 .unwrap_or_else(|| StdInstant::now() + StdDuration::from_secs(1));
152 let alarm = &mut alarms[alarm.id() as usize];
153 alarm.timestamp = timestamp;
154 unsafe { self.signaler.as_ref() }.signal();
155 });
156 64
157 true 65 DRIVER.signaler.wait_until(until);
158 } 66 }
159} 67}
160 68
69#[derive(Debug)]
161struct Signaler { 70struct Signaler {
162 mutex: Mutex<bool>, 71 mutex: Mutex<bool>,
163 condvar: Condvar, 72 condvar: Condvar,
164} 73}
165 74
166impl Signaler { 75impl Signaler {
167 fn new() -> Self { 76 const fn new() -> Self {
168 Self { 77 Self {
169 mutex: Mutex::new(false), 78 mutex: Mutex::new(false),
170 condvar: Condvar::new(), 79 condvar: Condvar::new(),
@@ -196,35 +105,3 @@ impl Signaler {
196 self.condvar.notify_one(); 105 self.condvar.notify_one();
197 } 106 }
198} 107}
199
200pub(crate) struct UninitCell<T>(MaybeUninit<UnsafeCell<T>>);
201unsafe impl<T> Send for UninitCell<T> {}
202unsafe impl<T> Sync for UninitCell<T> {}
203
204impl<T> UninitCell<T> {
205 pub const fn uninit() -> Self {
206 Self(MaybeUninit::uninit())
207 }
208
209 pub unsafe fn as_ptr(&self) -> *const T {
210 (*self.0.as_ptr()).get()
211 }
212
213 pub unsafe fn as_mut_ptr(&self) -> *mut T {
214 (*self.0.as_ptr()).get()
215 }
216
217 pub unsafe fn as_ref(&self) -> &T {
218 &*self.as_ptr()
219 }
220
221 pub unsafe fn write(&self, val: T) {
222 ptr::write(self.as_mut_ptr(), val)
223 }
224}
225
226impl<T: Copy> UninitCell<T> {
227 pub unsafe fn read(&self) -> T {
228 ptr::read(self.as_mut_ptr())
229 }
230}
diff --git a/embassy-time/src/driver_wasm.rs b/embassy-time/src/driver_wasm.rs
index ad884f060..646ce170e 100644
--- a/embassy-time/src/driver_wasm.rs
+++ b/embassy-time/src/driver_wasm.rs
@@ -1,28 +1,19 @@
1use core::sync::atomic::{AtomicU8, Ordering}; 1use std::sync::Mutex;
2use std::cell::UnsafeCell;
3use std::mem::MaybeUninit;
4use std::ptr;
5use std::sync::{Mutex, Once};
6 2
7use embassy_time_driver::{AlarmHandle, Driver}; 3use embassy_time_driver::Driver;
4use embassy_time_queue_utils::Queue;
8use wasm_bindgen::prelude::*; 5use wasm_bindgen::prelude::*;
9use wasm_timer::Instant as StdInstant; 6use wasm_timer::Instant as StdInstant;
10 7
11const ALARM_COUNT: usize = 4; 8#[derive(Debug)]
12 9#[cfg_attr(feature = "defmt", derive(defmt::Format))]
13struct AlarmState { 10struct AlarmState {
14 token: Option<f64>, 11 token: Option<f64>,
15 closure: Option<Closure<dyn FnMut() + 'static>>,
16} 12}
17 13
18unsafe impl Send for AlarmState {}
19
20impl AlarmState { 14impl AlarmState {
21 const fn new() -> Self { 15 const fn new() -> Self {
22 Self { 16 Self { token: None }
23 token: None,
24 closure: None,
25 }
26 } 17 }
27} 18}
28 19
@@ -32,68 +23,43 @@ extern "C" {
32 fn clearTimeout(token: f64); 23 fn clearTimeout(token: f64);
33} 24}
34 25
26#[derive(Debug)]
27#[cfg_attr(feature = "defmt", derive(defmt::Format))]
35struct TimeDriver { 28struct TimeDriver {
36 alarm_count: AtomicU8, 29 inner: Mutex<Inner>,
37
38 once: Once,
39 alarms: UninitCell<Mutex<[AlarmState; ALARM_COUNT]>>,
40 zero_instant: UninitCell<StdInstant>,
41} 30}
42 31
43const ALARM_NEW: AlarmState = AlarmState::new(); 32#[derive(Debug)]
44embassy_time_driver::time_driver_impl!(static DRIVER: TimeDriver = TimeDriver { 33#[cfg_attr(feature = "defmt", derive(defmt::Format))]
45 alarm_count: AtomicU8::new(0), 34struct Inner {
46 once: Once::new(), 35 alarm: AlarmState,
47 alarms: UninitCell::uninit(), 36 zero_instant: Option<StdInstant>,
48 zero_instant: UninitCell::uninit(), 37 queue: Queue,
49}); 38 closure: Option<Closure<dyn FnMut()>>,
50
51impl TimeDriver {
52 fn init(&self) {
53 self.once.call_once(|| unsafe {
54 self.alarms.write(Mutex::new([ALARM_NEW; ALARM_COUNT]));
55 self.zero_instant.write(StdInstant::now());
56 });
57 }
58} 39}
59 40
60impl Driver for TimeDriver { 41unsafe impl Send for Inner {}
61 fn now(&self) -> u64 {
62 self.init();
63 42
64 let zero = unsafe { self.zero_instant.read() }; 43embassy_time_driver::time_driver_impl!(static DRIVER: TimeDriver = TimeDriver {
65 StdInstant::now().duration_since(zero).as_micros() as u64 44 inner: Mutex::new(Inner{
66 } 45 zero_instant: None,
67 46 queue: Queue::new(),
68 unsafe fn allocate_alarm(&self) -> Option<AlarmHandle> { 47 alarm: AlarmState::new(),
69 let id = self.alarm_count.fetch_update(Ordering::AcqRel, Ordering::Acquire, |x| { 48 closure: None,
70 if x < ALARM_COUNT as u8 { 49 }),
71 Some(x + 1) 50});
72 } else {
73 None
74 }
75 });
76 51
77 match id { 52impl Inner {
78 Ok(id) => Some(AlarmHandle::new(id)), 53 fn init(&mut self) -> StdInstant {
79 Err(_) => None, 54 *self.zero_instant.get_or_insert_with(StdInstant::now)
80 }
81 } 55 }
82 56
83 fn set_alarm_callback(&self, alarm: AlarmHandle, callback: fn(*mut ()), ctx: *mut ()) { 57 fn now(&mut self) -> u64 {
84 self.init(); 58 StdInstant::now().duration_since(self.zero_instant.unwrap()).as_micros() as u64
85 let mut alarms = unsafe { self.alarms.as_ref() }.lock().unwrap();
86 let alarm = &mut alarms[alarm.id() as usize];
87 alarm.closure.replace(Closure::new(move || {
88 callback(ctx);
89 }));
90 } 59 }
91 60
92 fn set_alarm(&self, alarm: AlarmHandle, timestamp: u64) -> bool { 61 fn set_alarm(&mut self, timestamp: u64) -> bool {
93 self.init(); 62 if let Some(token) = self.alarm.token {
94 let mut alarms = unsafe { self.alarms.as_ref() }.lock().unwrap();
95 let alarm = &mut alarms[alarm.id() as usize];
96 if let Some(token) = alarm.token {
97 clearTimeout(token); 63 clearTimeout(token);
98 } 64 }
99 65
@@ -102,40 +68,42 @@ impl Driver for TimeDriver {
102 false 68 false
103 } else { 69 } else {
104 let timeout = (timestamp - now) as u32; 70 let timeout = (timestamp - now) as u32;
105 alarm.token = Some(setTimeout(alarm.closure.as_ref().unwrap(), timeout / 1000)); 71 let closure = self.closure.get_or_insert_with(|| Closure::new(dispatch));
72 self.alarm.token = Some(setTimeout(closure, timeout / 1000));
106 73
107 true 74 true
108 } 75 }
109 } 76 }
110} 77}
111 78
112pub(crate) struct UninitCell<T>(MaybeUninit<UnsafeCell<T>>); 79impl Driver for TimeDriver {
113unsafe impl<T> Send for UninitCell<T> {} 80 fn now(&self) -> u64 {
114unsafe impl<T> Sync for UninitCell<T> {} 81 let mut inner = self.inner.lock().unwrap();
115 82 let zero = inner.init();
116impl<T> UninitCell<T> { 83 StdInstant::now().duration_since(zero).as_micros() as u64
117 pub const fn uninit() -> Self {
118 Self(MaybeUninit::uninit())
119 }
120 unsafe fn as_ptr(&self) -> *const T {
121 (*self.0.as_ptr()).get()
122 }
123
124 pub unsafe fn as_mut_ptr(&self) -> *mut T {
125 (*self.0.as_ptr()).get()
126 }
127
128 pub unsafe fn as_ref(&self) -> &T {
129 &*self.as_ptr()
130 } 84 }
131 85
132 pub unsafe fn write(&self, val: T) { 86 fn schedule_wake(&self, at: u64, waker: &core::task::Waker) {
133 ptr::write(self.as_mut_ptr(), val) 87 let mut inner = self.inner.lock().unwrap();
88 inner.init();
89 if inner.queue.schedule_wake(at, waker) {
90 let now = inner.now();
91 let mut next = inner.queue.next_expiration(now);
92 while !inner.set_alarm(next) {
93 let now = inner.now();
94 next = inner.queue.next_expiration(now);
95 }
96 }
134 } 97 }
135} 98}
136 99
137impl<T: Copy> UninitCell<T> { 100fn dispatch() {
138 pub unsafe fn read(&self) -> T { 101 let inner = &mut *DRIVER.inner.lock().unwrap();
139 ptr::read(self.as_mut_ptr()) 102
103 let now = inner.now();
104 let mut next = inner.queue.next_expiration(now);
105 while !inner.set_alarm(next) {
106 let now = inner.now();
107 next = inner.queue.next_expiration(now);
140 } 108 }
141} 109}
diff --git a/embassy-time/src/duration.rs b/embassy-time/src/duration.rs
index 647d208e3..b2bfd6de9 100644
--- a/embassy-time/src/duration.rs
+++ b/embassy-time/src/duration.rs
@@ -37,6 +37,11 @@ impl Duration {
37 self.ticks * (1_000_000 / GCD_1M) / (TICK_HZ / GCD_1M) 37 self.ticks * (1_000_000 / GCD_1M) / (TICK_HZ / GCD_1M)
38 } 38 }
39 39
40 /// Convert the `Duration` to nanoseconds, rounding down.
41 pub const fn as_nanos(&self) -> u64 {
42 self.ticks * (1_000_000_000 / GCD_1G) / (TICK_HZ / GCD_1G)
43 }
44
40 /// Creates a duration from the specified number of clock ticks 45 /// Creates a duration from the specified number of clock ticks
41 pub const fn from_ticks(ticks: u64) -> Duration { 46 pub const fn from_ticks(ticks: u64) -> Duration {
42 Duration { ticks } 47 Duration { ticks }
@@ -64,9 +69,9 @@ impl Duration {
64 69
65 /// Creates a duration from the specified number of nanoseconds, rounding up. 70 /// Creates a duration from the specified number of nanoseconds, rounding up.
66 /// NOTE: Delays this small may be inaccurate. 71 /// NOTE: Delays this small may be inaccurate.
67 pub const fn from_nanos(micros: u64) -> Duration { 72 pub const fn from_nanos(nanoseconds: u64) -> Duration {
68 Duration { 73 Duration {
69 ticks: div_ceil(micros * (TICK_HZ / GCD_1G), 1_000_000_000 / GCD_1G), 74 ticks: div_ceil(nanoseconds * (TICK_HZ / GCD_1G), 1_000_000_000 / GCD_1G),
70 } 75 }
71 } 76 }
72 77
@@ -90,17 +95,87 @@ impl Duration {
90 } 95 }
91 } 96 }
92 97
98 /// Try to create a duration from the specified number of seconds, rounding up.
99 /// Fails if the number of seconds is too large.
100 pub const fn try_from_secs(secs: u64) -> Option<Duration> {
101 let Some(ticks) = secs.checked_mul(TICK_HZ) else {
102 return None;
103 };
104 Some(Duration { ticks })
105 }
106
107 /// Try to create a duration from the specified number of milliseconds, rounding up.
108 /// Fails if the number of milliseconds is too large.
109 pub const fn try_from_millis(millis: u64) -> Option<Duration> {
110 let Some(value) = millis.checked_mul(TICK_HZ / GCD_1K) else {
111 return None;
112 };
113 Some(Duration {
114 ticks: div_ceil(value, 1000 / GCD_1K),
115 })
116 }
117
118 /// Try to create a duration from the specified number of microseconds, rounding up.
119 /// Fails if the number of microseconds is too large.
120 /// NOTE: Delays this small may be inaccurate.
121 pub const fn try_from_micros(micros: u64) -> Option<Duration> {
122 let Some(value) = micros.checked_mul(TICK_HZ / GCD_1M) else {
123 return None;
124 };
125 Some(Duration {
126 ticks: div_ceil(value, 1_000_000 / GCD_1M),
127 })
128 }
129
130 /// Try to create a duration from the specified number of nanoseconds, rounding up.
131 /// Fails if the number of nanoseconds is too large.
132 /// NOTE: Delays this small may be inaccurate.
133 pub const fn try_from_nanos(nanoseconds: u64) -> Option<Duration> {
134 let Some(value) = nanoseconds.checked_mul(TICK_HZ / GCD_1G) else {
135 return None;
136 };
137 Some(Duration {
138 ticks: div_ceil(value, 1_000_000_000 / GCD_1G),
139 })
140 }
141
142 /// Try to create a duration from the specified number of seconds, rounding down.
143 /// Fails if the number of seconds is too large.
144 pub const fn try_from_secs_floor(secs: u64) -> Option<Duration> {
145 let Some(ticks) = secs.checked_mul(TICK_HZ) else {
146 return None;
147 };
148 Some(Duration { ticks })
149 }
150
151 /// Try to create a duration from the specified number of milliseconds, rounding down.
152 /// Fails if the number of milliseconds is too large.
153 pub const fn try_from_millis_floor(millis: u64) -> Option<Duration> {
154 let Some(value) = millis.checked_mul(TICK_HZ / GCD_1K) else {
155 return None;
156 };
157 Some(Duration {
158 ticks: value / (1000 / GCD_1K),
159 })
160 }
161
162 /// Try to create a duration from the specified number of microseconds, rounding down.
163 /// Fails if the number of microseconds is too large.
164 /// NOTE: Delays this small may be inaccurate.
165 pub const fn try_from_micros_floor(micros: u64) -> Option<Duration> {
166 let Some(value) = micros.checked_mul(TICK_HZ / GCD_1M) else {
167 return None;
168 };
169 Some(Duration {
170 ticks: value / (1_000_000 / GCD_1M),
171 })
172 }
173
93 /// Creates a duration corresponding to the specified Hz. 174 /// Creates a duration corresponding to the specified Hz.
94 /// NOTE: Giving this function a hz >= the TICK_HZ of your platform will clamp the Duration to 1 175 /// NOTE: Giving this function a hz >= the TICK_HZ of your platform will clamp the Duration to 1
95 /// tick. Doing so will not deadlock, but will certainly not produce the desired output. 176 /// tick. Doing so will not deadlock, but will certainly not produce the desired output.
96 pub const fn from_hz(hz: u64) -> Duration { 177 pub const fn from_hz(hz: u64) -> Duration {
97 let ticks = { 178 let ticks = { if hz >= TICK_HZ { 1 } else { (TICK_HZ + hz / 2) / hz } };
98 if hz >= TICK_HZ {
99 1
100 } else {
101 (TICK_HZ + hz / 2) / hz
102 }
103 };
104 Duration { ticks } 179 Duration { ticks }
105 } 180 }
106 181
@@ -217,3 +292,12 @@ impl From<Duration> for core::time::Duration {
217 core::time::Duration::from_micros(value.as_micros()) 292 core::time::Duration::from_micros(value.as_micros())
218 } 293 }
219} 294}
295
296impl core::iter::Sum for Duration {
297 fn sum<I>(iter: I) -> Self
298 where
299 I: Iterator<Item = Duration>,
300 {
301 Duration::from_ticks(iter.map(|d| d.as_ticks()).sum())
302 }
303}
diff --git a/embassy-time/src/fmt.rs b/embassy-time/src/fmt.rs
index 2ac42c557..8ca61bc39 100644
--- a/embassy-time/src/fmt.rs
+++ b/embassy-time/src/fmt.rs
@@ -6,6 +6,7 @@ use core::fmt::{Debug, Display, LowerHex};
6#[cfg(all(feature = "defmt", feature = "log"))] 6#[cfg(all(feature = "defmt", feature = "log"))]
7compile_error!("You may not enable both `defmt` and `log` features."); 7compile_error!("You may not enable both `defmt` and `log` features.");
8 8
9#[collapse_debuginfo(yes)]
9macro_rules! assert { 10macro_rules! assert {
10 ($($x:tt)*) => { 11 ($($x:tt)*) => {
11 { 12 {
@@ -17,6 +18,7 @@ macro_rules! assert {
17 }; 18 };
18} 19}
19 20
21#[collapse_debuginfo(yes)]
20macro_rules! assert_eq { 22macro_rules! assert_eq {
21 ($($x:tt)*) => { 23 ($($x:tt)*) => {
22 { 24 {
@@ -28,6 +30,7 @@ macro_rules! assert_eq {
28 }; 30 };
29} 31}
30 32
33#[collapse_debuginfo(yes)]
31macro_rules! assert_ne { 34macro_rules! assert_ne {
32 ($($x:tt)*) => { 35 ($($x:tt)*) => {
33 { 36 {
@@ -39,6 +42,7 @@ macro_rules! assert_ne {
39 }; 42 };
40} 43}
41 44
45#[collapse_debuginfo(yes)]
42macro_rules! debug_assert { 46macro_rules! debug_assert {
43 ($($x:tt)*) => { 47 ($($x:tt)*) => {
44 { 48 {
@@ -50,6 +54,7 @@ macro_rules! debug_assert {
50 }; 54 };
51} 55}
52 56
57#[collapse_debuginfo(yes)]
53macro_rules! debug_assert_eq { 58macro_rules! debug_assert_eq {
54 ($($x:tt)*) => { 59 ($($x:tt)*) => {
55 { 60 {
@@ -61,6 +66,7 @@ macro_rules! debug_assert_eq {
61 }; 66 };
62} 67}
63 68
69#[collapse_debuginfo(yes)]
64macro_rules! debug_assert_ne { 70macro_rules! debug_assert_ne {
65 ($($x:tt)*) => { 71 ($($x:tt)*) => {
66 { 72 {
@@ -72,6 +78,7 @@ macro_rules! debug_assert_ne {
72 }; 78 };
73} 79}
74 80
81#[collapse_debuginfo(yes)]
75macro_rules! todo { 82macro_rules! todo {
76 ($($x:tt)*) => { 83 ($($x:tt)*) => {
77 { 84 {
@@ -83,20 +90,19 @@ macro_rules! todo {
83 }; 90 };
84} 91}
85 92
86#[cfg(not(feature = "defmt"))] 93#[collapse_debuginfo(yes)]
87macro_rules! unreachable { 94macro_rules! unreachable {
88 ($($x:tt)*) => { 95 ($($x:tt)*) => {
89 ::core::unreachable!($($x)*) 96 {
90 }; 97 #[cfg(not(feature = "defmt"))]
91} 98 ::core::unreachable!($($x)*);
92 99 #[cfg(feature = "defmt")]
93#[cfg(feature = "defmt")] 100 ::defmt::unreachable!($($x)*);
94macro_rules! unreachable { 101 }
95 ($($x:tt)*) => {
96 ::defmt::unreachable!($($x)*)
97 }; 102 };
98} 103}
99 104
105#[collapse_debuginfo(yes)]
100macro_rules! panic { 106macro_rules! panic {
101 ($($x:tt)*) => { 107 ($($x:tt)*) => {
102 { 108 {
@@ -108,6 +114,7 @@ macro_rules! panic {
108 }; 114 };
109} 115}
110 116
117#[collapse_debuginfo(yes)]
111macro_rules! trace { 118macro_rules! trace {
112 ($s:literal $(, $x:expr)* $(,)?) => { 119 ($s:literal $(, $x:expr)* $(,)?) => {
113 { 120 {
@@ -121,6 +128,7 @@ macro_rules! trace {
121 }; 128 };
122} 129}
123 130
131#[collapse_debuginfo(yes)]
124macro_rules! debug { 132macro_rules! debug {
125 ($s:literal $(, $x:expr)* $(,)?) => { 133 ($s:literal $(, $x:expr)* $(,)?) => {
126 { 134 {
@@ -134,6 +142,7 @@ macro_rules! debug {
134 }; 142 };
135} 143}
136 144
145#[collapse_debuginfo(yes)]
137macro_rules! info { 146macro_rules! info {
138 ($s:literal $(, $x:expr)* $(,)?) => { 147 ($s:literal $(, $x:expr)* $(,)?) => {
139 { 148 {
@@ -147,6 +156,7 @@ macro_rules! info {
147 }; 156 };
148} 157}
149 158
159#[collapse_debuginfo(yes)]
150macro_rules! warn { 160macro_rules! warn {
151 ($s:literal $(, $x:expr)* $(,)?) => { 161 ($s:literal $(, $x:expr)* $(,)?) => {
152 { 162 {
@@ -160,6 +170,7 @@ macro_rules! warn {
160 }; 170 };
161} 171}
162 172
173#[collapse_debuginfo(yes)]
163macro_rules! error { 174macro_rules! error {
164 ($s:literal $(, $x:expr)* $(,)?) => { 175 ($s:literal $(, $x:expr)* $(,)?) => {
165 { 176 {
@@ -174,6 +185,7 @@ macro_rules! error {
174} 185}
175 186
176#[cfg(feature = "defmt")] 187#[cfg(feature = "defmt")]
188#[collapse_debuginfo(yes)]
177macro_rules! unwrap { 189macro_rules! unwrap {
178 ($($x:tt)*) => { 190 ($($x:tt)*) => {
179 ::defmt::unwrap!($($x)*) 191 ::defmt::unwrap!($($x)*)
@@ -181,6 +193,7 @@ macro_rules! unwrap {
181} 193}
182 194
183#[cfg(not(feature = "defmt"))] 195#[cfg(not(feature = "defmt"))]
196#[collapse_debuginfo(yes)]
184macro_rules! unwrap { 197macro_rules! unwrap {
185 ($arg:expr) => { 198 ($arg:expr) => {
186 match $crate::fmt::Try::into_result($arg) { 199 match $crate::fmt::Try::into_result($arg) {
diff --git a/embassy-time/src/instant.rs b/embassy-time/src/instant.rs
index 909f1b173..de5ebebf8 100644
--- a/embassy-time/src/instant.rs
+++ b/embassy-time/src/instant.rs
@@ -1,7 +1,7 @@
1use core::fmt; 1use core::fmt;
2use core::ops::{Add, AddAssign, Sub, SubAssign}; 2use core::ops::{Add, AddAssign, Sub, SubAssign};
3 3
4use super::{Duration, GCD_1K, GCD_1M, TICK_HZ}; 4use super::{Duration, GCD_1G, GCD_1K, GCD_1M, TICK_HZ};
5 5
6#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord)] 6#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord)]
7#[cfg_attr(feature = "defmt", derive(defmt::Format))] 7#[cfg_attr(feature = "defmt", derive(defmt::Format))]
@@ -17,6 +17,7 @@ impl Instant {
17 pub const MAX: Instant = Instant { ticks: u64::MAX }; 17 pub const MAX: Instant = Instant { ticks: u64::MAX };
18 18
19 /// Returns an Instant representing the current time. 19 /// Returns an Instant representing the current time.
20 #[inline]
20 pub fn now() -> Instant { 21 pub fn now() -> Instant {
21 Instant { 22 Instant {
22 ticks: embassy_time_driver::now(), 23 ticks: embassy_time_driver::now(),
@@ -28,6 +29,13 @@ impl Instant {
28 Self { ticks } 29 Self { ticks }
29 } 30 }
30 31
32 /// Create an Instant from a nanosecond count since system boot.
33 pub const fn from_nanos(nanos: u64) -> Self {
34 Self {
35 ticks: nanos * (TICK_HZ / GCD_1G) / (1_000_000_000 / GCD_1G),
36 }
37 }
38
31 /// Create an Instant from a microsecond count since system boot. 39 /// Create an Instant from a microsecond count since system boot.
32 pub const fn from_micros(micros: u64) -> Self { 40 pub const fn from_micros(micros: u64) -> Self {
33 Self { 41 Self {
@@ -49,6 +57,48 @@ impl Instant {
49 } 57 }
50 } 58 }
51 59
60 /// Try to create an Instant from a nanosecond count since system boot.
61 /// Fails if the number of nanoseconds is too large.
62 pub const fn try_from_nanos(nanos: u64) -> Option<Self> {
63 let Some(value) = nanos.checked_mul(TICK_HZ / GCD_1G) else {
64 return None;
65 };
66 Some(Self {
67 ticks: value / (1_000_000_000 / GCD_1G),
68 })
69 }
70
71 /// Try to create an Instant from a microsecond count since system boot.
72 /// Fails if the number of microseconds is too large.
73 pub const fn try_from_micros(micros: u64) -> Option<Self> {
74 let Some(value) = micros.checked_mul(TICK_HZ / GCD_1M) else {
75 return None;
76 };
77 Some(Self {
78 ticks: value / (1_000_000 / GCD_1M),
79 })
80 }
81
82 /// Try to create an Instant from a millisecond count since system boot.
83 /// Fails if the number of milliseconds is too large.
84 pub const fn try_from_millis(millis: u64) -> Option<Self> {
85 let Some(value) = millis.checked_mul(TICK_HZ / GCD_1K) else {
86 return None;
87 };
88 Some(Self {
89 ticks: value / (1000 / GCD_1K),
90 })
91 }
92
93 /// Try to create an Instant from a second count since system boot.
94 /// Fails if the number of seconds is too large.
95 pub const fn try_from_secs(seconds: u64) -> Option<Self> {
96 let Some(ticks) = seconds.checked_mul(TICK_HZ) else {
97 return None;
98 };
99 Some(Self { ticks })
100 }
101
52 /// Tick count since system boot. 102 /// Tick count since system boot.
53 pub const fn as_ticks(&self) -> u64 { 103 pub const fn as_ticks(&self) -> u64 {
54 self.ticks 104 self.ticks
@@ -69,6 +119,11 @@ impl Instant {
69 self.ticks * (1_000_000 / GCD_1M) / (TICK_HZ / GCD_1M) 119 self.ticks * (1_000_000 / GCD_1M) / (TICK_HZ / GCD_1M)
70 } 120 }
71 121
122 /// Nanoseconds since system boot.
123 pub const fn as_nanos(&self) -> u64 {
124 self.ticks * (1_000_000_000 / GCD_1G) / (TICK_HZ / GCD_1G)
125 }
126
72 /// Duration between this Instant and another Instant 127 /// Duration between this Instant and another Instant
73 /// Panics on over/underflow. 128 /// Panics on over/underflow.
74 pub fn duration_since(&self, earlier: Instant) -> Duration { 129 pub fn duration_since(&self, earlier: Instant) -> Duration {
@@ -114,6 +169,18 @@ impl Instant {
114 pub fn checked_sub(&self, duration: Duration) -> Option<Instant> { 169 pub fn checked_sub(&self, duration: Duration) -> Option<Instant> {
115 self.ticks.checked_sub(duration.ticks).map(|ticks| Instant { ticks }) 170 self.ticks.checked_sub(duration.ticks).map(|ticks| Instant { ticks })
116 } 171 }
172
173 /// Adds a Duration to self. In case of overflow, the maximum value is returned.
174 pub fn saturating_add(mut self, duration: Duration) -> Self {
175 self.ticks = self.ticks.saturating_add(duration.ticks);
176 self
177 }
178
179 /// Subtracts a Duration from self. In case of overflow, the minimum value is returned.
180 pub fn saturating_sub(mut self, duration: Duration) -> Self {
181 self.ticks = self.ticks.saturating_sub(duration.ticks);
182 self
183 }
117} 184}
118 185
119impl Add<Duration> for Instant { 186impl Add<Duration> for Instant {
diff --git a/embassy-time/src/lib.rs b/embassy-time/src/lib.rs
index 24ee51be7..e375fe93e 100644
--- a/embassy-time/src/lib.rs
+++ b/embassy-time/src/lib.rs
@@ -1,8 +1,10 @@
1#![cfg_attr(not(any(feature = "std", feature = "wasm", test)), no_std)] 1#![cfg_attr(not(any(feature = "std", feature = "wasm", test)), no_std)]
2#![allow(async_fn_in_trait)] 2#![allow(async_fn_in_trait)]
3#![allow(unsafe_op_in_unsafe_fn)]
3#![doc = include_str!("../README.md")] 4#![doc = include_str!("../README.md")]
4#![allow(clippy::new_without_default)] 5#![allow(clippy::new_without_default)]
5#![warn(missing_docs)] 6#![warn(missing_docs)]
7#![deny(missing_debug_implementations)]
6 8
7//! ## Feature flags 9//! ## Feature flags
8#![doc = document_features::document_features!(feature_label = r#"<span class="stab portability"><code>{feature}</code></span>"#)] 10#![doc = document_features::document_features!(feature_label = r#"<span class="stab portability"><code>{feature}</code></span>"#)]
@@ -25,21 +27,15 @@ pub use driver_mock::MockDriver;
25mod driver_std; 27mod driver_std;
26#[cfg(feature = "wasm")] 28#[cfg(feature = "wasm")]
27mod driver_wasm; 29mod driver_wasm;
28#[cfg(feature = "generic-queue")]
29mod queue_generic;
30 30
31pub use delay::{block_for, Delay}; 31pub use delay::{Delay, block_for};
32pub use duration::Duration; 32pub use duration::Duration;
33pub use embassy_time_driver::TICK_HZ; 33pub use embassy_time_driver::TICK_HZ;
34pub use instant::Instant; 34pub use instant::Instant;
35pub use timer::{with_deadline, with_timeout, Ticker, TimeoutError, Timer}; 35pub use timer::{Ticker, TimeoutError, Timer, WithTimeout, with_deadline, with_timeout};
36 36
37const fn gcd(a: u64, b: u64) -> u64 { 37const fn gcd(a: u64, b: u64) -> u64 {
38 if b == 0 { 38 if b == 0 { a } else { gcd(b, a % b) }
39 a
40 } else {
41 gcd(b, a % b)
42 }
43} 39}
44 40
45pub(crate) const GCD_1K: u64 = gcd(TICK_HZ, 1_000); 41pub(crate) const GCD_1K: u64 = gcd(TICK_HZ, 1_000);
diff --git a/embassy-time/src/queue_generic.rs b/embassy-time/src/queue_generic.rs
deleted file mode 100644
index 4882afd3e..000000000
--- a/embassy-time/src/queue_generic.rs
+++ /dev/null
@@ -1,348 +0,0 @@
1use core::cell::RefCell;
2use core::cmp::{min, Ordering};
3use core::task::Waker;
4
5use critical_section::Mutex;
6use embassy_time_driver::{allocate_alarm, set_alarm, set_alarm_callback, AlarmHandle};
7use embassy_time_queue_driver::TimerQueue;
8use heapless::Vec;
9
10use crate::Instant;
11
12#[cfg(feature = "generic-queue-8")]
13const QUEUE_SIZE: usize = 8;
14#[cfg(feature = "generic-queue-16")]
15const QUEUE_SIZE: usize = 16;
16#[cfg(feature = "generic-queue-32")]
17const QUEUE_SIZE: usize = 32;
18#[cfg(feature = "generic-queue-64")]
19const QUEUE_SIZE: usize = 64;
20#[cfg(feature = "generic-queue-128")]
21const QUEUE_SIZE: usize = 128;
22#[cfg(not(any(
23 feature = "generic-queue-8",
24 feature = "generic-queue-16",
25 feature = "generic-queue-32",
26 feature = "generic-queue-64",
27 feature = "generic-queue-128"
28)))]
29const QUEUE_SIZE: usize = 64;
30
31#[derive(Debug)]
32struct Timer {
33 at: Instant,
34 waker: Waker,
35}
36
37impl PartialEq for Timer {
38 fn eq(&self, other: &Self) -> bool {
39 self.at == other.at
40 }
41}
42
43impl Eq for Timer {}
44
45impl PartialOrd for Timer {
46 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
47 self.at.partial_cmp(&other.at)
48 }
49}
50
51impl Ord for Timer {
52 fn cmp(&self, other: &Self) -> Ordering {
53 self.at.cmp(&other.at)
54 }
55}
56
57struct InnerQueue {
58 queue: Vec<Timer, QUEUE_SIZE>,
59 alarm: AlarmHandle,
60}
61
62impl InnerQueue {
63 fn schedule_wake(&mut self, at: Instant, waker: &Waker) {
64 self.queue
65 .iter_mut()
66 .find(|timer| timer.waker.will_wake(waker))
67 .map(|timer| {
68 timer.at = min(timer.at, at);
69 })
70 .unwrap_or_else(|| {
71 let mut timer = Timer {
72 waker: waker.clone(),
73 at,
74 };
75
76 loop {
77 match self.queue.push(timer) {
78 Ok(()) => break,
79 Err(e) => timer = e,
80 }
81
82 self.queue.pop().unwrap().waker.wake();
83 }
84 });
85
86 // Don't wait for the alarm callback to trigger and directly
87 // dispatch all timers that are already due
88 //
89 // Then update the alarm if necessary
90 self.dispatch();
91 }
92
93 fn dispatch(&mut self) {
94 loop {
95 let now = Instant::now();
96
97 let mut next_alarm = Instant::MAX;
98
99 let mut i = 0;
100 while i < self.queue.len() {
101 let timer = &self.queue[i];
102 if timer.at <= now {
103 let timer = self.queue.swap_remove(i);
104 timer.waker.wake();
105 } else {
106 next_alarm = min(next_alarm, timer.at);
107 i += 1;
108 }
109 }
110
111 if self.update_alarm(next_alarm) {
112 break;
113 }
114 }
115 }
116
117 fn update_alarm(&mut self, next_alarm: Instant) -> bool {
118 if next_alarm == Instant::MAX {
119 true
120 } else {
121 set_alarm(self.alarm, next_alarm.as_ticks())
122 }
123 }
124
125 fn handle_alarm(&mut self) {
126 self.dispatch();
127 }
128}
129
130struct Queue {
131 inner: Mutex<RefCell<Option<InnerQueue>>>,
132}
133
134impl Queue {
135 const fn new() -> Self {
136 Self {
137 inner: Mutex::new(RefCell::new(None)),
138 }
139 }
140
141 fn schedule_wake(&'static self, at: Instant, waker: &Waker) {
142 critical_section::with(|cs| {
143 let mut inner = self.inner.borrow_ref_mut(cs);
144
145 if inner.is_none() {}
146
147 inner
148 .get_or_insert_with(|| {
149 let handle = unsafe { allocate_alarm() }.unwrap();
150 set_alarm_callback(handle, Self::handle_alarm_callback, self as *const _ as _);
151 InnerQueue {
152 queue: Vec::new(),
153 alarm: handle,
154 }
155 })
156 .schedule_wake(at, waker)
157 });
158 }
159
160 fn handle_alarm(&self) {
161 critical_section::with(|cs| self.inner.borrow_ref_mut(cs).as_mut().unwrap().handle_alarm())
162 }
163
164 fn handle_alarm_callback(ctx: *mut ()) {
165 unsafe { (ctx as *const Self).as_ref().unwrap() }.handle_alarm();
166 }
167}
168
169impl TimerQueue for Queue {
170 fn schedule_wake(&'static self, at: u64, waker: &Waker) {
171 Queue::schedule_wake(self, Instant::from_ticks(at), waker);
172 }
173}
174
175embassy_time_queue_driver::timer_queue_impl!(static QUEUE: Queue = Queue::new());
176
177#[cfg(test)]
178#[cfg(feature = "mock-driver")]
179mod tests {
180 use core::sync::atomic::{AtomicBool, Ordering};
181 use core::task::Waker;
182 use std::sync::Arc;
183 use std::task::Wake;
184
185 use serial_test::serial;
186
187 use crate::driver_mock::MockDriver;
188 use crate::queue_generic::QUEUE;
189 use crate::{Duration, Instant};
190
191 struct TestWaker {
192 pub awoken: AtomicBool,
193 }
194
195 impl Wake for TestWaker {
196 fn wake(self: Arc<Self>) {
197 self.awoken.store(true, Ordering::Relaxed);
198 }
199
200 fn wake_by_ref(self: &Arc<Self>) {
201 self.awoken.store(true, Ordering::Relaxed);
202 }
203 }
204
205 fn test_waker() -> (Arc<TestWaker>, Waker) {
206 let arc = Arc::new(TestWaker {
207 awoken: AtomicBool::new(false),
208 });
209 let waker = Waker::from(arc.clone());
210
211 (arc, waker)
212 }
213
214 fn setup() {
215 MockDriver::get().reset();
216 critical_section::with(|cs| *QUEUE.inner.borrow_ref_mut(cs) = None);
217 }
218
219 fn queue_len() -> usize {
220 critical_section::with(|cs| {
221 QUEUE
222 .inner
223 .borrow_ref(cs)
224 .as_ref()
225 .map(|inner| inner.queue.iter().count())
226 .unwrap_or(0)
227 })
228 }
229
230 #[test]
231 #[serial]
232 fn test_schedule() {
233 setup();
234
235 assert_eq!(queue_len(), 0);
236
237 let (flag, waker) = test_waker();
238
239 QUEUE.schedule_wake(Instant::from_secs(1), &waker);
240
241 assert!(!flag.awoken.load(Ordering::Relaxed));
242 assert_eq!(queue_len(), 1);
243 }
244
245 #[test]
246 #[serial]
247 fn test_schedule_same() {
248 setup();
249
250 let (_flag, waker) = test_waker();
251
252 QUEUE.schedule_wake(Instant::from_secs(1), &waker);
253
254 assert_eq!(queue_len(), 1);
255
256 QUEUE.schedule_wake(Instant::from_secs(1), &waker);
257
258 assert_eq!(queue_len(), 1);
259
260 QUEUE.schedule_wake(Instant::from_secs(100), &waker);
261
262 assert_eq!(queue_len(), 1);
263
264 let (_flag2, waker2) = test_waker();
265
266 QUEUE.schedule_wake(Instant::from_secs(100), &waker2);
267
268 assert_eq!(queue_len(), 2);
269 }
270
271 #[test]
272 #[serial]
273 fn test_trigger() {
274 setup();
275
276 let (flag, waker) = test_waker();
277
278 QUEUE.schedule_wake(Instant::from_secs(100), &waker);
279
280 assert!(!flag.awoken.load(Ordering::Relaxed));
281
282 MockDriver::get().advance(Duration::from_secs(99));
283
284 assert!(!flag.awoken.load(Ordering::Relaxed));
285
286 assert_eq!(queue_len(), 1);
287
288 MockDriver::get().advance(Duration::from_secs(1));
289
290 assert!(flag.awoken.load(Ordering::Relaxed));
291
292 assert_eq!(queue_len(), 0);
293 }
294
295 #[test]
296 #[serial]
297 fn test_immediate_trigger() {
298 setup();
299
300 let (flag, waker) = test_waker();
301
302 QUEUE.schedule_wake(Instant::from_secs(100), &waker);
303
304 MockDriver::get().advance(Duration::from_secs(50));
305
306 let (flag2, waker2) = test_waker();
307
308 QUEUE.schedule_wake(Instant::from_secs(40), &waker2);
309
310 assert!(!flag.awoken.load(Ordering::Relaxed));
311 assert!(flag2.awoken.load(Ordering::Relaxed));
312 assert_eq!(queue_len(), 1);
313 }
314
315 #[test]
316 #[serial]
317 fn test_queue_overflow() {
318 setup();
319
320 for i in 1..super::QUEUE_SIZE {
321 let (flag, waker) = test_waker();
322
323 QUEUE.schedule_wake(Instant::from_secs(310), &waker);
324
325 assert_eq!(queue_len(), i);
326 assert!(!flag.awoken.load(Ordering::Relaxed));
327 }
328
329 let (flag, waker) = test_waker();
330
331 QUEUE.schedule_wake(Instant::from_secs(300), &waker);
332
333 assert_eq!(queue_len(), super::QUEUE_SIZE);
334 assert!(!flag.awoken.load(Ordering::Relaxed));
335
336 let (flag2, waker2) = test_waker();
337
338 QUEUE.schedule_wake(Instant::from_secs(305), &waker2);
339
340 assert_eq!(queue_len(), super::QUEUE_SIZE);
341 assert!(flag.awoken.load(Ordering::Relaxed));
342
343 let (_flag3, waker3) = test_waker();
344 QUEUE.schedule_wake(Instant::from_secs(320), &waker3);
345 assert_eq!(queue_len(), super::QUEUE_SIZE);
346 assert!(flag2.awoken.load(Ordering::Relaxed));
347 }
348}
diff --git a/embassy-time/src/timer.rs b/embassy-time/src/timer.rs
index bc39d8bc7..2f5967c63 100644
--- a/embassy-time/src/timer.rs
+++ b/embassy-time/src/timer.rs
@@ -1,10 +1,9 @@
1use core::future::{poll_fn, Future}; 1use core::future::{Future, poll_fn};
2use core::pin::{pin, Pin}; 2use core::pin::Pin;
3use core::task::{Context, Poll}; 3use core::task::{Context, Poll};
4 4
5use futures_util::future::{select, Either}; 5use futures_core::Stream;
6use futures_util::stream::FusedStream; 6use futures_core::stream::FusedStream;
7use futures_util::Stream;
8 7
9use crate::{Duration, Instant}; 8use crate::{Duration, Instant};
10 9
@@ -17,11 +16,10 @@ pub struct TimeoutError;
17/// 16///
18/// If the future completes before the timeout, its output is returned. Otherwise, on timeout, 17/// If the future completes before the timeout, its output is returned. Otherwise, on timeout,
19/// work on the future is stopped (`poll` is no longer called), the future is dropped and `Err(TimeoutError)` is returned. 18/// work on the future is stopped (`poll` is no longer called), the future is dropped and `Err(TimeoutError)` is returned.
20pub async fn with_timeout<F: Future>(timeout: Duration, fut: F) -> Result<F::Output, TimeoutError> { 19pub fn with_timeout<F: Future>(timeout: Duration, fut: F) -> TimeoutFuture<F> {
21 let timeout_fut = Timer::after(timeout); 20 TimeoutFuture {
22 match select(pin!(fut), timeout_fut).await { 21 timer: Timer::after(timeout),
23 Either::Left((r, _)) => Ok(r), 22 fut,
24 Either::Right(_) => Err(TimeoutError),
25 } 23 }
26} 24}
27 25
@@ -29,16 +27,75 @@ pub async fn with_timeout<F: Future>(timeout: Duration, fut: F) -> Result<F::Out
29/// 27///
30/// If the future completes before the deadline, its output is returned. Otherwise, on timeout, 28/// If the future completes before the deadline, its output is returned. Otherwise, on timeout,
31/// work on the future is stopped (`poll` is no longer called), the future is dropped and `Err(TimeoutError)` is returned. 29/// work on the future is stopped (`poll` is no longer called), the future is dropped and `Err(TimeoutError)` is returned.
32pub async fn with_deadline<F: Future>(at: Instant, fut: F) -> Result<F::Output, TimeoutError> { 30pub fn with_deadline<F: Future>(at: Instant, fut: F) -> TimeoutFuture<F> {
33 let timeout_fut = Timer::at(at); 31 TimeoutFuture {
34 match select(pin!(fut), timeout_fut).await { 32 timer: Timer::at(at),
35 Either::Left((r, _)) => Ok(r), 33 fut,
36 Either::Right(_) => Err(TimeoutError), 34 }
35}
36
37/// Provides functions to run a given future with a timeout or a deadline.
38pub trait WithTimeout: Sized {
39 /// Output type of the future.
40 type Output;
41
42 /// Runs a given future with a timeout.
43 ///
44 /// If the future completes before the timeout, its output is returned. Otherwise, on timeout,
45 /// work on the future is stopped (`poll` is no longer called), the future is dropped and `Err(TimeoutError)` is returned.
46 fn with_timeout(self, timeout: Duration) -> TimeoutFuture<Self>;
47
48 /// Runs a given future with a deadline time.
49 ///
50 /// If the future completes before the deadline, its output is returned. Otherwise, on timeout,
51 /// work on the future is stopped (`poll` is no longer called), the future is dropped and `Err(TimeoutError)` is returned.
52 fn with_deadline(self, at: Instant) -> TimeoutFuture<Self>;
53}
54
55impl<F: Future> WithTimeout for F {
56 type Output = F::Output;
57
58 fn with_timeout(self, timeout: Duration) -> TimeoutFuture<Self> {
59 with_timeout(timeout, self)
60 }
61
62 fn with_deadline(self, at: Instant) -> TimeoutFuture<Self> {
63 with_deadline(at, self)
64 }
65}
66
67/// Future for the [`with_timeout`] and [`with_deadline`] functions.
68#[must_use = "futures do nothing unless you `.await` or poll them"]
69#[derive(Debug)]
70#[cfg_attr(feature = "defmt", derive(defmt::Format))]
71pub struct TimeoutFuture<F> {
72 timer: Timer,
73 fut: F,
74}
75
76impl<F: Unpin> Unpin for TimeoutFuture<F> {}
77
78impl<F: Future> Future for TimeoutFuture<F> {
79 type Output = Result<F::Output, TimeoutError>;
80
81 fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
82 let this = unsafe { self.get_unchecked_mut() };
83 let fut = unsafe { Pin::new_unchecked(&mut this.fut) };
84 let timer = unsafe { Pin::new_unchecked(&mut this.timer) };
85 if let Poll::Ready(x) = fut.poll(cx) {
86 return Poll::Ready(Ok(x));
87 }
88 if let Poll::Ready(_) = timer.poll(cx) {
89 return Poll::Ready(Err(TimeoutError));
90 }
91 Poll::Pending
37 } 92 }
38} 93}
39 94
40/// A future that completes at a specified [Instant](struct.Instant.html). 95/// A future that completes at a specified [Instant](struct.Instant.html).
41#[must_use = "futures do nothing unless you `.await` or poll them"] 96#[must_use = "futures do nothing unless you `.await` or poll them"]
97#[derive(Debug)]
98#[cfg_attr(feature = "defmt", derive(defmt::Format))]
42pub struct Timer { 99pub struct Timer {
43 expires_at: Instant, 100 expires_at: Instant,
44 yielded_once: bool, 101 yielded_once: bool,
@@ -46,6 +103,7 @@ pub struct Timer {
46 103
47impl Timer { 104impl Timer {
48 /// Expire at specified [Instant](struct.Instant.html) 105 /// Expire at specified [Instant](struct.Instant.html)
106 /// Will expire immediately if the Instant is in the past.
49 pub fn at(expires_at: Instant) -> Self { 107 pub fn at(expires_at: Instant) -> Self {
50 Self { 108 Self {
51 expires_at, 109 expires_at,
@@ -127,7 +185,7 @@ impl Future for Timer {
127 if self.yielded_once && self.expires_at <= Instant::now() { 185 if self.yielded_once && self.expires_at <= Instant::now() {
128 Poll::Ready(()) 186 Poll::Ready(())
129 } else { 187 } else {
130 embassy_time_queue_driver::schedule_wake(self.expires_at.as_ticks(), cx.waker()); 188 embassy_time_driver::schedule_wake(self.expires_at.as_ticks(), cx.waker());
131 self.yielded_once = true; 189 self.yielded_once = true;
132 Poll::Pending 190 Poll::Pending
133 } 191 }
@@ -170,6 +228,12 @@ impl Future for Timer {
170/// } 228/// }
171/// } 229/// }
172/// ``` 230/// ```
231///
232/// ## Cancel safety
233/// It is safe to cancel waiting for the next tick,
234/// meaning no tick is lost if the Future is dropped.
235#[derive(Debug)]
236#[cfg_attr(feature = "defmt", derive(defmt::Format))]
173pub struct Ticker { 237pub struct Ticker {
174 expires_at: Instant, 238 expires_at: Instant,
175 duration: Duration, 239 duration: Duration,
@@ -201,6 +265,9 @@ impl Ticker {
201 } 265 }
202 266
203 /// Waits for the next tick. 267 /// Waits for the next tick.
268 ///
269 /// ## Cancel safety
270 /// The produced Future is cancel safe, meaning no tick is lost if the Future is dropped.
204 pub fn next(&mut self) -> impl Future<Output = ()> + Send + Sync + '_ { 271 pub fn next(&mut self) -> impl Future<Output = ()> + Send + Sync + '_ {
205 poll_fn(|cx| { 272 poll_fn(|cx| {
206 if self.expires_at <= Instant::now() { 273 if self.expires_at <= Instant::now() {
@@ -208,7 +275,7 @@ impl Ticker {
208 self.expires_at += dur; 275 self.expires_at += dur;
209 Poll::Ready(()) 276 Poll::Ready(())
210 } else { 277 } else {
211 embassy_time_queue_driver::schedule_wake(self.expires_at.as_ticks(), cx.waker()); 278 embassy_time_driver::schedule_wake(self.expires_at.as_ticks(), cx.waker());
212 Poll::Pending 279 Poll::Pending
213 } 280 }
214 }) 281 })
@@ -225,7 +292,7 @@ impl Stream for Ticker {
225 self.expires_at += dur; 292 self.expires_at += dur;
226 Poll::Ready(Some(())) 293 Poll::Ready(Some(()))
227 } else { 294 } else {
228 embassy_time_queue_driver::schedule_wake(self.expires_at.as_ticks(), cx.waker()); 295 embassy_time_driver::schedule_wake(self.expires_at.as_ticks(), cx.waker());
229 Poll::Pending 296 Poll::Pending
230 } 297 }
231 } 298 }