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)]
87mod test {
88 #![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_time_subtraction)]
98 #![allow(clippy::useless_vec)]
99 #![allow(clippy::needless_pass_by_value)]
100 use super::*;
103
104 #[test]
105 fn opt_timestamp() {
106 use coarsetime::{Duration, Instant};
107
108 let ts = AtomicOptTimestamp::new();
109 assert!(ts.time_since_update().is_none());
110
111 let zero = Duration::from_secs(0);
112 let one_sec = Duration::from_secs(1);
113
114 let first = Instant::now();
115 let in_a_bit = first + one_sec * 10;
116 let even_later = first + one_sec * 25;
117
118 assert!(ts.time_since_update_at(first).is_none());
119
120 ts.update_to(first);
121 assert_eq!(ts.time_since_update_at(first), Some(zero));
122 assert_eq!(ts.time_since_update_at(in_a_bit), Some(one_sec * 10));
123
124 ts.update_to(in_a_bit);
125 assert!(ts.time_since_update_at(first).is_none());
126 assert_eq!(ts.time_since_update_at(in_a_bit), Some(zero));
127 assert_eq!(ts.time_since_update_at(even_later), Some(one_sec * 15));
128
129 ts.update_to(first);
131 assert!(ts.time_since_update_at(first).is_none());
132 assert_eq!(ts.time_since_update_at(in_a_bit), Some(zero));
133 assert_eq!(ts.time_since_update_at(even_later), Some(one_sec * 15));
134
135 ts.clear();
136 assert!(ts.time_since_update_at(first).is_none());
137 assert!(ts.time_since_update_at(in_a_bit).is_none());
138 assert!(ts.time_since_update_at(even_later).is_none());
139 }
140
141 #[test]
142 fn update_if_none() {
143 let ts = AtomicOptTimestamp::new();
144 assert!(ts.time_since_update().is_none());
145
146 let time1 = coarsetime::Instant::now();
148 ts.update_if_none();
149 let d = ts.time_since_update();
150 let time2 = coarsetime::Instant::now();
151 assert!(d.is_some());
152 assert!(d.unwrap() <= time2 - time1);
153
154 std::thread::sleep(std::time::Duration::from_millis(100));
155 let time3 = coarsetime::Instant::now();
157 assert!(time3 > time2);
159 ts.update_if_none();
160 let d2 = ts.time_since_update();
161 assert!(d2.is_some());
162 assert!(d2.unwrap() > d.unwrap());
163 }
164}