Skip to main content

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}