diff options
| author | Dario Nieuwenhuis <[email protected]> | 2024-08-13 17:45:48 +0200 |
|---|---|---|
| committer | Dario Nieuwenhuis <[email protected]> | 2024-09-11 01:36:11 +0200 |
| commit | d193c9ef44a61c9de381044fd2968a95ce2ffc75 (patch) | |
| tree | d48f5532cf44e86f9d8c4ee236a6fbc7a179aa3b /embassy-time-driver/src/lib.rs | |
| parent | 1ea29f1d2e94e85d87bbe45598a096d1f8da0f34 (diff) | |
time-driver: clarify docs for set_alarm.
Diffstat (limited to 'embassy-time-driver/src/lib.rs')
| -rw-r--r-- | embassy-time-driver/src/lib.rs | 52 |
1 files changed, 40 insertions, 12 deletions
diff --git a/embassy-time-driver/src/lib.rs b/embassy-time-driver/src/lib.rs index aab2f626e..8000a9dcb 100644 --- a/embassy-time-driver/src/lib.rs +++ b/embassy-time-driver/src/lib.rs | |||
| @@ -113,24 +113,52 @@ pub trait Driver: Send + Sync + 'static { | |||
| 113 | /// It is UB to make the alarm fire before setting a callback. | 113 | /// It is UB to make the alarm fire before setting a callback. |
| 114 | unsafe fn allocate_alarm(&self) -> Option<AlarmHandle>; | 114 | unsafe fn allocate_alarm(&self) -> Option<AlarmHandle>; |
| 115 | 115 | ||
| 116 | /// Sets the callback function to be called when the alarm triggers. | 116 | /// Set the callback function to be called when the alarm triggers. |
| 117 | /// The callback may be called from any context (interrupt or thread mode). | 117 | /// The callback may be called from any context (interrupt or thread mode). |
| 118 | fn set_alarm_callback(&self, alarm: AlarmHandle, callback: fn(*mut ()), ctx: *mut ()); | 118 | fn set_alarm_callback(&self, alarm: AlarmHandle, callback: fn(*mut ()), ctx: *mut ()); |
| 119 | 119 | ||
| 120 | /// Sets an alarm at the given timestamp. When the current timestamp reaches the alarm | 120 | /// Set an alarm at the given timestamp. |
| 121 | /// timestamp, the provided callback function will be called. | ||
| 122 | /// | 121 | /// |
| 123 | /// The `Driver` implementation should guarantee that the alarm callback is never called synchronously from `set_alarm`. | 122 | /// ## Behavior |
| 124 | /// Rather - if `timestamp` is already in the past - `false` should be returned and alarm should not be set, | ||
| 125 | /// or alternatively, the driver should return `true` and arrange to call the alarm callback as soon as possible, but not synchronously. | ||
| 126 | /// There is a rare third possibility that the alarm was barely in the future, and by the time it was enabled, it had slipped into the | ||
| 127 | /// past. This is can be detected by double-checking that the alarm is still in the future after enabling it; if it isn't, `false` | ||
| 128 | /// should also be returned to indicate that the callback may have been called already by the alarm, but it is not guaranteed, so the | ||
| 129 | /// caller should also call the callback, just like in the more common `false` case. (Note: This requires idempotency of the callback.) | ||
| 130 | /// | 123 | /// |
| 131 | /// When callback is called, it is guaranteed that now() will return a value greater or equal than timestamp. | 124 | /// If `timestamp` is in the future, `set_alarm` schedules calling the callback function |
| 125 | /// at that time, and returns `true`. | ||
| 132 | /// | 126 | /// |
| 133 | /// Only one alarm can be active at a time for each AlarmHandle. This overwrites any previously-set alarm if any. | 127 | /// If `timestamp` is in the past, `set_alarm` has two allowed behaviors. Implementations can pick whether to: |
| 128 | /// | ||
| 129 | /// - Schedule calling the callback function "immediately", as if the requested timestamp was "now+epsilon" and return `true`, or | ||
| 130 | /// - Not schedule the callback, and return `false`. | ||
| 131 | /// | ||
| 132 | /// Callers must ensure to behave correctly with either behavior. | ||
| 133 | /// | ||
| 134 | /// When callback is called, it is guaranteed that `now()` will return a value greater than or equal to `timestamp`. | ||
| 135 | /// | ||
| 136 | /// ## Reentrancy | ||
| 137 | /// | ||
| 138 | /// Calling the callback from `set_alarm` synchronously is not allowed. If the implementation chooses the first option above, | ||
| 139 | /// it must still call the callback from another context (i.e. an interrupt handler or background thread), it's not allowed | ||
| 140 | /// to call it synchronously in the context `set_alarm` is running. | ||
| 141 | /// | ||
| 142 | /// The reason for the above is callers are explicitly permitted to do both of: | ||
| 143 | /// - Lock a mutex in the alarm callback. | ||
| 144 | /// - Call `set_alarm` while having that mutex locked. | ||
| 145 | /// | ||
| 146 | /// If `set_alarm` called the callback synchronously, it'd cause a deadlock or panic because it'd cause the | ||
| 147 | /// mutex to be locked twice reentrantly in the same context. | ||
| 148 | /// | ||
| 149 | /// ## Overwriting alarms | ||
| 150 | /// | ||
| 151 | /// Only one alarm can be active at a time for each `AlarmHandle`. This overwrites any previously-set alarm if any. | ||
| 152 | /// | ||
| 153 | /// ## Unsetting the alarm | ||
| 154 | /// | ||
| 155 | /// There is no `unset_alarm` API. Instead, callers can call `set_alarm` with `timestamp` set to `u64::MAX`. | ||
| 156 | /// | ||
| 157 | /// This allows for more efficient implementations, since they don't need to distinguish between the "alarm set" and | ||
| 158 | /// "alarm not set" cases, thanks to the fact "Alarm set for u64::MAX" is effectively equivalent for "alarm not set". | ||
| 159 | /// | ||
| 160 | /// This means implementations need to be careful to avoid timestamp overflows. The recommendation is to make `timestamp` | ||
| 161 | /// be in the same units as hardware ticks to avoid any conversions, which makes avoiding overflow easier. | ||
| 134 | fn set_alarm(&self, alarm: AlarmHandle, timestamp: u64) -> bool; | 162 | fn set_alarm(&self, alarm: AlarmHandle, timestamp: u64) -> bool; |
| 135 | } | 163 | } |
| 136 | 164 | ||
