1use std::sync::atomic::{AtomicU64, Ordering};
5
6#[derive(Default, Debug)]
15pub(crate) struct AtomicOptTimestamp {
16 latest: AtomicU64,
22}
23impl AtomicOptTimestamp {
24 pub(crate) const fn new() -> Self {
26 AtomicOptTimestamp {
27 latest: AtomicU64::new(0),
28 }
29 }
30
31 pub(crate) fn update(&self) {
33 self.update_to(coarsetime::Instant::now());
36 }
37
38 pub(crate) fn update_if_none(&self) {
41 let now = coarsetime::Instant::now().as_ticks();
42
43 let _ignore = self
44 .latest
45 .compare_exchange(0, now, Ordering::Relaxed, Ordering::Relaxed);
46 }
47
48 pub(crate) fn clear(&self) {
50 self.latest.store(0, Ordering::Relaxed);
51 }
52
53 pub(crate) fn time_since_update(&self) -> Option<coarsetime::Duration> {
57 self.time_since_update_at(coarsetime::Instant::now())
58 }
59
60 #[inline]
66 pub(crate) fn time_since_update_at(
67 &self,
68 now: coarsetime::Instant,
69 ) -> Option<coarsetime::Duration> {
70 let earlier = self.latest.load(Ordering::Relaxed);
71 let now = now.as_ticks();
72 if now >= earlier && earlier != 0 {
73 Some(coarsetime::Duration::from_ticks(now - earlier))
74 } else {
75 None
76 }
77 }
78
79 #[inline]
81 pub(crate) fn update_to(&self, now: coarsetime::Instant) {
82 self.latest.fetch_max(now.as_ticks(), Ordering::Relaxed);
83 }
84}
85
86#[cfg(test)]
87#[allow(clippy::unwrap_used)]
88mod test {
89 #![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 use super::*;
104
105 #[test]
106 fn opt_timestamp() {
107 use coarsetime::{Duration, Instant};
108
109 let ts = AtomicOptTimestamp::new();
110 assert!(ts.time_since_update().is_none());
111
112 let zero = Duration::from_secs(0);
113 let one_sec = Duration::from_secs(1);
114
115 let first = Instant::now();
116 let in_a_bit = first + one_sec * 10;
117 let even_later = first + one_sec * 25;
118
119 assert!(ts.time_since_update_at(first).is_none());
120
121 ts.update_to(first);
122 assert_eq!(ts.time_since_update_at(first), Some(zero));
123 assert_eq!(ts.time_since_update_at(in_a_bit), Some(one_sec * 10));
124
125 ts.update_to(in_a_bit);
126 assert!(ts.time_since_update_at(first).is_none());
127 assert_eq!(ts.time_since_update_at(in_a_bit), Some(zero));
128 assert_eq!(ts.time_since_update_at(even_later), Some(one_sec * 15));
129
130 ts.update_to(first);
132 assert!(ts.time_since_update_at(first).is_none());
133 assert_eq!(ts.time_since_update_at(in_a_bit), Some(zero));
134 assert_eq!(ts.time_since_update_at(even_later), Some(one_sec * 15));
135
136 ts.clear();
137 assert!(ts.time_since_update_at(first).is_none());
138 assert!(ts.time_since_update_at(in_a_bit).is_none());
139 assert!(ts.time_since_update_at(even_later).is_none());
140 }
141
142 #[test]
143 fn update_if_none() {
144 let ts = AtomicOptTimestamp::new();
145 assert!(ts.time_since_update().is_none());
146
147 let time1 = coarsetime::Instant::now();
149 ts.update_if_none();
150 let d = ts.time_since_update();
151 let time2 = coarsetime::Instant::now();
152 assert!(d.is_some());
153 assert!(d.unwrap() <= time2 - time1);
154
155 std::thread::sleep(std::time::Duration::from_millis(100));
156 let time3 = coarsetime::Instant::now();
158 assert!(time3 > time2);
160 ts.update_if_none();
161 let d2 = ts.time_since_update();
162 assert!(d2.is_some());
163 assert!(d2.unwrap() > d.unwrap());
164 }
165}