kayrx_timer/
clock.rs

1//! Source of time abstraction.
2//!
3//! By default, `std::time::Instant::now()` is used. 
4
5use crate::Instant;
6
7#[derive(Debug, Clone)]
8pub(crate) struct Clock {}
9
10pub(crate) fn now() -> Instant {
11    Instant::from_std(std::time::Instant::now())
12}
13
14impl Clock {
15   pub(crate) fn new() -> Clock {
16       Clock {}
17   }
18
19    pub(crate) fn now(&self) -> Instant {
20        now()
21    }
22
23    pub(crate) fn enter<F, R>(&self, f: F) -> R
24    where
25        F: FnOnce() -> R,
26    {
27        f()
28    }
29}
30
31
32// Test Utils
33
34pub mod clock_util {
35    use crate::{Duration, Instant};
36
37    use std::cell::Cell;
38    use std::sync::{Arc, Mutex};
39
40    /// A handle to a source of time.
41    #[derive(Debug, Clone)]
42    pub(crate) struct Clock {
43        inner: Arc<Inner>,
44    }
45
46    #[derive(Debug)]
47    struct Inner {
48        /// Instant at which the clock was created
49        start: std::time::Instant,
50
51        /// Current, "frozen" time as an offset from `start`.
52        frozen: Mutex<Option<Duration>>,
53    }
54
55    thread_local! {
56        /// Thread-local tracking the current clock
57        static CLOCK: Cell<Option<*const Clock>> = Cell::new(None)
58    }
59
60    /// Pause time
61    ///
62    /// The current value of `Instant::now()` is saved and all subsequent calls
63    /// to `Instant::now()` will return the saved value. This is useful for
64    /// running tests that are dependent on time.
65    ///
66    /// # Panics
67    ///
68    /// Panics if time is already frozen or if called from outside of the kayrx::krse
69    /// runtime.
70    pub fn pause() {
71        CLOCK.with(|cell| {
72            let ptr = match cell.get() {
73                Some(ptr) => ptr,
74                None => panic!("time cannot be frozen from outside the kayrx::krse runtime"),
75            };
76
77            let clock = unsafe { &*ptr };
78            let mut frozen = clock.inner.frozen.lock().unwrap();
79
80            if frozen.is_some() {
81                panic!("time is already frozen");
82            }
83
84            *frozen = Some(clock.inner.start.elapsed());
85        })
86    }
87
88    /// Resume time
89    ///
90    /// Clears the saved `Instant::now()` value. Subsequent calls to
91    /// `Instant::now()` will return the value returned by the system call.
92    ///
93    /// # Panics
94    ///
95    /// Panics if time is not frozen or if called from outside of the kayrx::krse
96    /// runtime.
97    pub fn resume() {
98        CLOCK.with(|cell| {
99            let ptr = match cell.get() {
100                Some(ptr) => ptr,
101                None => panic!("time cannot be frozen from outside the kayrx::krse runtime"),
102            };
103
104            let clock = unsafe { &*ptr };
105            let mut frozen = clock.inner.frozen.lock().unwrap();
106
107            if frozen.is_none() {
108                panic!("time is not frozen");
109            }
110
111            *frozen = None;
112        })
113    }
114
115    /// Advance time
116    ///
117    /// Increments the saved `Instant::now()` value by `duration`. Subsequent
118    /// calls to `Instant::now()` will return the result of the increment.
119    ///
120    /// # Panics
121    ///
122    /// Panics if time is not frozen or if called from outside of the kayrx::krse
123    /// runtime.
124    // pub async fn advance(duration: Duration) {
125    //     CLOCK.with(|cell| {
126    //         let ptr = match cell.get() {
127    //             Some(ptr) => ptr,
128    //             None => panic!("time cannot be frozen from outside the kayrx::krse runtime"),
129    //         };
130
131    //         let clock = unsafe { &*ptr };
132    //         clock.advance(duration);
133    //     });
134
135    //     crate::task::yield_now().await;
136    // }
137
138    /// Return the current instant, factoring in frozen time.
139    pub(crate) fn now() -> Instant {
140        CLOCK.with(|cell| {
141            Instant::from_std(match cell.get() {
142                Some(ptr) => {
143                    let clock = unsafe { &*ptr };
144
145                    if let Some(frozen) = *clock.inner.frozen.lock().unwrap() {
146                        clock.inner.start + frozen
147                    } else {
148                        std::time::Instant::now()
149                    }
150                }
151                None => std::time::Instant::now(),
152            })
153        })
154    }
155
156    impl Clock {
157        /// Return a new `Clock` instance that uses the current execution context's
158        /// source of time.
159        pub(crate) fn new() -> Clock {
160            Clock {
161                inner: Arc::new(Inner {
162                    start: std::time::Instant::now(),
163                    frozen: Mutex::new(None),
164                }),
165            }
166        }
167
168        // TODO: delete this. Some tests rely on this
169        /// Return a new `Clock` instance that uses the current execution context's
170        /// source of time.
171        pub(crate) fn new_frozen() -> Clock {
172            Clock {
173                inner: Arc::new(Inner {
174                    start: std::time::Instant::now(),
175                    frozen: Mutex::new(Some(Duration::from_millis(0))),
176                }),
177            }
178        }
179
180        pub(crate) fn advance(&self, duration: Duration) {
181            let mut frozen = self.inner.frozen.lock().unwrap();
182
183            if let Some(ref mut elapsed) = *frozen {
184                *elapsed += duration;
185            } else {
186                panic!("time is not frozen");
187            }
188        }
189
190        // TODO: delete this as well
191        pub(crate) fn advanced(&self) -> Duration {
192            self.inner.frozen.lock().unwrap().unwrap()
193        }
194
195        pub(crate) fn now(&self) -> Instant {
196            Instant::from_std(if let Some(frozen) = *self.inner.frozen.lock().unwrap() {
197                self.inner.start + frozen
198            } else {
199                std::time::Instant::now()
200            })
201        }
202
203        /// Set the clock as the default source of time for the duration of the
204        /// closure
205        pub(crate) fn enter<F, R>(&self, f: F) -> R
206        where
207            F: FnOnce() -> R,
208        {
209            CLOCK.with(|cell| {
210                assert!(
211                    cell.get().is_none(),
212                    "default clock already set for execution context"
213                );
214
215                // Ensure that the clock is removed from the thread-local context
216                // when leaving the scope. This handles cases that involve panicking.
217                struct Reset<'a>(&'a Cell<Option<*const Clock>>);
218
219                impl Drop for Reset<'_> {
220                    fn drop(&mut self) {
221                        self.0.set(None);
222                    }
223                }
224
225                let _reset = Reset(cell);
226
227                cell.set(Some(self as *const Clock));
228
229                f()
230            })
231        }
232    }
233}