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)]
87#[allow(clippy::unwrap_used)]
88mod test {
89// @@ begin test lint list maintained by maint/add_warning @@
90#![allow(clippy::bool_assert_comparison)]
91 #![allow(clippy::clone_on_copy)]
92 #![allow(clippy::dbg_macro)]
93 #![allow(clippy::mixed_attributes_style)]
94 #![allow(clippy::print_stderr)]
95 #![allow(clippy::print_stdout)]
96 #![allow(clippy::single_char_pattern)]
97 #![allow(clippy::unwrap_used)]
98 #![allow(clippy::unchecked_duration_subtraction)]
99 #![allow(clippy::useless_vec)]
100 #![allow(clippy::needless_pass_by_value)]
101//! <!-- @@ end test lint list maintained by maint/add_warning @@ -->
102103use super::*;
104105#[test]
106fn opt_timestamp() {
107use coarsetime::{Duration, Instant};
108109let ts = AtomicOptTimestamp::new();
110assert!(ts.time_since_update().is_none());
111112let zero = Duration::from_secs(0);
113let one_sec = Duration::from_secs(1);
114115let first = Instant::now();
116let in_a_bit = first + one_sec * 10;
117let even_later = first + one_sec * 25;
118119assert!(ts.time_since_update_at(first).is_none());
120121 ts.update_to(first);
122assert_eq!(ts.time_since_update_at(first), Some(zero));
123assert_eq!(ts.time_since_update_at(in_a_bit), Some(one_sec * 10));
124125 ts.update_to(in_a_bit);
126assert!(ts.time_since_update_at(first).is_none());
127assert_eq!(ts.time_since_update_at(in_a_bit), Some(zero));
128assert_eq!(ts.time_since_update_at(even_later), Some(one_sec * 15));
129130// Make sure we can't move backwards.
131ts.update_to(first);
132assert!(ts.time_since_update_at(first).is_none());
133assert_eq!(ts.time_since_update_at(in_a_bit), Some(zero));
134assert_eq!(ts.time_since_update_at(even_later), Some(one_sec * 15));
135136 ts.clear();
137assert!(ts.time_since_update_at(first).is_none());
138assert!(ts.time_since_update_at(in_a_bit).is_none());
139assert!(ts.time_since_update_at(even_later).is_none());
140 }
141142#[test]
143fn update_if_none() {
144let ts = AtomicOptTimestamp::new();
145assert!(ts.time_since_update().is_none());
146147// Calling "update_if_none" on a None AtomicOptTimestamp should set it.
148let time1 = coarsetime::Instant::now();
149 ts.update_if_none();
150let d = ts.time_since_update();
151let time2 = coarsetime::Instant::now();
152assert!(d.is_some());
153assert!(d.unwrap() <= time2 - time1);
154155 std::thread::sleep(std::time::Duration::from_millis(100));
156// Calling "update_if_none" on a Some AtomicOptTimestamp doesn't change it.
157let time3 = coarsetime::Instant::now();
158// If coarsetime doesn't register this, then the rest of our test won't work.
159assert!(time3 > time2);
160 ts.update_if_none();
161let d2 = ts.time_since_update();
162assert!(d2.is_some());
163assert!(d2.unwrap() > d.unwrap());
164 }
165}