1use std::cell::Cell;
2use std::fmt;
3use std::rc::Rc;
4use std::time::{Duration, Instant};
5
6pub trait RuntimeClockSource {
7 fn now(&self) -> Instant;
8
9 fn advance(&self, _duration: Duration) -> bool {
10 false
11 }
12}
13
14#[derive(Clone)]
15pub struct RuntimeClock {
16 source: Rc<dyn RuntimeClockSource>,
17 origin: Instant,
18}
19
20impl RuntimeClock {
21 #[must_use]
22 pub fn system() -> Self {
23 Self::from_source(SystemRuntimeClock)
24 }
25
26 #[must_use]
27 pub fn from_source(source: impl RuntimeClockSource + 'static) -> Self {
28 let source: Rc<dyn RuntimeClockSource> = Rc::new(source);
29 let origin = source.now();
30 Self { source, origin }
31 }
32
33 #[must_use]
34 pub fn now(&self) -> Instant {
35 self.source.now()
36 }
37
38 #[must_use]
39 pub fn elapsed(&self) -> Duration {
40 self.now().saturating_duration_since(self.origin)
41 }
42
43 #[must_use]
44 pub fn advance(&self, duration: Duration) -> bool {
45 self.source.advance(duration)
46 }
47}
48
49impl Default for RuntimeClock {
50 fn default() -> Self {
51 Self::system()
52 }
53}
54
55impl fmt::Debug for RuntimeClock {
56 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
57 formatter.write_str("RuntimeClock(..)")
58 }
59}
60
61#[derive(Clone, Copy, Debug)]
62struct SystemRuntimeClock;
63
64impl RuntimeClockSource for SystemRuntimeClock {
65 fn now(&self) -> Instant {
66 Instant::now()
67 }
68}
69
70#[derive(Clone, Debug)]
71pub struct ManualRuntimeClock {
72 now: Rc<Cell<Instant>>,
73}
74
75impl ManualRuntimeClock {
76 #[must_use]
77 pub fn new(now: Instant) -> Self {
78 Self {
79 now: Rc::new(Cell::new(now)),
80 }
81 }
82
83 #[must_use]
84 pub fn clock(&self) -> RuntimeClock {
85 RuntimeClock::from_source(self.clone())
86 }
87
88 pub fn advance(&self, duration: Duration) {
89 self.now.set(self.now.get() + duration);
90 }
91
92 pub fn set(&self, now: Instant) {
93 self.now.set(now);
94 }
95}
96
97impl RuntimeClockSource for ManualRuntimeClock {
98 fn now(&self) -> Instant {
99 self.now.get()
100 }
101
102 fn advance(&self, duration: Duration) -> bool {
103 self.advance(duration);
104 true
105 }
106}
107
108#[cfg(test)]
109mod tests {
110 use super::*;
111
112 #[test]
113 fn manual_clock_is_shared_and_monotonic_by_host_policy() {
114 let start = Instant::now();
115 let manual = ManualRuntimeClock::new(start);
116 let clock = manual.clock();
117 manual.advance(Duration::from_millis(16));
118 assert_eq!(clock.now(), start + Duration::from_millis(16));
119 assert_eq!(clock.elapsed(), Duration::from_millis(16));
120 }
121}