use super::{abi, error::expect_success}; use crate::{mem::MaybeUninit, time::Duration}; #[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Debug, Hash)] pub struct Instant(abi::SYSTIM); impl Instant { pub fn now() -> Instant { // Safety: The provided pointer is valid unsafe { let mut out = MaybeUninit::uninit(); expect_success(abi::get_tim(out.as_mut_ptr()), &"get_tim"); Instant(out.assume_init()) } } pub fn checked_sub_instant(&self, other: &Instant) -> Option { self.0.checked_sub(other.0).map(|ticks| { // `SYSTIM` is measured in microseconds Duration::from_micros(ticks) }) } pub fn checked_add_duration(&self, other: &Duration) -> Option { // `SYSTIM` is measured in microseconds let ticks = other.as_micros(); Some(Instant(self.0.checked_add(ticks.try_into().ok()?)?)) } pub fn checked_sub_duration(&self, other: &Duration) -> Option { // `SYSTIM` is measured in microseconds let ticks = other.as_micros(); Some(Instant(self.0.checked_sub(ticks.try_into().ok()?)?)) } } /// Split `Duration` into zero or more `RELTIM`s. #[inline] pub fn dur2reltims(dur: Duration) -> impl Iterator { // `RELTIM` is microseconds let mut ticks = dur.as_micros(); crate::iter::from_fn(move || { if ticks == 0 { None } else if ticks <= abi::TMAX_RELTIM as u128 { Some(crate::mem::replace(&mut ticks, 0) as abi::RELTIM) } else { ticks -= abi::TMAX_RELTIM as u128; Some(abi::TMAX_RELTIM) } }) } /// Split `Duration` into one or more `TMO`s. #[inline] fn dur2tmos(dur: Duration) -> impl Iterator { // `TMO` is microseconds let mut ticks = dur.as_micros(); let mut end = false; crate::iter::from_fn(move || { if end { None } else if ticks <= abi::TMAX_RELTIM as u128 { end = true; Some(crate::mem::replace(&mut ticks, 0) as abi::TMO) } else { ticks -= abi::TMAX_RELTIM as u128; Some(abi::TMAX_RELTIM) } }) } /// Split `Duration` into one or more API calls with timeout. #[inline] pub fn with_tmos(dur: Duration, mut f: impl FnMut(abi::TMO) -> abi::ER) -> abi::ER { let mut er = abi::E_TMOUT; for tmo in dur2tmos(dur) { er = f(tmo); if er != abi::E_TMOUT { break; } } er } /// Split `Duration` into one or more API calls with timeout. This function can /// handle spurious wakeups. #[inline] pub fn with_tmos_strong(dur: Duration, mut f: impl FnMut(abi::TMO) -> abi::ER) -> abi::ER { // `TMO` and `SYSTIM` are microseconds. // Clamp at `SYSTIM::MAX` for performance reasons. This shouldn't cause // a problem in practice. (`u64::MAX` μs ≈ 584942 years) let ticks = dur.as_micros().min(abi::SYSTIM::MAX as u128) as abi::SYSTIM; let start = Instant::now().0; let mut elapsed = 0; let mut er = abi::E_TMOUT; while elapsed <= ticks { er = f(elapsed.min(abi::TMAX_RELTIM as abi::SYSTIM) as abi::TMO); if er != abi::E_TMOUT { break; } elapsed = Instant::now().0.wrapping_sub(start); } er } #[cfg(test)] mod tests;