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}