1use std::{
16 cell::RefCell,
17 fmt::{Display, Formatter},
18 sync::{
19 LazyLock,
20 atomic::{AtomicU64, Ordering},
21 },
22 time::Duration,
23};
24
25use parking_lot::Mutex;
26
27use crate::drop_guard::DropGuard;
28
29pub trait Clock {
31 fn now(&self) -> Instant;
33
34 fn advance_to(&self, instant: Instant);
38}
39
40impl Clock for AtomicU64 {
41 fn now(&self) -> Instant {
42 *EPOCH + Duration::from_nanos(self.load(Ordering::Relaxed))
43 }
44
45 fn advance_to(&self, instant: Instant) {
46 let nanos = instant.saturating_since(*EPOCH).as_nanos();
47 assert!(nanos < u64::MAX as u128, "simulation is not supposed to run for more than 584 years");
48 let nanos = nanos as u64;
49 let old = self.swap(nanos, Ordering::Relaxed);
50 assert!(old <= nanos, "clock is not monotonic");
51 }
52}
53
54impl Clock for Mutex<Instant> {
55 fn now(&self) -> Instant {
56 *self.lock()
57 }
58
59 fn advance_to(&self, instant: Instant) {
60 *self.lock() = instant;
61 }
62}
63
64#[derive(Clone, Copy, Eq, PartialOrd, Ord)]
68pub struct Instant(tokio::time::Instant);
69
70thread_local! {
71 static TOLERANCE: RefCell<Duration> = const { RefCell::new(Duration::from_nanos(0)) };
72}
73
74impl PartialEq for Instant {
75 fn eq(&self, other: &Self) -> bool {
76 let tolerance = TOLERANCE.with(|tolerance| *tolerance.borrow());
77 if tolerance.is_zero() {
78 self.0 == other.0
79 } else if self > other {
80 self.0 - other.0 <= tolerance
81 } else {
82 other.0 - self.0 <= tolerance
83 }
84 }
85}
86
87impl std::fmt::Debug for Instant {
88 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
89 f.debug_tuple("Instant").field(&self.saturating_since(*EPOCH)).finish()
90 }
91}
92
93impl Display for Instant {
94 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
95 let duration = self.saturating_since(*EPOCH);
96 write!(f, "{:.6?}", duration)
97 }
98}
99
100impl<'de> serde::Deserialize<'de> for Instant {
101 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
102 where
103 D: serde::Deserializer<'de>,
104 {
105 let duration = Duration::deserialize(deserializer)?;
106 Ok(*EPOCH + duration)
107 }
108}
109
110impl serde::Serialize for Instant {
111 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
112 where
113 S: serde::Serializer,
114 {
115 let duration = self.saturating_since(*EPOCH);
116 duration.serialize(serializer)
117 }
118}
119
120impl Instant {
121 pub fn with_tolerance_for_test(tolerance: Duration) -> DropGuard<Duration, fn(Duration)> {
122 fn restore(tolerance: Duration) {
123 TOLERANCE.with_borrow_mut(|t2| *t2 = tolerance)
124 }
125 TOLERANCE.with_borrow_mut(|t| DropGuard::new(std::mem::replace(t, tolerance), restore as fn(Duration)))
126 }
127
128 pub(crate) fn from_tokio(instant: tokio::time::Instant) -> Self {
129 Self(instant)
130 }
131
132 pub(crate) fn to_tokio(self) -> tokio::time::Instant {
133 self.0
134 }
135
136 pub(crate) fn now() -> Self {
137 Self(tokio::time::Instant::now())
138 }
139
140 pub fn pretty(self, now: Self) -> String {
141 if let Some(duration) = self.checked_since(now) {
142 format!("{:?} in the future", duration)
143 } else if let Some(duration) = now.checked_since(self) {
144 format!("{:?} ago", duration)
145 } else {
146 "(time bug)".to_string()
147 }
148 }
149
150 pub fn saturating_since(&self, other: Self) -> Duration {
151 self.0.duration_since(other.0)
152 }
153
154 pub fn checked_since(&self, other: Self) -> Option<Duration> {
155 self.0.checked_duration_since(other.0)
156 }
157
158 pub fn at_offset(offset: Duration) -> Self {
159 *EPOCH + offset
160 }
161}
162
163impl std::ops::Add<Duration> for Instant {
164 type Output = Instant;
165
166 #[expect(clippy::expect_used)]
167 fn add(self, duration: Duration) -> Self {
168 Instant(
169 self.0.checked_add(duration).expect("simulation is not supposed to run for more than 290 billion years"),
170 )
171 }
172}
173
174impl std::ops::Sub<Duration> for Instant {
175 type Output = Instant;
176
177 #[expect(clippy::expect_used)]
178 fn sub(self, duration: Duration) -> Self {
179 Instant(
180 self.0.checked_sub(duration).expect("simulation is not supposed to run for more than 290 billion years"),
181 )
182 }
183}
184
185pub static EPOCH: LazyLock<Instant> = LazyLock::new(Instant::now);
189
190#[test]
191fn instant() {
192 let now = Instant::now();
193 let later = now + Duration::from_secs(1);
194
195 assert_eq!(later.checked_since(now).unwrap(), Duration::from_secs(1));
196 assert_eq!(now.checked_since(later), None);
197
198 assert_eq!(later.saturating_since(now), Duration::from_secs(1));
199 assert_eq!(now.saturating_since(later), Duration::from_secs(0));
200
201 assert_eq!(now + Duration::from_secs(1), later);
202 assert_eq!(later - Duration::from_secs(1), now);
203}