Skip to main content

atomic_time/
option_instant.rs

1use std::time::Instant;
2
3use super::*;
4
5/// Atomic version of [`Option<Instant>`].
6///
7/// Values are encoded relative to a process-local baseline pairing
8/// `std::time::SystemTime::now()` with `std::time::Instant::now()` (see
9/// [`crate::utils::encode_instant_to_duration`] and
10/// [`crate::utils::decode_instant_from_duration`]). Within the same process,
11/// values in the platform's representable `Instant` range round-trip exactly.
12/// Encodings are not portable across processes or restarts: there they only
13/// approximate wall-clock time and must not be used as persistent deadlines.
14/// System clock adjustments can affect that cross-process interpretation, while
15/// behavior across system sleep follows the platform's `Instant` semantics.
16///
17/// With the `serde` feature, this type retains its `Option<Duration>` proxy
18/// wire format. For persistent wall-clock values, use
19/// [`crate::AtomicOptionSystemTime`].
20#[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  /// Equivalent to `Option::<Instant>::None`.
32  #[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  /// Equivalent to atomic version `Option::<Instant>::None`.
46  ///
47  /// # Examples
48  ///
49  /// ```rust
50  /// use atomic_time::AtomicOptionInstant;
51  ///
52  /// let none = AtomicOptionInstant::none();
53  /// assert_eq!(none.load(std::sync::atomic::Ordering::SeqCst), None);
54  /// ```
55  #[inline(always)]
56  pub const fn none() -> Self {
57    Self(AtomicOptionDuration::new(None))
58  }
59
60  /// Returns the instant corresponding to "now".
61  ///
62  /// # Examples
63  /// ```
64  /// use atomic_time::AtomicOptionInstant;
65  ///
66  /// let now = AtomicOptionInstant::now();
67  /// ```
68  #[inline(always)]
69  pub fn now() -> Self {
70    Self::new(Some(Instant::now()))
71  }
72
73  /// Creates a new `AtomicOptionInstant` with the given `Instant` value.
74  #[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  /// Loads a value from the atomic instant.
82  #[inline(always)]
83  pub fn load(&self, order: Ordering) -> Option<Instant> {
84    self.0.load(order).map(decode_instant_from_duration)
85  }
86
87  /// Stores a value into the atomic instant.
88  #[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  /// Stores a value into the atomic instant, returning the previous value.
94  #[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  /// Stores a value into the atomic instant if the current value is the same as the `current`
103  /// value.
104  #[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  /// Stores a value into the atomic instant if the current value is the same as the `current`
124  /// value.
125  #[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  /// Fetches the value and applies a function that can choose whether to store
145  /// a new value.
146  ///
147  /// Returns `Ok(previous_value)` when `f` returns `Some(_)`, and
148  /// `Err(previous_value)` when it returns `None`. The outer `Option` is the
149  /// update decision; returning `Some(None)` stores `None`. The closure can run
150  /// more than once when another thread changes the value concurrently, but it
151  /// is applied only once to the value that is stored.
152  ///
153  /// `set_order` describes the ordering of the successful update and
154  /// `fetch_order` describes failed compare-and-exchange loads. They have the
155  /// same requirements as the success and failure orderings of
156  /// [`compare_exchange`](Self::compare_exchange).
157  ///
158  /// # Examples
159  ///
160  /// ```rust
161  /// use atomic_time::AtomicOptionInstant;
162  /// use std::{sync::atomic::Ordering, time::{Duration, Instant}};
163  ///
164  /// let start = Instant::now();
165  /// let x = AtomicOptionInstant::new(Some(start));
166  /// assert_eq!(x.try_update(Ordering::SeqCst, Ordering::SeqCst, |_| None), Err(Some(start)));
167  /// assert_eq!(x.try_update(Ordering::SeqCst, Ordering::SeqCst, |old| Some(old.map(|instant| instant + Duration::from_secs(1)))), Ok(Some(start)));
168  /// ```
169  #[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  /// Fetches the value, and applies a function to it that returns an optional
190  /// new value. Returns a `Result` of `Ok(previous_value)` if the function returned `Some(_)`, else
191  /// `Err(previous_value)`.
192  ///
193  /// Note: This may call the function multiple times if the value has been changed from other threads in
194  /// the meantime, as long as the function returns `Some(_)`, but the function will have been applied
195  /// only once to the stored value.
196  ///
197  /// `fetch_update` takes two [`Ordering`] arguments to describe the memory ordering of this operation.
198  /// The first describes the required ordering for when the operation finally succeeds while the second
199  /// describes the required ordering for loads. These correspond to the success and failure orderings of
200  /// [`compare_exchange`] respectively.
201  ///
202  /// Using [`Acquire`](Ordering::Acquire) as success ordering makes the store part
203  /// of this operation [`Relaxed`](Ordering::Relaxed), and using [`Release`](Ordering::Release) makes the final successful load
204  /// [`Relaxed`](Ordering::Relaxed). The (failed) load ordering can only be [`SeqCst`](Ordering::SeqCst), [`Acquire`](Ordering::Acquire) or [`Relaxed`](Ordering::Relaxed)
205  /// and must be equivalent to or weaker than the success ordering.
206  ///
207  /// [`compare_exchange`]: #method.compare_exchange
208  ///
209  /// # Examples
210  ///
211  /// ```rust
212  /// use atomic_time::AtomicOptionInstant;
213  /// use std::{time::{Duration, Instant}, sync::atomic::Ordering};
214  ///
215  /// let now = Instant::now();
216  /// let x = AtomicOptionInstant::none();
217  /// assert_eq!(x.fetch_update(Ordering::SeqCst, Ordering::SeqCst, |_| None), Err(None));
218  /// x.store(Some(now), Ordering::SeqCst);
219  /// assert_eq!(x.fetch_update(Ordering::SeqCst, Ordering::SeqCst, |x| Some(x.map(|val| val + Duration::from_secs(1)))), Ok(Some(now)));
220  /// assert_eq!(x.fetch_update(Ordering::SeqCst, Ordering::SeqCst, |x| Some(x.map(|val| val + Duration::from_secs(1)))), Ok(Some(now + Duration::from_secs(1))));
221  /// assert_eq!(x.fetch_update(Ordering::SeqCst, Ordering::SeqCst, |x| Some(x.map(|val| val + Duration::from_secs(1)))), Ok(Some(now + Duration::from_secs(2))));
222  /// assert_eq!(x.load(Ordering::SeqCst), Some(now + Duration::from_secs(3)));
223  /// ```
224  #[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  /// Fetches the value, applies a function to produce a new value, stores it,
238  /// and returns the previous value.
239  ///
240  /// The closure can run more than once when another thread changes the value
241  /// concurrently, but it is applied only once to the value that is stored.
242  /// `set_order` and `fetch_order` have the same requirements as the success
243  /// and failure orderings of [`compare_exchange`](Self::compare_exchange).
244  ///
245  /// # Examples
246  ///
247  /// ```rust
248  /// use atomic_time::AtomicOptionInstant;
249  /// use std::{sync::atomic::Ordering, time::{Duration, Instant}};
250  ///
251  /// let start = Instant::now();
252  /// let x = AtomicOptionInstant::new(Some(start));
253  /// assert_eq!(x.update(Ordering::SeqCst, Ordering::SeqCst, |old| old.map(|instant| instant + Duration::from_secs(1))), Some(start));
254  /// assert_eq!(x.load(Ordering::SeqCst), Some(start + Duration::from_secs(1)));
255  /// ```
256  #[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  /// Atomically stores the smaller of the current value and `val`, returning
270  /// the previous value.
271  ///
272  /// `None` is ordered before every `Some` value. `Some` values are ordered by
273  /// the process-local baseline encoding. This ordering is meaningful only for
274  /// values using the same process-local baseline; it does not assign ordering
275  /// semantics across processes or process restarts. `order` describes the
276  /// memory ordering of the read-modify-write operation. Using
277  /// [`Acquire`](Ordering::Acquire) makes its store part relaxed, and using
278  /// [`Release`](Ordering::Release) makes its load part relaxed.
279  ///
280  /// # Examples
281  ///
282  /// ```rust
283  /// use atomic_time::AtomicOptionInstant;
284  /// use std::{sync::atomic::Ordering, time::{Duration, Instant}};
285  ///
286  /// let middle = Instant::now();
287  /// let low = middle - Duration::from_secs(1);
288  /// let high = middle + Duration::from_secs(1);
289  /// let x = AtomicOptionInstant::new(Some(middle));
290  /// assert_eq!(x.fetch_min(Some(high), Ordering::SeqCst), Some(middle));
291  /// assert_eq!(x.fetch_min(Some(low), Ordering::SeqCst), Some(middle));
292  /// assert_eq!(x.load(Ordering::SeqCst), Some(low));
293  /// ```
294  #[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  /// Atomically stores the larger of the current value and `val`, returning
303  /// the previous value.
304  ///
305  /// `None` is ordered before every `Some` value. `Some` values are ordered by
306  /// the process-local baseline encoding. This ordering is meaningful only for
307  /// values using the same process-local baseline; it does not assign ordering
308  /// semantics across processes or process restarts. `order` describes the
309  /// memory ordering of the read-modify-write operation. Using
310  /// [`Acquire`](Ordering::Acquire) makes its store part relaxed, and using
311  /// [`Release`](Ordering::Release) makes its load part relaxed.
312  ///
313  /// # Examples
314  ///
315  /// ```rust
316  /// use atomic_time::AtomicOptionInstant;
317  /// use std::{sync::atomic::Ordering, time::{Duration, Instant}};
318  ///
319  /// let high = Instant::now() + Duration::from_secs(1);
320  /// let x = AtomicOptionInstant::none();
321  /// assert_eq!(x.fetch_max(None, Ordering::SeqCst), None);
322  /// assert_eq!(x.fetch_max(Some(high), Ordering::SeqCst), None);
323  /// assert_eq!(x.load(Ordering::SeqCst), Some(high));
324  /// ```
325  #[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  /// Returns `true` if operations on values of this type are lock-free.
334  /// If the compiler or the platform doesn't support the necessary
335  /// atomic instructions, global locks for every potentially
336  /// concurrent atomic operation will be used.
337  ///
338  /// # Examples
339  /// ```
340  /// use atomic_time::AtomicOptionInstant;
341  ///
342  /// let is_lock_free = AtomicOptionInstant::is_lock_free();
343  /// ```
344  #[inline(always)]
345  pub fn is_lock_free() -> bool {
346    AtomicU128::is_lock_free()
347  }
348
349  /// Returns whether operations on values of this type are always lock-free.
350  ///
351  /// A `false` result does not preclude lock-free operations selected through
352  /// runtime CPU feature detection; use [`is_lock_free`](Self::is_lock_free)
353  /// to query the current target at runtime.
354  ///
355  /// # Examples
356  ///
357  /// ```rust
358  /// use atomic_time::AtomicOptionInstant;
359  ///
360  /// const ALWAYS_LOCK_FREE: bool = AtomicOptionInstant::is_always_lock_free();
361  /// let _ = ALWAYS_LOCK_FREE;
362  /// ```
363  #[inline(always)]
364  pub const fn is_always_lock_free() -> bool {
365    AtomicOptionDuration::is_always_lock_free()
366  }
367
368  /// Consumes the atomic and returns the contained value.
369  ///
370  /// This is safe because passing `self` by value guarantees that no other threads are
371  /// concurrently accessing the atomic data.
372  #[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    // Fixed starting value + CAS loop = exact final result. The
594    // earlier implementation did `load + add + store` (not a CAS) and
595    // asserted "within 4 seconds of now", which even a single
596    // surviving write would satisfy.
597    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    // 4 threads × 1 second = 4 seconds, no lost updates.
618    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    // Inputs outside the platform Instant range use the process baseline as a
726    // fallback rather than saturating an Instant value.
727    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    // Preserve the existing Ok deserialization behavior for an extreme wire
738    // value; its decoded Instant falls back to the process baseline.
739    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}