1use std::time::Instant;
2
3use super::*;
4
5#[repr(transparent)]
7pub struct AtomicOptionInstant(AtomicOptionDuration);
8
9impl core::fmt::Debug for AtomicOptionInstant {
10 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
11 f.debug_tuple("AtomicOptionInstant")
12 .field(&self.load(Ordering::SeqCst))
13 .finish()
14 }
15}
16impl Default for AtomicOptionInstant {
17 #[cfg_attr(not(tarpaulin), inline(always))]
19 fn default() -> Self {
20 Self::none()
21 }
22}
23impl From<Option<Instant>> for AtomicOptionInstant {
24 #[cfg_attr(not(tarpaulin), inline(always))]
25 fn from(instant: Option<Instant>) -> Self {
26 Self::new(instant)
27 }
28}
29
30impl AtomicOptionInstant {
31 #[cfg_attr(not(tarpaulin), inline(always))]
42 pub const fn none() -> Self {
43 Self(AtomicOptionDuration::new(None))
44 }
45
46 #[cfg_attr(not(tarpaulin), inline(always))]
55 pub fn now() -> Self {
56 Self::new(Some(Instant::now()))
57 }
58
59 #[cfg_attr(not(tarpaulin), inline(always))]
61 pub fn new(instant: Option<Instant>) -> Self {
62 Self(AtomicOptionDuration::new(
63 instant.map(encode_instant_to_duration),
64 ))
65 }
66
67 #[cfg_attr(not(tarpaulin), inline(always))]
69 pub fn load(&self, order: Ordering) -> Option<Instant> {
70 self.0.load(order).map(decode_instant_from_duration)
71 }
72
73 #[cfg_attr(not(tarpaulin), inline(always))]
75 pub fn store(&self, instant: Option<Instant>, order: Ordering) {
76 self.0.store(instant.map(encode_instant_to_duration), order)
77 }
78
79 #[cfg_attr(not(tarpaulin), inline(always))]
81 pub fn swap(&self, instant: Option<Instant>, order: Ordering) -> Option<Instant> {
82 self
83 .0
84 .swap(instant.map(encode_instant_to_duration), order)
85 .map(decode_instant_from_duration)
86 }
87
88 #[cfg_attr(not(tarpaulin), inline(always))]
91 pub fn compare_exchange(
92 &self,
93 current: Option<Instant>,
94 new: Option<Instant>,
95 success: Ordering,
96 failure: Ordering,
97 ) -> Result<Option<Instant>, Option<Instant>> {
98 match self.0.compare_exchange(
99 current.map(encode_instant_to_duration),
100 new.map(encode_instant_to_duration),
101 success,
102 failure,
103 ) {
104 Ok(duration) => Ok(duration.map(decode_instant_from_duration)),
105 Err(duration) => Err(duration.map(decode_instant_from_duration)),
106 }
107 }
108
109 #[cfg_attr(not(tarpaulin), inline(always))]
112 pub fn compare_exchange_weak(
113 &self,
114 current: Option<Instant>,
115 new: Option<Instant>,
116 success: Ordering,
117 failure: Ordering,
118 ) -> Result<Option<Instant>, Option<Instant>> {
119 match self.0.compare_exchange_weak(
120 current.map(encode_instant_to_duration),
121 new.map(encode_instant_to_duration),
122 success,
123 failure,
124 ) {
125 Ok(duration) => Ok(duration.map(decode_instant_from_duration)),
126 Err(duration) => Err(duration.map(decode_instant_from_duration)),
127 }
128 }
129
130 #[cfg_attr(not(tarpaulin), inline(always))]
166 pub fn fetch_update<F>(
167 &self,
168 set_order: Ordering,
169 fetch_order: Ordering,
170 mut f: F,
171 ) -> Result<Option<Instant>, Option<Instant>>
172 where
173 F: FnMut(Option<Instant>) -> Option<Option<Instant>>,
174 {
175 self
176 .0
177 .fetch_update(set_order, fetch_order, |duration| {
178 f(duration.map(decode_instant_from_duration))
179 .map(|system_time| system_time.map(encode_instant_to_duration))
180 })
181 .map(|duration| duration.map(decode_instant_from_duration))
182 .map_err(|duration| duration.map(decode_instant_from_duration))
183 }
184
185 #[cfg_attr(not(tarpaulin), inline(always))]
197 pub fn is_lock_free() -> bool {
198 AtomicU128::is_lock_free()
199 }
200
201 #[cfg_attr(not(tarpaulin), inline(always))]
206 pub fn into_inner(self) -> Option<Instant> {
207 self.0.into_inner().map(decode_instant_from_duration)
208 }
209}
210
211#[cfg(feature = "serde")]
212const _: () = {
213 use core::time::Duration;
214 use serde::{Deserialize, Serialize};
215
216 impl Serialize for AtomicOptionInstant {
217 fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
218 self.0.load(Ordering::SeqCst).serialize(serializer)
219 }
220 }
221
222 impl<'de> Deserialize<'de> for AtomicOptionInstant {
223 fn deserialize<D: serde::Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
224 Ok(Self::new(
225 Option::<Duration>::deserialize(deserializer)?.map(decode_instant_from_duration),
226 ))
227 }
228 }
229};
230
231#[cfg(test)]
232mod tests {
233 use super::*;
234 use std::time::Duration;
235
236 #[test]
237 fn test_atomic_option_instant_none() {
238 let atomic_instant = AtomicOptionInstant::none();
239 assert_eq!(atomic_instant.load(Ordering::SeqCst), None);
240 }
241
242 #[test]
243 fn test_atomic_option_instant_now() {
244 let atomic_instant = AtomicOptionInstant::now();
245 let now = Instant::now();
246 if let Some(loaded_instant) = atomic_instant.load(Ordering::SeqCst) {
247 assert!(loaded_instant <= now);
248 assert!(loaded_instant >= now - Duration::from_secs(1));
249 } else {
250 panic!("AtomicOptionInstant::now() should not be None");
251 }
252 }
253
254 #[test]
255 fn test_atomic_option_instant_new_and_load() {
256 let now = Some(Instant::now());
257 let atomic_instant = AtomicOptionInstant::new(now);
258 assert_eq!(atomic_instant.load(Ordering::SeqCst), now);
259 }
260
261 #[test]
262 fn test_atomic_option_instant_store_and_load() {
263 let now = Some(Instant::now());
264 let after_one_sec = now.map(|t| t + Duration::from_secs(1));
265 let atomic_instant = AtomicOptionInstant::new(now);
266 atomic_instant.store(after_one_sec, Ordering::SeqCst);
267 assert_eq!(atomic_instant.load(Ordering::SeqCst), after_one_sec);
268 }
269
270 #[test]
271 fn test_atomic_option_instant_swap() {
272 let now = Some(Instant::now());
273 let after_one_sec = now.map(|t| t + Duration::from_secs(1));
274 let atomic_instant = AtomicOptionInstant::new(now);
275 let prev_instant = atomic_instant.swap(after_one_sec, Ordering::SeqCst);
276 assert_eq!(prev_instant, now);
277 assert_eq!(atomic_instant.load(Ordering::SeqCst), after_one_sec);
278 }
279
280 #[test]
281 fn test_atomic_option_instant_compare_exchange() {
282 let now = Some(Instant::now());
283 let after_one_sec = now.map(|t| t + Duration::from_secs(1));
284 let atomic_instant = AtomicOptionInstant::new(now);
285 let result =
286 atomic_instant.compare_exchange(now, after_one_sec, Ordering::SeqCst, Ordering::SeqCst);
287 assert!(result.is_ok());
288 assert_eq!(atomic_instant.load(Ordering::SeqCst), after_one_sec);
289 }
290
291 #[test]
292 fn test_atomic_option_instant_compare_exchange_weak() {
293 let now = Some(Instant::now());
294 let after_one_sec = now.map(|t| t + Duration::from_secs(1));
295 let atomic_instant = AtomicOptionInstant::new(now);
296
297 let mut result;
298 loop {
299 result = atomic_instant.compare_exchange_weak(
300 now,
301 after_one_sec,
302 Ordering::SeqCst,
303 Ordering::SeqCst,
304 );
305 if result.is_ok() {
306 break;
307 }
308 }
309 assert!(result.is_ok());
310 assert_eq!(atomic_instant.load(Ordering::SeqCst), after_one_sec);
311 }
312
313 #[test]
314 fn test_atomic_option_instant_fetch_update() {
315 let now = Some(Instant::now());
316 let atomic_instant = AtomicOptionInstant::new(now);
317
318 let result = atomic_instant.fetch_update(Ordering::SeqCst, Ordering::SeqCst, |prev| {
319 Some(prev.map(|t| t + Duration::from_secs(1)))
320 });
321 assert!(result.is_ok());
322 assert_eq!(result.unwrap(), now);
323 assert_eq!(
324 atomic_instant.load(Ordering::SeqCst),
325 now.map(|t| t + Duration::from_secs(1))
326 );
327 }
328
329 #[test]
330 fn test_atomic_option_instant_thread_safety() {
331 use std::sync::Arc;
332 use std::thread;
333
334 let start = Instant::now();
339 let atomic_time = Arc::new(AtomicOptionInstant::new(Some(start)));
340 let mut handles = vec![];
341
342 for _ in 0..4 {
343 let atomic_clone = Arc::clone(&atomic_time);
344 let handle = thread::spawn(move || {
345 atomic_clone
346 .fetch_update(Ordering::SeqCst, Ordering::SeqCst, |current| {
347 current.map(|t| Some(t + Duration::from_secs(1)))
348 })
349 .expect("atomic is always Some in this test");
350 });
351 handles.push(handle);
352 }
353
354 for handle in handles {
355 handle.join().unwrap();
356 }
357
358 assert_eq!(
360 atomic_time.load(Ordering::SeqCst),
361 Some(start + Duration::from_secs(4))
362 );
363 }
364
365 #[test]
366 fn test_atomic_option_instant_debug() {
367 let atomic_instant = AtomicOptionInstant::now();
368 let debug_str = format!("{:?}", atomic_instant);
369 assert!(debug_str.contains("AtomicOptionInstant"));
370 }
371
372 #[test]
373 fn test_atomic_option_instant_default() {
374 let atomic_instant = AtomicOptionInstant::default();
375 assert_eq!(atomic_instant.load(Ordering::SeqCst), None);
376 }
377
378 #[test]
379 fn test_atomic_option_instant_from() {
380 let now = Some(Instant::now());
381 let atomic_instant = AtomicOptionInstant::from(now);
382 assert_eq!(atomic_instant.load(Ordering::SeqCst), now);
383 }
384
385 #[test]
386 fn test_atomic_option_instant_from_none() {
387 let atomic_instant = AtomicOptionInstant::from(None);
388 assert_eq!(atomic_instant.load(Ordering::SeqCst), None);
389 }
390
391 #[test]
392 fn test_atomic_option_instant_into_inner() {
393 let now = Some(Instant::now());
394 let atomic_instant = AtomicOptionInstant::new(now);
395 assert_eq!(atomic_instant.into_inner(), now);
396 }
397
398 #[test]
399 fn test_atomic_option_instant_into_inner_none() {
400 let atomic_instant = AtomicOptionInstant::none();
401 assert_eq!(atomic_instant.into_inner(), None);
402 }
403
404 #[test]
405 fn test_atomic_option_instant_compare_exchange_failure() {
406 let now = Some(Instant::now());
407 let other = now.map(|t| t + Duration::from_secs(5));
408 let atomic_instant = AtomicOptionInstant::new(now);
409 let result = atomic_instant.compare_exchange(other, other, Ordering::SeqCst, Ordering::SeqCst);
410 assert!(result.is_err());
411 assert_eq!(result.unwrap_err(), now);
412 }
413
414 #[test]
415 fn test_atomic_option_instant_compare_exchange_weak_failure() {
416 let now = Some(Instant::now());
417 let other = now.map(|t| t + Duration::from_secs(5));
418 let atomic_instant = AtomicOptionInstant::new(now);
419 let result =
420 atomic_instant.compare_exchange_weak(other, other, Ordering::SeqCst, Ordering::SeqCst);
421 assert!(result.is_err());
422 }
423
424 #[test]
425 fn test_atomic_option_instant_fetch_update_failure() {
426 let now = Some(Instant::now());
427 let atomic_instant = AtomicOptionInstant::new(now);
428 let result = atomic_instant.fetch_update(Ordering::SeqCst, Ordering::SeqCst, |_| None);
429 assert!(result.is_err());
430 assert_eq!(result.unwrap_err(), now);
431 }
432
433 #[cfg(feature = "serde")]
434 #[test]
435 fn test_atomic_option_instant_serde() {
436 use serde::{Deserialize, Serialize};
437
438 #[derive(Serialize, Deserialize)]
439 struct Test {
440 time: AtomicOptionInstant,
441 }
442
443 let now = Instant::now();
444 let test = Test {
445 time: AtomicOptionInstant::new(Some(now)),
446 };
447 let serialized = serde_json::to_string(&test).unwrap();
448 let deserialized: Test = serde_json::from_str(&serialized).unwrap();
449 assert_eq!(deserialized.time.load(Ordering::SeqCst), Some(now));
450 }
451
452 #[test]
453 fn decode_option_instant_from_extreme_duration_does_not_panic() {
454 let max_dur = Duration::new(u64::MAX, 999_999_999);
455 let decoded = crate::utils::decode_instant_from_duration(max_dur);
456 let _ = decoded;
458 }
459
460 #[cfg(feature = "serde")]
461 #[test]
462 fn deserialize_extreme_option_instant_does_not_panic() {
463 let json = r#"{"secs":18446744073709551615,"nanos":999999999}"#;
467 let result: Result<AtomicOptionInstant, _> = serde_json::from_str(json);
468 assert!(
469 result.is_ok(),
470 "deserialization of extreme Option<Instant> should not panic"
471 );
472 assert!(result.unwrap().load(Ordering::SeqCst).is_some());
473 }
474}