Skip to main content

qubit_clock/wall/
manual_wall_clock.rs

1// =============================================================================
2//    Copyright (c) 2025 - 2026 Haixing Hu.
3//
4//    SPDX-License-Identifier: Apache-2.0
5//
6//    Licensed under the Apache License, Version 2.0.
7// =============================================================================
8//! Defines a manually re-anchorable wall-clock projection.
9
10use std::sync::Arc;
11use std::sync::Mutex;
12use std::sync::MutexGuard;
13use std::time::SystemTime;
14
15use crate::ManualMonotonicClock;
16use crate::MonotonicClock;
17use crate::MonotonicInstant;
18use crate::WallClock;
19
20/// A wall clock projected from a shared [`ManualMonotonicClock`].
21///
22/// Advancing the monotonic clock advances this wall clock by the same
23/// duration. Calling [`reanchor()`](Self::reanchor) changes only the wall-time
24/// mapping and never changes the underlying monotonic clock.
25#[derive(Debug)]
26pub struct ManualWallClock {
27    /// Shared monotonic timeline used to project elapsed wall time.
28    clock: Arc<ManualMonotonicClock>,
29    /// Wall time and monotonic instant that define the current projection.
30    anchor: Mutex<(SystemTime, MonotonicInstant)>,
31}
32
33impl ManualWallClock {
34    /// Creates a wall clock whose current reading is `wall_time`.
35    ///
36    /// Future readings advance according to the explicitly shared `clock`.
37    ///
38    /// # Parameters
39    ///
40    /// * `wall_time` - Wall-clock value assigned to the current manual instant.
41    /// * `clock` - Shared manual monotonic timeline driving future readings.
42    ///
43    /// # Returns
44    ///
45    /// A wall clock anchored to the supplied wall and monotonic times.
46    #[must_use]
47    #[inline]
48    pub fn from_clock(wall_time: SystemTime, clock: Arc<ManualMonotonicClock>) -> Self {
49        let monotonic_anchor = clock.now();
50        Self {
51            clock,
52            anchor: Mutex::new((wall_time, monotonic_anchor)),
53        }
54    }
55
56    /// Reassigns the current monotonic instant to `wall_time`.
57    ///
58    /// This operation may move wall time forward or backward. It does not
59    /// advance the monotonic clock and does not wake monotonic sleepers. The
60    /// anchor mutex remains held while the monotonic clock is sampled, so
61    /// concurrent calls to [`now()`](WallClock::now) observe either the old or
62    /// the new mapping without combining both snapshots.
63    ///
64    /// # Parameters
65    ///
66    /// * `wall_time` - Replacement wall time for the current monotonic instant.
67    #[inline]
68    pub fn reanchor(&self, wall_time: SystemTime) {
69        let mut anchor = self.lock_anchor();
70        let monotonic_anchor = self.clock.now();
71        *anchor = (wall_time, monotonic_anchor);
72    }
73
74    /// Locks the wall and monotonic anchor pair, recovering after poisoning.
75    ///
76    /// # Returns
77    ///
78    /// A guard granting mutable access to the anchor pair.
79    #[inline]
80    fn lock_anchor(&self) -> MutexGuard<'_, (SystemTime, MonotonicInstant)> {
81        self.anchor.lock().unwrap_or_else(std::sync::PoisonError::into_inner)
82    }
83}
84
85impl WallClock for ManualWallClock {
86    /// Returns wall time derived from the anchor and current monotonic time.
87    ///
88    /// # Returns
89    ///
90    /// The anchored wall time plus elapsed manual monotonic time.
91    ///
92    /// # Panics
93    ///
94    /// Panics if the manually advanced duration cannot be represented by
95    /// [`SystemTime`]. Normal application and test durations are representable.
96    #[inline]
97    fn now(&self) -> SystemTime {
98        let anchor = self.lock_anchor();
99        let (wall_anchor, monotonic_anchor) = *anchor;
100        let monotonic_now = self.clock.now();
101        drop(anchor);
102        let elapsed = monotonic_now
103            .duration_since(monotonic_anchor)
104            .expect("manual wall clock must retain its monotonic clock domain");
105        wall_anchor
106            .checked_add(elapsed)
107            .expect("manual wall time exceeded SystemTime range")
108    }
109}