rivet_bsp_support/delay.rs
1//! `embedded-hal-async::delay::DelayNs` on top of the preemptive tier's
2//! blocking sleep (plan.md Phase 15).
3//!
4//! This is a blocking implementation of an async trait — deliberately.
5//! Rivet's preemptive tier has no "suspend this task, return control to
6//! an executor" primitive the way the cooperative tier's `Sleep` does
7//! (that one needs a *compile-time* duration, `Sleep<const MICROS: u64>`,
8//! which doesn't fit `DelayNs`'s runtime-parameterized signature); what a
9//! preemptive task actually does to give up the CPU for a while *is*
10//! block (`rivet::preempt::sleep_ms`) — the scheduler runs something else
11//! in the meantime, which is the same practical effect `.await`ing a real
12//! async delay would have. A driver written against `DelayNs` works
13//! correctly called from a preemptive task; it just isn't meaningful to
14//! call from the cooperative executor's own task (blocking there would
15//! stall every other cooperative task, exactly as blocking always would).
16
17pub struct RivetDelay;
18
19impl embedded_hal_async::delay::DelayNs for RivetDelay {
20 async fn delay_ns(&mut self, ns: u32) {
21 // Round up to whole milliseconds — `sleep_ms`'s actual resolution
22 // is bounded by `RIVET_TICK_HZ` anyway, so a sub-tick request
23 // rounding up to one tick is honest, not a fake no-op.
24 let ms = (ns as u64).div_ceil(1_000_000).max(1);
25 rivet::preempt::sleep_ms(ms);
26 }
27}
28
29#[cfg(test)]
30mod tests {
31 use super::*;
32
33 // Compile-only: proves `RivetDelay` is usable through generic
34 // `embedded-hal-async` code, not just directly.
35 #[allow(dead_code)]
36 async fn generic<D: embedded_hal_async::delay::DelayNs>(d: &mut D) {
37 d.delay_ms(1).await;
38 }
39
40 #[test]
41 fn type_checks() {
42 let _ = generic::<RivetDelay>;
43 }
44}