Skip to main content

osdp/
clock.rs

1//! Time abstraction. Drivers take a [`Clock`] so that timing logic is
2//! testable from `no_std` targets without pulling in a runtime.
3
4/// Minimal monotonic clock.
5///
6/// Implementations must be monotonic: `now_ms()` never goes backwards within a
7/// single instance.
8pub trait Clock {
9    /// Current time as milliseconds since an arbitrary epoch.
10    fn now_ms(&self) -> u64;
11}
12
13/// `Clock` that returns the system monotonic time. Available with `std`.
14#[cfg(feature = "std")]
15#[cfg_attr(docsrs, doc(cfg(feature = "std")))]
16pub struct SystemClock {
17    epoch: std::time::Instant,
18}
19
20#[cfg(feature = "std")]
21impl SystemClock {
22    /// New clock anchored at the current instant.
23    pub fn new() -> Self {
24        Self {
25            epoch: std::time::Instant::now(),
26        }
27    }
28}
29
30#[cfg(feature = "std")]
31impl Default for SystemClock {
32    fn default() -> Self {
33        Self::new()
34    }
35}
36
37#[cfg(feature = "std")]
38impl Clock for SystemClock {
39    fn now_ms(&self) -> u64 {
40        self.epoch.elapsed().as_millis() as u64
41    }
42}
43
44/// Manually-driven `Clock` for tests.
45///
46/// Shares its underlying counter across clones via [`alloc::sync::Arc`], so
47/// a clone handed to a driver and the original held by a test refer to the
48/// *same* time value.
49#[cfg(feature = "alloc")]
50#[derive(Debug, Default, Clone)]
51pub struct MockClock {
52    inner: alloc::sync::Arc<core::sync::atomic::AtomicU64>,
53}
54
55#[cfg(feature = "alloc")]
56impl MockClock {
57    /// New clock at `t = 0`.
58    pub fn new() -> Self {
59        Self::default()
60    }
61
62    /// Advance the clock by `ms` milliseconds.
63    pub fn advance(&self, ms: u64) {
64        self.inner
65            .fetch_add(ms, core::sync::atomic::Ordering::Relaxed);
66    }
67
68    /// Set the clock to `ms` milliseconds.
69    pub fn set(&self, ms: u64) {
70        self.inner.store(ms, core::sync::atomic::Ordering::Relaxed);
71    }
72}
73
74#[cfg(feature = "alloc")]
75impl Clock for MockClock {
76    fn now_ms(&self) -> u64 {
77        self.inner.load(core::sync::atomic::Ordering::Relaxed)
78    }
79}
80
81impl<C: Clock + ?Sized> Clock for &C {
82    fn now_ms(&self) -> u64 {
83        (*self).now_ms()
84    }
85}