1use std::time::Instant;
2
3use super::*;
4
5#[repr(transparent)]
21pub struct AtomicOptionInstant(AtomicOptionDuration);
22
23impl core::fmt::Debug for AtomicOptionInstant {
24 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
25 f.debug_tuple("AtomicOptionInstant")
26 .field(&self.load(Ordering::SeqCst))
27 .finish()
28 }
29}
30impl Default for AtomicOptionInstant {
31 #[inline(always)]
33 fn default() -> Self {
34 Self::none()
35 }
36}
37impl From<Option<Instant>> for AtomicOptionInstant {
38 #[inline(always)]
39 fn from(instant: Option<Instant>) -> Self {
40 Self::new(instant)
41 }
42}
43
44impl AtomicOptionInstant {
45 #[inline(always)]
56 pub const fn none() -> Self {
57 Self(AtomicOptionDuration::new(None))
58 }
59
60 #[inline(always)]
69 pub fn now() -> Self {
70 Self::new(Some(Instant::now()))
71 }
72
73 #[inline(always)]
75 pub fn new(instant: Option<Instant>) -> Self {
76 Self(AtomicOptionDuration::new(
77 instant.map(encode_instant_to_duration),
78 ))
79 }
80
81 #[inline(always)]
83 pub fn load(&self, order: Ordering) -> Option<Instant> {
84 self.0.load(order).map(decode_instant_from_duration)
85 }
86
87 #[inline(always)]
89 pub fn store(&self, instant: Option<Instant>, order: Ordering) {
90 self.0.store(instant.map(encode_instant_to_duration), order)
91 }
92
93 #[inline(always)]
95 pub fn swap(&self, instant: Option<Instant>, order: Ordering) -> Option<Instant> {
96 self
97 .0
98 .swap(instant.map(encode_instant_to_duration), order)
99 .map(decode_instant_from_duration)
100 }
101
102 #[inline(always)]
105 pub fn compare_exchange(
106 &self,
107 current: Option<Instant>,
108 new: Option<Instant>,
109 success: Ordering,
110 failure: Ordering,
111 ) -> Result<Option<Instant>, Option<Instant>> {
112 match self.0.compare_exchange(
113 current.map(encode_instant_to_duration),
114 new.map(encode_instant_to_duration),
115 success,
116 failure,
117 ) {
118 Ok(duration) => Ok(duration.map(decode_instant_from_duration)),
119 Err(duration) => Err(duration.map(decode_instant_from_duration)),
120 }
121 }
122
123 #[inline(always)]
126 pub fn compare_exchange_weak(
127 &self,
128 current: Option<Instant>,
129 new: Option<Instant>,
130 success: Ordering,
131 failure: Ordering,
132 ) -> Result<Option<Instant>, Option<Instant>> {
133 match self.0.compare_exchange_weak(
134 current.map(encode_instant_to_duration),
135 new.map(encode_instant_to_duration),
136 success,
137 failure,
138 ) {
139 Ok(duration) => Ok(duration.map(decode_instant_from_duration)),
140 Err(duration) => Err(duration.map(decode_instant_from_duration)),
141 }
142 }
143
144 #[inline(always)]
170 pub fn try_update<F>(
171 &self,
172 set_order: Ordering,
173 fetch_order: Ordering,
174 mut f: F,
175 ) -> Result<Option<Instant>, Option<Instant>>
176 where
177 F: FnMut(Option<Instant>) -> Option<Option<Instant>>,
178 {
179 self
180 .0
181 .try_update(set_order, fetch_order, |duration| {
182 f(duration.map(decode_instant_from_duration))
183 .map(|instant| instant.map(encode_instant_to_duration))
184 })
185 .map(|duration| duration.map(decode_instant_from_duration))
186 .map_err(|duration| duration.map(decode_instant_from_duration))
187 }
188
189 #[inline(always)]
225 pub fn fetch_update<F>(
226 &self,
227 set_order: Ordering,
228 fetch_order: Ordering,
229 f: F,
230 ) -> Result<Option<Instant>, Option<Instant>>
231 where
232 F: FnMut(Option<Instant>) -> Option<Option<Instant>>,
233 {
234 self.try_update(set_order, fetch_order, f)
235 }
236
237 #[inline(always)]
257 pub fn update<F>(&self, set_order: Ordering, fetch_order: Ordering, mut f: F) -> Option<Instant>
258 where
259 F: FnMut(Option<Instant>) -> Option<Instant>,
260 {
261 self
262 .0
263 .update(set_order, fetch_order, |duration| {
264 f(duration.map(decode_instant_from_duration)).map(encode_instant_to_duration)
265 })
266 .map(decode_instant_from_duration)
267 }
268
269 #[inline(always)]
295 pub fn fetch_min(&self, val: Option<Instant>, order: Ordering) -> Option<Instant> {
296 self
297 .0
298 .fetch_min(val.map(encode_instant_to_duration), order)
299 .map(decode_instant_from_duration)
300 }
301
302 #[inline(always)]
326 pub fn fetch_max(&self, val: Option<Instant>, order: Ordering) -> Option<Instant> {
327 self
328 .0
329 .fetch_max(val.map(encode_instant_to_duration), order)
330 .map(decode_instant_from_duration)
331 }
332
333 #[inline(always)]
345 pub fn is_lock_free() -> bool {
346 AtomicU128::is_lock_free()
347 }
348
349 #[inline(always)]
364 pub const fn is_always_lock_free() -> bool {
365 AtomicOptionDuration::is_always_lock_free()
366 }
367
368 #[inline(always)]
373 pub fn into_inner(self) -> Option<Instant> {
374 self.0.into_inner().map(decode_instant_from_duration)
375 }
376}
377
378#[cfg(feature = "serde")]
379const _: () = {
380 use core::time::Duration;
381 use serde::{Deserialize, Serialize};
382
383 impl Serialize for AtomicOptionInstant {
384 fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
385 self.0.load(Ordering::SeqCst).serialize(serializer)
386 }
387 }
388
389 impl<'de> Deserialize<'de> for AtomicOptionInstant {
390 fn deserialize<D: serde::Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
391 Ok(Self::new(
392 Option::<Duration>::deserialize(deserializer)?.map(decode_instant_from_duration),
393 ))
394 }
395 }
396};
397
398#[cfg(test)]
399mod tests {
400 use super::*;
401 use std::time::Duration;
402
403 #[test]
404 fn test_atomic_option_instant_none() {
405 let atomic_instant = AtomicOptionInstant::none();
406 assert_eq!(atomic_instant.load(Ordering::SeqCst), None);
407 }
408
409 #[test]
410 fn test_atomic_option_instant_now() {
411 let atomic_instant = AtomicOptionInstant::now();
412 let now = Instant::now();
413 if let Some(loaded_instant) = atomic_instant.load(Ordering::SeqCst) {
414 assert!(loaded_instant <= now);
415 assert!(loaded_instant >= now - Duration::from_secs(1));
416 } else {
417 panic!("AtomicOptionInstant::now() should not be None");
418 }
419 }
420
421 #[test]
422 fn test_atomic_option_instant_new_and_load() {
423 let now = Some(Instant::now());
424 let atomic_instant = AtomicOptionInstant::new(now);
425 assert_eq!(atomic_instant.load(Ordering::SeqCst), now);
426 }
427
428 #[test]
429 fn test_atomic_option_instant_store_and_load() {
430 let now = Some(Instant::now());
431 let after_one_sec = now.map(|t| t + Duration::from_secs(1));
432 let atomic_instant = AtomicOptionInstant::new(now);
433 atomic_instant.store(after_one_sec, Ordering::SeqCst);
434 assert_eq!(atomic_instant.load(Ordering::SeqCst), after_one_sec);
435 }
436
437 #[test]
438 fn test_atomic_option_instant_swap() {
439 let now = Some(Instant::now());
440 let after_one_sec = now.map(|t| t + Duration::from_secs(1));
441 let atomic_instant = AtomicOptionInstant::new(now);
442 let prev_instant = atomic_instant.swap(after_one_sec, Ordering::SeqCst);
443 assert_eq!(prev_instant, now);
444 assert_eq!(atomic_instant.load(Ordering::SeqCst), after_one_sec);
445 }
446
447 #[test]
448 fn test_atomic_option_instant_compare_exchange() {
449 let now = Some(Instant::now());
450 let after_one_sec = now.map(|t| t + Duration::from_secs(1));
451 let atomic_instant = AtomicOptionInstant::new(now);
452 let result =
453 atomic_instant.compare_exchange(now, after_one_sec, Ordering::SeqCst, Ordering::SeqCst);
454 assert!(result.is_ok());
455 assert_eq!(atomic_instant.load(Ordering::SeqCst), after_one_sec);
456 }
457
458 #[test]
459 fn test_atomic_option_instant_compare_exchange_weak() {
460 let now = Some(Instant::now());
461 let after_one_sec = now.map(|t| t + Duration::from_secs(1));
462 let atomic_instant = AtomicOptionInstant::new(now);
463
464 let mut result;
465 loop {
466 result = atomic_instant.compare_exchange_weak(
467 now,
468 after_one_sec,
469 Ordering::SeqCst,
470 Ordering::SeqCst,
471 );
472 if result.is_ok() {
473 break;
474 }
475 }
476 assert!(result.is_ok());
477 assert_eq!(atomic_instant.load(Ordering::SeqCst), after_one_sec);
478 }
479
480 #[test]
481 fn test_atomic_option_instant_fetch_update() {
482 let now = Some(Instant::now());
483 let atomic_instant = AtomicOptionInstant::new(now);
484
485 let result = atomic_instant.fetch_update(Ordering::SeqCst, Ordering::SeqCst, |prev| {
486 Some(prev.map(|t| t + Duration::from_secs(1)))
487 });
488 assert!(result.is_ok());
489 assert_eq!(result.unwrap(), now);
490 assert_eq!(
491 atomic_instant.load(Ordering::SeqCst),
492 now.map(|t| t + Duration::from_secs(1))
493 );
494 }
495
496 #[test]
497 fn test_atomic_option_instant_try_update() {
498 let start = Instant::now();
499 let atomic_instant = AtomicOptionInstant::new(Some(start));
500
501 assert_eq!(
502 atomic_instant.try_update(Ordering::SeqCst, Ordering::SeqCst, |_| None),
503 Err(Some(start))
504 );
505 assert_eq!(
506 atomic_instant.try_update(Ordering::SeqCst, Ordering::SeqCst, |current| {
507 Some(current.map(|instant| instant + Duration::from_secs(2)))
508 }),
509 Ok(Some(start))
510 );
511 assert_eq!(
512 atomic_instant.load(Ordering::SeqCst),
513 Some(start + Duration::from_secs(2))
514 );
515 }
516
517 #[test]
518 fn test_atomic_option_instant_update_returns_previous_value() {
519 let start = Instant::now();
520 let atomic_instant = AtomicOptionInstant::new(Some(start));
521
522 assert_eq!(
523 atomic_instant.update(Ordering::SeqCst, Ordering::SeqCst, |current| {
524 current.map(|instant| instant + Duration::from_secs(2))
525 }),
526 Some(start)
527 );
528 assert_eq!(
529 atomic_instant.load(Ordering::SeqCst),
530 Some(start + Duration::from_secs(2))
531 );
532 }
533
534 #[test]
535 fn test_atomic_option_instant_fetch_min_and_max() {
536 let middle = Instant::now();
537 let low = middle.checked_sub(Duration::from_secs(2)).unwrap();
538 let high = middle.checked_add(Duration::from_secs(2)).unwrap();
539 let atomic_instant = AtomicOptionInstant::new(Some(middle));
540
541 assert_eq!(
542 atomic_instant.fetch_min(Some(high), Ordering::SeqCst),
543 Some(middle)
544 );
545 assert_eq!(atomic_instant.load(Ordering::SeqCst), Some(middle));
546 assert_eq!(
547 atomic_instant.fetch_min(Some(middle), Ordering::SeqCst),
548 Some(middle)
549 );
550 assert_eq!(atomic_instant.load(Ordering::SeqCst), Some(middle));
551 assert_eq!(
552 atomic_instant.fetch_min(Some(low), Ordering::SeqCst),
553 Some(middle)
554 );
555 assert_eq!(atomic_instant.load(Ordering::SeqCst), Some(low));
556
557 atomic_instant.store(Some(middle), Ordering::SeqCst);
558 assert_eq!(
559 atomic_instant.fetch_max(None, Ordering::SeqCst),
560 Some(middle)
561 );
562 assert_eq!(atomic_instant.load(Ordering::SeqCst), Some(middle));
563 assert_eq!(
564 atomic_instant.fetch_max(Some(middle), Ordering::SeqCst),
565 Some(middle)
566 );
567 assert_eq!(atomic_instant.load(Ordering::SeqCst), Some(middle));
568 assert_eq!(
569 atomic_instant.fetch_max(Some(high), Ordering::SeqCst),
570 Some(middle)
571 );
572 assert_eq!(atomic_instant.load(Ordering::SeqCst), Some(high));
573
574 atomic_instant.store(None, Ordering::SeqCst);
575 assert_eq!(atomic_instant.fetch_min(None, Ordering::SeqCst), None);
576 assert_eq!(atomic_instant.load(Ordering::SeqCst), None);
577 assert_eq!(atomic_instant.fetch_max(None, Ordering::SeqCst), None);
578 assert_eq!(atomic_instant.load(Ordering::SeqCst), None);
579 assert_eq!(atomic_instant.fetch_max(Some(low), Ordering::SeqCst), None);
580 assert_eq!(atomic_instant.load(Ordering::SeqCst), Some(low));
581 }
582
583 #[test]
584 fn test_atomic_option_instant_always_lock_free_implies_lock_free() {
585 assert!(!AtomicOptionInstant::is_always_lock_free() || AtomicOptionInstant::is_lock_free());
586 }
587
588 #[test]
589 fn test_atomic_option_instant_thread_safety() {
590 use std::sync::Arc;
591 use std::thread;
592
593 let start = Instant::now();
598 let atomic_time = Arc::new(AtomicOptionInstant::new(Some(start)));
599 let mut handles = vec![];
600
601 for _ in 0..4 {
602 let atomic_clone = Arc::clone(&atomic_time);
603 let handle = thread::spawn(move || {
604 atomic_clone
605 .fetch_update(Ordering::SeqCst, Ordering::SeqCst, |current| {
606 current.map(|t| Some(t + Duration::from_secs(1)))
607 })
608 .expect("atomic is always Some in this test");
609 });
610 handles.push(handle);
611 }
612
613 for handle in handles {
614 handle.join().unwrap();
615 }
616
617 assert_eq!(
619 atomic_time.load(Ordering::SeqCst),
620 Some(start + Duration::from_secs(4))
621 );
622 }
623
624 #[test]
625 fn test_atomic_option_instant_debug() {
626 let atomic_instant = AtomicOptionInstant::now();
627 let debug_str = format!("{:?}", atomic_instant);
628 assert!(debug_str.contains("AtomicOptionInstant"));
629 }
630
631 #[test]
632 fn test_atomic_option_instant_default() {
633 let atomic_instant = AtomicOptionInstant::default();
634 assert_eq!(atomic_instant.load(Ordering::SeqCst), None);
635 }
636
637 #[test]
638 fn test_atomic_option_instant_from() {
639 let now = Some(Instant::now());
640 let atomic_instant = AtomicOptionInstant::from(now);
641 assert_eq!(atomic_instant.load(Ordering::SeqCst), now);
642 }
643
644 #[test]
645 fn test_atomic_option_instant_from_none() {
646 let atomic_instant = AtomicOptionInstant::from(None);
647 assert_eq!(atomic_instant.load(Ordering::SeqCst), None);
648 }
649
650 #[test]
651 fn test_atomic_option_instant_into_inner() {
652 let now = Some(Instant::now());
653 let atomic_instant = AtomicOptionInstant::new(now);
654 assert_eq!(atomic_instant.into_inner(), now);
655 }
656
657 #[test]
658 fn test_atomic_option_instant_into_inner_none() {
659 let atomic_instant = AtomicOptionInstant::none();
660 assert_eq!(atomic_instant.into_inner(), None);
661 }
662
663 #[test]
664 fn test_atomic_option_instant_compare_exchange_failure() {
665 let now = Some(Instant::now());
666 let other = now.map(|t| t + Duration::from_secs(5));
667 let atomic_instant = AtomicOptionInstant::new(now);
668 let result = atomic_instant.compare_exchange(other, other, Ordering::SeqCst, Ordering::SeqCst);
669 assert!(result.is_err());
670 assert_eq!(result.unwrap_err(), now);
671 }
672
673 #[test]
674 fn test_atomic_option_instant_compare_exchange_weak_failure() {
675 let now = Some(Instant::now());
676 let other = now.map(|t| t + Duration::from_secs(5));
677 let atomic_instant = AtomicOptionInstant::new(now);
678 let result =
679 atomic_instant.compare_exchange_weak(other, other, Ordering::SeqCst, Ordering::SeqCst);
680 assert!(result.is_err());
681 }
682
683 #[test]
684 fn test_atomic_option_instant_fetch_update_failure() {
685 let now = Some(Instant::now());
686 let atomic_instant = AtomicOptionInstant::new(now);
687 let result = atomic_instant.fetch_update(Ordering::SeqCst, Ordering::SeqCst, |_| None);
688 assert!(result.is_err());
689 assert_eq!(result.unwrap_err(), now);
690 }
691
692 #[cfg(feature = "serde")]
693 #[test]
694 fn test_atomic_option_instant_serde_round_trip() {
695 let now = Instant::now();
696 for instant in [
697 None,
698 Some(now.checked_sub(Duration::from_secs(1)).unwrap()),
699 Some(now.checked_add(Duration::from_secs(1)).unwrap()),
700 ] {
701 let atomic = AtomicOptionInstant::new(instant);
702 let serialized = serde_json::to_string(&atomic).unwrap();
703 let deserialized: AtomicOptionInstant = serde_json::from_str(&serialized).unwrap();
704 assert_eq!(deserialized.load(Ordering::SeqCst), instant);
705 }
706 }
707
708 #[cfg(feature = "serde")]
709 #[test]
710 fn test_atomic_option_instant_serde_matches_option_duration_wire_model() {
711 for instant in [None, Some(Instant::now())] {
712 let atomic = AtomicOptionInstant::new(instant);
713 let wire_model = instant.map(crate::utils::encode_instant_to_duration);
714 assert_eq!(
715 serde_json::to_string(&atomic).unwrap(),
716 serde_json::to_string(&wire_model).unwrap()
717 );
718 }
719 }
720
721 #[test]
722 fn decode_extreme_option_instant_falls_back_to_baseline() {
723 let max_dur = Duration::new(u64::MAX, 999_999_999);
724 let decoded = crate::utils::decode_instant_from_duration(max_dur);
725 let other_extreme = Duration::new(u64::MAX, 999_999_998);
728 assert_eq!(
729 decoded,
730 crate::utils::decode_instant_from_duration(other_extreme)
731 );
732 }
733
734 #[cfg(feature = "serde")]
735 #[test]
736 fn deserialize_extreme_option_instant_uses_baseline_fallback() {
737 let json = r#"{"secs":18446744073709551615,"nanos":999999999}"#;
740 let result: Result<AtomicOptionInstant, _> = serde_json::from_str(json);
741 let atomic = result.expect("extreme Option<Instant> deserialization remains Ok");
742 assert_eq!(
743 atomic.load(Ordering::SeqCst),
744 Some(crate::utils::decode_instant_from_duration(Duration::new(
745 u64::MAX,
746 999_999_999
747 )))
748 );
749 }
750}