1 use super::{abi, error::expect_success};
2 use crate::{mem::MaybeUninit, time::Duration};
4 #[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Debug, Hash)]
5 pub struct Instant(abi::SYSTIM);
8 pub fn now() -> Instant {
9 // Safety: The provided pointer is valid
11 let mut out = MaybeUninit::uninit();
12 expect_success(abi::get_tim(out.as_mut_ptr()), &"get_tim");
13 Instant(out.assume_init())
17 pub fn checked_sub_instant(&self, other: &Instant) -> Option<Duration> {
18 self.0.checked_sub(other.0).map(|ticks| {
19 // `SYSTIM` is measured in microseconds
20 Duration::from_micros(ticks)
24 pub fn checked_add_duration(&self, other: &Duration) -> Option<Instant> {
25 // `SYSTIM` is measured in microseconds
26 let ticks = other.as_micros();
28 Some(Instant(self.0.checked_add(ticks.try_into().ok()?)?))
31 pub fn checked_sub_duration(&self, other: &Duration) -> Option<Instant> {
32 // `SYSTIM` is measured in microseconds
33 let ticks = other.as_micros();
35 Some(Instant(self.0.checked_sub(ticks.try_into().ok()?)?))
39 /// Split `Duration` into zero or more `RELTIM`s.
41 pub fn dur2reltims(dur: Duration) -> impl Iterator<Item = abi::RELTIM> {
42 // `RELTIM` is microseconds
43 let mut ticks = dur.as_micros();
45 crate::iter::from_fn(move || {
48 } else if ticks <= abi::TMAX_RELTIM as u128 {
49 Some(crate::mem::replace(&mut ticks, 0) as abi::RELTIM)
51 ticks -= abi::TMAX_RELTIM as u128;
52 Some(abi::TMAX_RELTIM)
57 /// Split `Duration` into one or more `TMO`s.
59 fn dur2tmos(dur: Duration) -> impl Iterator<Item = abi::TMO> {
60 // `TMO` is microseconds
61 let mut ticks = dur.as_micros();
64 crate::iter::from_fn(move || {
67 } else if ticks <= abi::TMAX_RELTIM as u128 {
69 Some(crate::mem::replace(&mut ticks, 0) as abi::TMO)
71 ticks -= abi::TMAX_RELTIM as u128;
72 Some(abi::TMAX_RELTIM)
77 /// Split `Duration` into one or more API calls with timeout.
79 pub fn with_tmos(dur: Duration, mut f: impl FnMut(abi::TMO) -> abi::ER) -> abi::ER {
80 let mut er = abi::E_TMOUT;
81 for tmo in dur2tmos(dur) {
83 if er != abi::E_TMOUT {
90 /// Split `Duration` into one or more API calls with timeout. This function can
91 /// handle spurious wakeups.
93 pub fn with_tmos_strong(dur: Duration, mut f: impl FnMut(abi::TMO) -> abi::ER) -> abi::ER {
94 // `TMO` and `SYSTIM` are microseconds.
95 // Clamp at `SYSTIM::MAX` for performance reasons. This shouldn't cause
96 // a problem in practice. (`u64::MAX` μs ≈ 584942 years)
97 let ticks = dur.as_micros().min(abi::SYSTIM::MAX as u128) as abi::SYSTIM;
99 let start = Instant::now().0;
101 let mut er = abi::E_TMOUT;
102 while elapsed <= ticks {
103 er = f(elapsed.min(abi::TMAX_RELTIM as abi::SYSTIM) as abi::TMO);
104 if er != abi::E_TMOUT {
107 elapsed = Instant::now().0.wrapping_sub(start);