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