diff options
Diffstat (limited to 'embassy-time-driver/src/lib.rs')
| -rw-r--r-- | embassy-time-driver/src/lib.rs | 215 |
1 files changed, 72 insertions, 143 deletions
diff --git a/embassy-time-driver/src/lib.rs b/embassy-time-driver/src/lib.rs index 12f40b9b9..57a9f7587 100644 --- a/embassy-time-driver/src/lib.rs +++ b/embassy-time-driver/src/lib.rs | |||
| @@ -17,28 +17,12 @@ | |||
| 17 | //! Otherwise, don’t enable any `tick-hz-*` feature to let the user configure the tick rate themselves by | 17 | //! Otherwise, don’t enable any `tick-hz-*` feature to let the user configure the tick rate themselves by |
| 18 | //! enabling a feature on `embassy-time`. | 18 | //! enabling a feature on `embassy-time`. |
| 19 | //! | 19 | //! |
| 20 | //! # Linkage details | 20 | //! ### Example |
| 21 | //! | ||
| 22 | //! Instead of the usual "trait + generic params" approach, calls from embassy to the driver are done via `extern` functions. | ||
| 23 | //! | ||
| 24 | //! `embassy` internally defines the driver functions as `extern "Rust" { fn _embassy_time_now() -> u64; }` and calls them. | ||
| 25 | //! The driver crate defines the functions as `#[no_mangle] fn _embassy_time_now() -> u64`. The linker will resolve the | ||
| 26 | //! calls from the `embassy` crate to call into the driver crate. | ||
| 27 | //! | ||
| 28 | //! If there is none or multiple drivers in the crate tree, linking will fail. | ||
| 29 | //! | ||
| 30 | //! This method has a few key advantages for something as foundational as timekeeping: | ||
| 31 | //! | ||
| 32 | //! - The time driver is available everywhere easily, without having to thread the implementation | ||
| 33 | //! through generic parameters. This is especially helpful for libraries. | ||
| 34 | //! - It means comparing `Instant`s will always make sense: if there were multiple drivers | ||
| 35 | //! active, one could compare an `Instant` from driver A to an `Instant` from driver B, which | ||
| 36 | //! would yield incorrect results. | ||
| 37 | //! | ||
| 38 | //! # Example | ||
| 39 | //! | 21 | //! |
| 40 | //! ``` | 22 | //! ``` |
| 41 | //! use embassy_time_driver::{Driver, AlarmHandle}; | 23 | //! use core::task::Waker; |
| 24 | //! | ||
| 25 | //! use embassy_time_driver::Driver; | ||
| 42 | //! | 26 | //! |
| 43 | //! struct MyDriver{} // not public! | 27 | //! struct MyDriver{} // not public! |
| 44 | //! | 28 | //! |
| @@ -46,23 +30,79 @@ | |||
| 46 | //! fn now(&self) -> u64 { | 30 | //! fn now(&self) -> u64 { |
| 47 | //! todo!() | 31 | //! todo!() |
| 48 | //! } | 32 | //! } |
| 49 | //! unsafe fn allocate_alarm(&self) -> Option<AlarmHandle> { | 33 | //! |
| 50 | //! todo!() | 34 | //! fn schedule_wake(&self, at: u64, waker: &Waker) { |
| 51 | //! } | ||
| 52 | //! fn set_alarm_callback(&self, alarm: AlarmHandle, callback: fn(*mut ()), ctx: *mut ()) { | ||
| 53 | //! todo!() | ||
| 54 | //! } | ||
| 55 | //! fn set_alarm(&self, alarm: AlarmHandle, timestamp: u64) -> bool { | ||
| 56 | //! todo!() | 35 | //! todo!() |
| 57 | //! } | 36 | //! } |
| 58 | //! } | 37 | //! } |
| 59 | //! | 38 | //! |
| 60 | //! embassy_time_driver::time_driver_impl!(static DRIVER: MyDriver = MyDriver{}); | 39 | //! embassy_time_driver::time_driver_impl!(static DRIVER: MyDriver = MyDriver{}); |
| 61 | //! ``` | 40 | //! ``` |
| 41 | //! | ||
| 42 | //! ## Implementing the timer queue | ||
| 43 | //! | ||
| 44 | //! The simplest (but suboptimal) way to implement a timer queue is to define a single queue in the | ||
| 45 | //! time driver. Declare a field protected by an appropriate mutex (e.g. `critical_section::Mutex`). | ||
| 46 | //! | ||
| 47 | //! Then, you'll need to adapt the `schedule_wake` method to use this queue. | ||
| 48 | //! | ||
| 49 | //! ```ignore | ||
| 50 | //! use core::cell::RefCell; | ||
| 51 | //! use core::task::Waker; | ||
| 52 | //! | ||
| 53 | //! use embassy_time_queue_driver::Queue; | ||
| 54 | //! use embassy_time_driver::Driver; | ||
| 55 | //! | ||
| 56 | //! struct MyDriver { | ||
| 57 | //! timer_queue: critical_section::Mutex<RefCell<Queue>>, | ||
| 58 | //! } | ||
| 59 | //! | ||
| 60 | //! impl MyDriver { | ||
| 61 | //! fn set_alarm(&self, cs: &CriticalSection, at: u64) -> bool { | ||
| 62 | //! todo!() | ||
| 63 | //! } | ||
| 64 | //! } | ||
| 65 | //! | ||
| 66 | //! impl Driver for MyDriver { | ||
| 67 | //! // fn now(&self) -> u64 { ... } | ||
| 68 | //! | ||
| 69 | //! fn schedule_wake(&self, at: u64, waker: &Waker) { | ||
| 70 | //! critical_section::with(|cs| { | ||
| 71 | //! let mut queue = self.queue.borrow(cs).borrow_mut(); | ||
| 72 | //! if queue.schedule_wake(at, waker) { | ||
| 73 | //! let mut next = queue.next_expiration(self.now()); | ||
| 74 | //! while !self.set_alarm(cs, next) { | ||
| 75 | //! next = queue.next_expiration(self.now()); | ||
| 76 | //! } | ||
| 77 | //! } | ||
| 78 | //! }); | ||
| 79 | //! } | ||
| 80 | //! } | ||
| 81 | //! ``` | ||
| 82 | //! | ||
| 83 | //! # Linkage details | ||
| 84 | //! | ||
| 85 | //! Instead of the usual "trait + generic params" approach, calls from embassy to the driver are done via `extern` functions. | ||
| 86 | //! | ||
| 87 | //! `embassy` internally defines the driver function as `extern "Rust" { fn _embassy_time_now() -> u64; }` and calls it. | ||
| 88 | //! The driver crate defines the function as `#[no_mangle] fn _embassy_time_now() -> u64`. The linker will resolve the | ||
| 89 | //! calls from the `embassy` crate to call into the driver crate. | ||
| 90 | //! | ||
| 91 | //! If there is none or multiple drivers in the crate tree, linking will fail. | ||
| 92 | //! | ||
| 93 | //! This method has a few key advantages for something as foundational as timekeeping: | ||
| 94 | //! | ||
| 95 | //! - The time driver is available everywhere easily, without having to thread the implementation | ||
| 96 | //! through generic parameters. This is especially helpful for libraries. | ||
| 97 | //! - It means comparing `Instant`s will always make sense: if there were multiple drivers | ||
| 98 | //! active, one could compare an `Instant` from driver A to an `Instant` from driver B, which | ||
| 99 | //! would yield incorrect results. | ||
| 62 | 100 | ||
| 63 | //! ## Feature flags | 101 | //! ## Feature flags |
| 64 | #![doc = document_features::document_features!(feature_label = r#"<span class="stab portability"><code>{feature}</code></span>"#)] | 102 | #![doc = document_features::document_features!(feature_label = r#"<span class="stab portability"><code>{feature}</code></span>"#)] |
| 65 | 103 | ||
| 104 | use core::task::Waker; | ||
| 105 | |||
| 66 | mod tick; | 106 | mod tick; |
| 67 | 107 | ||
| 68 | /// Ticks per second of the global timebase. | 108 | /// Ticks per second of the global timebase. |
| @@ -70,28 +110,6 @@ mod tick; | |||
| 70 | /// This value is specified by the [`tick-*` Cargo features](crate#tick-rate) | 110 | /// This value is specified by the [`tick-*` Cargo features](crate#tick-rate) |
| 71 | pub const TICK_HZ: u64 = tick::TICK_HZ; | 111 | pub const TICK_HZ: u64 = tick::TICK_HZ; |
| 72 | 112 | ||
| 73 | /// Alarm handle, assigned by the driver. | ||
| 74 | #[derive(Clone, Copy)] | ||
| 75 | pub struct AlarmHandle { | ||
| 76 | id: u8, | ||
| 77 | } | ||
| 78 | |||
| 79 | impl AlarmHandle { | ||
| 80 | /// Create an AlarmHandle | ||
| 81 | /// | ||
| 82 | /// Safety: May only be called by the current global Driver impl. | ||
| 83 | /// The impl is allowed to rely on the fact that all `AlarmHandle` instances | ||
| 84 | /// are created by itself in unsafe code (e.g. indexing operations) | ||
| 85 | pub unsafe fn new(id: u8) -> Self { | ||
| 86 | Self { id } | ||
| 87 | } | ||
| 88 | |||
| 89 | /// Get the ID of the AlarmHandle. | ||
| 90 | pub fn id(&self) -> u8 { | ||
| 91 | self.id | ||
| 92 | } | ||
| 93 | } | ||
| 94 | |||
| 95 | /// Time driver | 113 | /// Time driver |
| 96 | pub trait Driver: Send + Sync + 'static { | 114 | pub trait Driver: Send + Sync + 'static { |
| 97 | /// Return the current timestamp in ticks. | 115 | /// Return the current timestamp in ticks. |
| @@ -106,75 +124,13 @@ pub trait Driver: Send + Sync + 'static { | |||
| 106 | /// or chaining multiple timers together. | 124 | /// or chaining multiple timers together. |
| 107 | fn now(&self) -> u64; | 125 | fn now(&self) -> u64; |
| 108 | 126 | ||
| 109 | /// Try allocating an alarm handle. Returns None if no alarms left. | 127 | /// Schedules a waker to be awoken at moment `at`. |
| 110 | /// Initially the alarm has no callback set, and a null `ctx` pointer. | 128 | /// If this moment is in the past, the waker might be awoken immediately. |
| 111 | /// | 129 | fn schedule_wake(&self, at: u64, waker: &Waker); |
| 112 | /// The allocated alarm is a reusable resource and can be used multiple times. | ||
| 113 | /// Once the alarm has fired, it remains allocated and can be set again without needing | ||
| 114 | /// to be reallocated. | ||
| 115 | /// | ||
| 116 | /// # Safety | ||
| 117 | /// It is UB to make the alarm fire before setting a callback. | ||
| 118 | unsafe fn allocate_alarm(&self) -> Option<AlarmHandle>; | ||
| 119 | |||
| 120 | /// Set the callback function to be called when the alarm triggers. | ||
| 121 | /// The callback may be called from any context (interrupt or thread mode). | ||
| 122 | /// | ||
| 123 | /// The callback is maintained after the alarm has fired. Callers do not need | ||
| 124 | /// to set a callback again before setting another alarm, unless they want to | ||
| 125 | /// change the callback function or context. | ||
| 126 | fn set_alarm_callback(&self, alarm: AlarmHandle, callback: fn(*mut ()), ctx: *mut ()); | ||
| 127 | |||
| 128 | /// Set an alarm at the given timestamp. | ||
| 129 | /// | ||
| 130 | /// ## Behavior | ||
| 131 | /// | ||
| 132 | /// If `timestamp` is in the future, `set_alarm` schedules calling the callback function | ||
| 133 | /// at that time, and returns `true`. | ||
| 134 | /// | ||
| 135 | /// If `timestamp` is in the past, `set_alarm` has two allowed behaviors. Implementations can pick whether to: | ||
| 136 | /// | ||
| 137 | /// - Schedule calling the callback function "immediately", as if the requested timestamp was "now+epsilon" and return `true`, or | ||
| 138 | /// - Not schedule the callback, and return `false`. | ||
| 139 | /// | ||
| 140 | /// Callers must ensure to behave correctly with either behavior. | ||
| 141 | /// | ||
| 142 | /// When callback is called, it is guaranteed that `now()` will return a value greater than or equal to `timestamp`. | ||
| 143 | /// | ||
| 144 | /// ## Reentrancy | ||
| 145 | /// | ||
| 146 | /// Calling the callback from `set_alarm` synchronously is not allowed. If the implementation chooses the first option above, | ||
| 147 | /// it must still call the callback from another context (i.e. an interrupt handler or background thread), it's not allowed | ||
| 148 | /// to call it synchronously in the context `set_alarm` is running. | ||
| 149 | /// | ||
| 150 | /// The reason for the above is callers are explicitly permitted to do both of: | ||
| 151 | /// - Lock a mutex in the alarm callback. | ||
| 152 | /// - Call `set_alarm` while having that mutex locked. | ||
| 153 | /// | ||
| 154 | /// If `set_alarm` called the callback synchronously, it'd cause a deadlock or panic because it'd cause the | ||
| 155 | /// mutex to be locked twice reentrantly in the same context. | ||
| 156 | /// | ||
| 157 | /// ## Overwriting alarms | ||
| 158 | /// | ||
| 159 | /// Only one alarm can be active at a time for each `AlarmHandle`. This overwrites any previously-set alarm if any. | ||
| 160 | /// | ||
| 161 | /// ## Unsetting the alarm | ||
| 162 | /// | ||
| 163 | /// There is no `unset_alarm` API. Instead, callers can call `set_alarm` with `timestamp` set to `u64::MAX`. | ||
| 164 | /// | ||
| 165 | /// This allows for more efficient implementations, since they don't need to distinguish between the "alarm set" and | ||
| 166 | /// "alarm not set" cases, thanks to the fact "Alarm set for u64::MAX" is effectively equivalent for "alarm not set". | ||
| 167 | /// | ||
| 168 | /// This means implementations need to be careful to avoid timestamp overflows. The recommendation is to make `timestamp` | ||
| 169 | /// be in the same units as hardware ticks to avoid any conversions, which makes avoiding overflow easier. | ||
| 170 | fn set_alarm(&self, alarm: AlarmHandle, timestamp: u64) -> bool; | ||
| 171 | } | 130 | } |
| 172 | 131 | ||
| 173 | extern "Rust" { | 132 | extern "Rust" { |
| 174 | fn _embassy_time_now() -> u64; | 133 | fn _embassy_time_now() -> u64; |
| 175 | fn _embassy_time_allocate_alarm() -> Option<AlarmHandle>; | ||
| 176 | fn _embassy_time_set_alarm_callback(alarm: AlarmHandle, callback: fn(*mut ()), ctx: *mut ()); | ||
| 177 | fn _embassy_time_set_alarm(alarm: AlarmHandle, timestamp: u64) -> bool; | ||
| 178 | } | 134 | } |
| 179 | 135 | ||
| 180 | /// See [`Driver::now`] | 136 | /// See [`Driver::now`] |
| @@ -182,23 +138,6 @@ pub fn now() -> u64 { | |||
| 182 | unsafe { _embassy_time_now() } | 138 | unsafe { _embassy_time_now() } |
| 183 | } | 139 | } |
| 184 | 140 | ||
| 185 | /// See [`Driver::allocate_alarm`] | ||
| 186 | /// | ||
| 187 | /// Safety: it is UB to make the alarm fire before setting a callback. | ||
| 188 | pub unsafe fn allocate_alarm() -> Option<AlarmHandle> { | ||
| 189 | _embassy_time_allocate_alarm() | ||
| 190 | } | ||
| 191 | |||
| 192 | /// See [`Driver::set_alarm_callback`] | ||
| 193 | pub fn set_alarm_callback(alarm: AlarmHandle, callback: fn(*mut ()), ctx: *mut ()) { | ||
| 194 | unsafe { _embassy_time_set_alarm_callback(alarm, callback, ctx) } | ||
| 195 | } | ||
| 196 | |||
| 197 | /// See [`Driver::set_alarm`] | ||
| 198 | pub fn set_alarm(alarm: AlarmHandle, timestamp: u64) -> bool { | ||
| 199 | unsafe { _embassy_time_set_alarm(alarm, timestamp) } | ||
| 200 | } | ||
| 201 | |||
| 202 | /// Set the time Driver implementation. | 141 | /// Set the time Driver implementation. |
| 203 | /// | 142 | /// |
| 204 | /// See the module documentation for an example. | 143 | /// See the module documentation for an example. |
| @@ -213,18 +152,8 @@ macro_rules! time_driver_impl { | |||
| 213 | } | 152 | } |
| 214 | 153 | ||
| 215 | #[no_mangle] | 154 | #[no_mangle] |
| 216 | unsafe fn _embassy_time_allocate_alarm() -> Option<$crate::AlarmHandle> { | 155 | fn _embassy_time_schedule_wake(at: u64, waker: &core::task::Waker) { |
| 217 | <$t as $crate::Driver>::allocate_alarm(&$name) | 156 | <$t as $crate::Driver>::schedule_wake(&$name, at, waker); |
| 218 | } | ||
| 219 | |||
| 220 | #[no_mangle] | ||
| 221 | fn _embassy_time_set_alarm_callback(alarm: $crate::AlarmHandle, callback: fn(*mut ()), ctx: *mut ()) { | ||
| 222 | <$t as $crate::Driver>::set_alarm_callback(&$name, alarm, callback, ctx) | ||
| 223 | } | ||
| 224 | |||
| 225 | #[no_mangle] | ||
| 226 | fn _embassy_time_set_alarm(alarm: $crate::AlarmHandle, timestamp: u64) -> bool { | ||
| 227 | <$t as $crate::Driver>::set_alarm(&$name, alarm, timestamp) | ||
| 228 | } | 157 | } |
| 229 | }; | 158 | }; |
| 230 | } | 159 | } |
