1#![doc = include_str!("../README.md")]
9#![cfg_attr(docsrs, feature(doc_cfg))]
11#![cfg_attr(coverage_nightly, feature(coverage_attribute))]
13#![forbid(unsafe_code)]
15
16#[cfg(any(test, feature = "test-clock"))]
18mod paused;
19
20use std::fmt;
21use std::sync::{Arc, Condvar, Mutex, MutexGuard};
22use std::time::Instant;
23
24#[derive(Clone)]
30#[cfg_attr(not(any(test, feature = "test-clock")), derive(PartialEq, Eq))]
32pub struct Clock {
33 #[cfg(any(test, feature = "test-clock"))]
35 paused: Option<Arc<paused::Paused>>,
36}
37
38impl Clock {
39 pub fn real() -> Self {
41 Self {
42 #[cfg(any(test, feature = "test-clock"))]
43 paused: None,
44 }
45 }
46
47 pub fn now(&self) -> Instant {
49 #[cfg(any(test, feature = "test-clock"))]
50 if let Some(paused) = &self.paused {
51 return paused.now();
52 }
53 Instant::now()
54 }
55
56 pub fn waiter(&self) -> Waiter {
59 let signal = Arc::new(Signal::default());
60 #[cfg(any(test, feature = "test-clock"))]
61 if let Some(paused) = &self.paused {
62 paused.register(&signal);
63 }
64 Waiter {
65 clock: self.clone(),
66 signal,
67 }
68 }
69}
70
71impl fmt::Debug for Clock {
72 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
74 let mut clock = f.debug_struct("Clock");
75 #[cfg(any(test, feature = "test-clock"))]
76 if let Some(paused) = &self.paused {
77 return clock
78 .field("paused", &true)
79 .field("advanced", &paused.advanced())
80 .finish();
81 }
82 clock.field("paused", &false).finish()
83 }
84}
85
86#[derive(Clone)]
108pub struct Waiter {
109 clock: Clock,
111 signal: Arc<Signal>,
113}
114
115impl Waiter {
116 pub fn notify_all(&self) {
120 self.signal.notify_all();
121 }
122
123 pub fn wait_until<T>(
139 &self,
140 deadline: Option<Instant>,
141 mut ready: impl FnMut() -> Option<T>,
142 ) -> Option<T> {
143 #[cfg(any(test, feature = "test-clock"))]
145 let timer = deadline.filter(|_| self.clock.paused.is_none());
146 #[cfg(not(any(test, feature = "test-clock")))]
147 let timer = deadline;
148 loop {
149 let seen = self.signal.generation();
151 if let Some(value) = ready() {
152 return Some(value);
153 }
154 if deadline.is_some_and(|deadline| self.clock.now() >= deadline) {
155 return None;
156 }
157 self.signal.park(seen, timer);
158 }
159 }
160}
161
162impl fmt::Debug for Waiter {
163 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
165 f.debug_struct("Waiter")
166 .field("clock", &self.clock)
167 .finish_non_exhaustive()
168 }
169}
170
171#[derive(Default)]
175struct Signal {
176 state: Mutex<SignalState>,
178 changed: Condvar,
180}
181
182#[derive(Default)]
184struct SignalState {
185 generation: u64,
187 #[cfg(test)]
190 parks: usize,
191}
192
193impl Signal {
194 fn lock(&self) -> MutexGuard<'_, SignalState> {
196 self.state.lock().expect("waiter signal not poisoned")
197 }
198
199 fn generation(&self) -> u64 {
201 self.lock().generation
202 }
203
204 fn notify_all(&self) {
206 self.lock().generation += 1;
207 self.changed.notify_all();
208 }
209
210 fn park(&self, seen: u64, timer: Option<Instant>) {
213 let mut state = self.lock();
214 #[cfg(test)]
215 if state.generation == seen {
216 state.parks += 1;
217 self.changed.notify_all();
218 }
219 while state.generation == seen {
220 state = match timer {
221 None => self
222 .changed
223 .wait(state)
224 .expect("waiter signal not poisoned"),
225 Some(timer) => {
226 let now = Instant::now();
228 if now >= timer {
229 break;
230 }
231 self.changed
232 .wait_timeout(state, timer - now)
233 .expect("waiter signal not poisoned")
234 .0
235 }
236 };
237 }
238 }
239}
240
241#[cfg(test)]
243#[cfg_attr(coverage_nightly, coverage(off))]
244mod tests {
245 use super::*;
246 use std::panic::{self, AssertUnwindSafe};
247 use std::sync::atomic::{AtomicBool, Ordering};
248 use std::thread;
249 use std::time::Duration;
250
251 fn parked(waiter: &Waiter, count: usize) {
254 let mut state = waiter.signal.lock();
255 while state.parks < count {
256 state = waiter.signal.changed.wait(state).unwrap();
257 }
258 }
259
260 #[test]
264 fn test_paused_clock_sharing() {
265 let other = Clock::paused();
266 let clock = Clock::paused();
267 let clone = clock.clone();
268 let other_start = other.now();
269 let start = clock.now();
270
271 clone.advance(Duration::from_secs(1));
272 assert_eq!(clock.now(), start + Duration::from_secs(1));
273 clock.advance_to(start + Duration::from_secs(3));
274 assert_eq!(clone.now(), start + Duration::from_secs(3));
275 clock.advance(Duration::ZERO);
276 clock.advance_to(start + Duration::from_secs(3));
277 assert_eq!(clock.now(), start + Duration::from_secs(3));
278 assert_eq!(other.now(), other_start);
279
280 other.advance_to(clock.now());
281 assert_eq!(other.now(), clock.now());
282 assert_eq!(clock, clone);
283 assert_ne!(clock, other);
284 assert_ne!(clock, Clock::real());
285 assert_eq!(Clock::real(), Clock::real());
286 }
287
288 #[test]
291 fn test_failed_advance_keeps_time() {
292 let clock = Clock::paused();
293 let real = Clock::real();
294 clock.advance(Duration::from_secs(1));
295 let now = clock.now();
296
297 let misuses: [(&str, &dyn Fn()); 3] = [
298 ("real", &|| real.advance(Duration::from_secs(1))),
299 ("backwards", &|| {
300 clock.advance_to(now - Duration::from_secs(1))
301 }),
302 ("overflow", &|| clock.advance(Duration::MAX)),
303 ];
304 for (case, misuse) in misuses {
305 assert!(
306 panic::catch_unwind(AssertUnwindSafe(misuse)).is_err(),
307 "{case}"
308 );
309 assert_eq!(clock.now(), now, "{case}");
310 }
311 clock.advance(Duration::from_secs(1));
312 assert_eq!(clock.now(), now + Duration::from_secs(1));
313 }
314
315 #[test]
318 fn test_wait_checks_ready_first() {
319 for clock in [Clock::real(), Clock::paused()] {
320 let waiter = clock.waiter();
321 let now = clock.now();
322 assert_eq!(
323 waiter.wait_until(Some(now), || Some(1)),
324 Some(1),
325 "{clock:?}"
326 );
327 assert_eq!(
328 waiter.wait_until(Some(now), || None::<u8>),
329 None,
330 "{clock:?}"
331 );
332 }
333 }
334
335 #[test]
338 fn test_wait_sees_changes_after_its_check() {
339 let clock = Clock::paused();
340 let waiter = clock.waiter();
341 let deadline = clock.now() + Duration::from_secs(2);
342 let mut checks = 0;
343 let result = waiter.wait_until(Some(deadline), || {
344 checks += 1;
345 if checks == 1 {
346 waiter.notify_all();
347 } else if clock.now() < deadline {
348 clock.advance(Duration::from_secs(1));
349 }
350 None::<()>
351 });
352 assert_eq!(result, None);
353 assert_eq!(clock.now(), deadline);
354 }
355
356 #[test]
360 fn test_advance_wakes_parked_waiters() {
361 let clock = Clock::paused();
362 let deadline = clock.now() + Duration::from_secs(60);
363 let mut waiters: Vec<_> = (0..4).map(|_| clock.waiter()).collect();
365 waiters.truncate(1);
366 waiters.push(clock.clone().waiter());
367 drop(clock.waiter());
368
369 let threads: Vec<_> = waiters
370 .iter()
371 .map(|waiter| {
372 let waiter = waiter.clone();
373 thread::spawn(move || waiter.wait_until(Some(deadline), || None::<()>))
374 })
375 .collect();
376 for waiter in &waiters {
377 parked(waiter, 1);
378 }
379 clock.advance(Duration::from_secs(30));
380 for waiter in &waiters {
381 parked(waiter, 2);
382 }
383 clock.advance(Duration::from_secs(30));
384 for thread in threads {
385 assert_eq!(thread.join().unwrap(), None);
386 }
387 }
388
389 #[test]
392 fn test_ready_value_survives_advance() {
393 let clock = Clock::paused();
394 let waiter = clock.waiter();
395 let deadline = clock.now() + Duration::from_secs(1);
396 let ready = Arc::new(AtomicBool::new(false));
397 let waiting = thread::spawn({
398 let (waiter, ready) = (waiter.clone(), ready.clone());
399 move || {
400 waiter.wait_until(Some(deadline), || {
401 ready.load(Ordering::SeqCst).then_some(())
402 })
403 }
404 });
405 parked(&waiter, 1);
406 ready.store(true, Ordering::SeqCst);
407 clock.advance(Duration::from_secs(1));
408 assert_eq!(waiting.join().unwrap(), Some(()));
409 }
410
411 #[test]
414 fn test_notify_wakes_parked_waiters() {
415 for clock in [Clock::real(), Clock::paused()] {
416 let waiter = clock.waiter();
417 let open = Arc::new(AtomicBool::new(false));
418 let threads: Vec<_> = (0..2)
419 .map(|_| {
420 let (waiter, open) = (waiter.clone(), open.clone());
421 thread::spawn(move || {
422 waiter.wait_until(None, || open.load(Ordering::SeqCst).then_some(()))
423 })
424 })
425 .collect();
426 parked(&waiter, 2);
427 open.store(true, Ordering::SeqCst);
428 waiter.notify_all();
429 for thread in threads {
430 assert_eq!(thread.join().unwrap(), Some(()), "{clock:?}");
431 }
432 }
433 }
434
435 #[test]
438 fn test_real_deadline_expires() {
439 let waiter = Clock::real().waiter();
440 let deadline = Instant::now() + Duration::from_millis(1);
441 assert_eq!(waiter.wait_until(Some(deadline), || None::<()>), None);
442 assert!(Instant::now() >= deadline);
443 }
444}