1//! Implement a fast 'timestamp' for determining when an event last
2//! happened.
34use std::sync::atomic::{AtomicU64, Ordering};
56/// An object for determining whether an event happened,
7/// and if yes, when it happened.
8///
9/// Every `Timestamp` has internal mutability. A timestamp can move
10/// forward in time, but never backwards.
11///
12/// Internally, it uses the `coarsetime` crate to represent times in a way
13/// that lets us do atomic updates.
14#[derive(Default, Debug)]
15pub(crate) struct AtomicOptTimestamp {
16/// A timestamp (from `coarsetime`) describing when this timestamp
17 /// was last updated.
18 ///
19 /// I'd rather just use [`coarsetime::Instant`], but that doesn't have
20 /// an atomic form.
21latest: AtomicU64,
22}
23impl AtomicOptTimestamp {
24/// Construct a new timestamp that has never been updated.
25pub(crate) const fn new() -> Self {
26 AtomicOptTimestamp {
27 latest: AtomicU64::new(0),
28 }
29 }
3031/// Update this timestamp to (at least) the current time.
32pub(crate) fn update(&self) {
33// TODO: Do we want to use 'Instant::recent() instead,' and
34 // add an updater thread?
35self.update_to(coarsetime::Instant::now());
36 }
3738/// If the timestamp is currently unset, then set it to the current time.
39 /// Otherwise leave it alone.
40pub(crate) fn update_if_none(&self) {
41let now = coarsetime::Instant::now().as_ticks();
4243let _ignore = self
44.latest
45 .compare_exchange(0, now, Ordering::Relaxed, Ordering::Relaxed);
46 }
4748/// Clear the timestamp and make it not updated again.
49pub(crate) fn clear(&self) {
50self.latest.store(0, Ordering::Relaxed);
51 }
5253/// Return the time since `update` was last called.
54 ///
55 /// Return `None` if update was never called.
56pub(crate) fn time_since_update(&self) -> Option<coarsetime::Duration> {
57self.time_since_update_at(coarsetime::Instant::now())
58 }
5960/// Return the time between the time when `update` was last
61 /// called, and the time `now`.
62 ///
63 /// Return `None` if `update` was never called, or `now` is before
64 /// that time.
65#[inline]
66pub(crate) fn time_since_update_at(
67&self,
68 now: coarsetime::Instant,
69 ) -> Option<coarsetime::Duration> {
70let earlier = self.latest.load(Ordering::Relaxed);
71let now = now.as_ticks();
72if now >= earlier && earlier != 0 {
73Some(coarsetime::Duration::from_ticks(now - earlier))
74 } else {
75None
76}
77 }
7879/// Update this timestamp to (at least) the time `now`.
80#[inline]
81pub(crate) fn update_to(&self, now: coarsetime::Instant) {
82self.latest.fetch_max(now.as_ticks(), Ordering::Relaxed);
83 }
84}
8586#[cfg(test)]
87mod test {
88// @@ begin test lint list maintained by maint/add_warning @@
89#![allow(clippy::bool_assert_comparison)]
90 #![allow(clippy::clone_on_copy)]
91 #![allow(clippy::dbg_macro)]
92 #![allow(clippy::mixed_attributes_style)]
93 #![allow(clippy::print_stderr)]
94 #![allow(clippy::print_stdout)]
95 #![allow(clippy::single_char_pattern)]
96 #![allow(clippy::unwrap_used)]
97 #![allow(clippy::unchecked_duration_subtraction)]
98 #![allow(clippy::useless_vec)]
99 #![allow(clippy::needless_pass_by_value)]
100//! <!-- @@ end test lint list maintained by maint/add_warning @@ -->
101102use super::*;
103104#[test]
105fn opt_timestamp() {
106use coarsetime::{Duration, Instant};
107108let ts = AtomicOptTimestamp::new();
109assert!(ts.time_since_update().is_none());
110111let zero = Duration::from_secs(0);
112let one_sec = Duration::from_secs(1);
113114let first = Instant::now();
115let in_a_bit = first + one_sec * 10;
116let even_later = first + one_sec * 25;
117118assert!(ts.time_since_update_at(first).is_none());
119120 ts.update_to(first);
121assert_eq!(ts.time_since_update_at(first), Some(zero));
122assert_eq!(ts.time_since_update_at(in_a_bit), Some(one_sec * 10));
123124 ts.update_to(in_a_bit);
125assert!(ts.time_since_update_at(first).is_none());
126assert_eq!(ts.time_since_update_at(in_a_bit), Some(zero));
127assert_eq!(ts.time_since_update_at(even_later), Some(one_sec * 15));
128129// Make sure we can't move backwards.
130ts.update_to(first);
131assert!(ts.time_since_update_at(first).is_none());
132assert_eq!(ts.time_since_update_at(in_a_bit), Some(zero));
133assert_eq!(ts.time_since_update_at(even_later), Some(one_sec * 15));
134135 ts.clear();
136assert!(ts.time_since_update_at(first).is_none());
137assert!(ts.time_since_update_at(in_a_bit).is_none());
138assert!(ts.time_since_update_at(even_later).is_none());
139 }
140141#[test]
142fn update_if_none() {
143let ts = AtomicOptTimestamp::new();
144assert!(ts.time_since_update().is_none());
145146// Calling "update_if_none" on a None AtomicOptTimestamp should set it.
147let time1 = coarsetime::Instant::now();
148 ts.update_if_none();
149let d = ts.time_since_update();
150let time2 = coarsetime::Instant::now();
151assert!(d.is_some());
152assert!(d.unwrap() <= time2 - time1);
153154 std::thread::sleep(std::time::Duration::from_millis(100));
155// Calling "update_if_none" on a Some AtomicOptTimestamp doesn't change it.
156let time3 = coarsetime::Instant::now();
157// If coarsetime doesn't register this, then the rest of our test won't work.
158assert!(time3 > time2);
159 ts.update_if_none();
160let d2 = ts.time_since_update();
161assert!(d2.is_some());
162assert!(d2.unwrap() > d.unwrap());
163 }
164}