Skip to main content

atomic_time/
option_duration.rs

1use crate::AtomicU128;
2use core::{sync::atomic::Ordering, time::Duration};
3
4/// Atomic version of [`Option<Duration>`].
5#[repr(transparent)]
6pub struct AtomicOptionDuration(AtomicU128);
7impl core::fmt::Debug for AtomicOptionDuration {
8  fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
9    f.debug_tuple("AtomicOptionDuration")
10      .field(&self.load(Ordering::SeqCst))
11      .finish()
12  }
13}
14impl Default for AtomicOptionDuration {
15  /// Creates an `AtomicOptionDuration` initialized to `None`.
16  #[inline(always)]
17  fn default() -> Self {
18    Self::none()
19  }
20}
21impl From<Option<Duration>> for AtomicOptionDuration {
22  #[inline(always)]
23  fn from(duration: Option<Duration>) -> Self {
24    Self::new(duration)
25  }
26}
27impl AtomicOptionDuration {
28  /// Creates a new `AtomicOptionDuration` with `None`.
29  #[inline(always)]
30  pub const fn none() -> Self {
31    Self(AtomicU128::new(encode_option_duration(None)))
32  }
33
34  /// Creates a new `AtomicOptionDuration` with the given value.
35  #[inline(always)]
36  pub const fn new(duration: Option<Duration>) -> Self {
37    Self(AtomicU128::new(encode_option_duration(duration)))
38  }
39  /// Loads `Option<Duration>` from `AtomicOptionDuration`.
40  ///
41  /// load takes an [`Ordering`] argument which describes the memory ordering of this operation.
42  ///
43  /// # Panics
44  /// Panics if order is [`Release`](Ordering::Release) or [`AcqRel`](Ordering::AcqRel).
45  #[inline(always)]
46  pub fn load(&self, ordering: Ordering) -> Option<Duration> {
47    decode_option_duration(self.0.load(ordering))
48  }
49  /// Stores a value into the `AtomicOptionDuration`.
50  ///
51  /// `store` takes an [`Ordering`] argument which describes the memory ordering
52  /// of this operation.
53  ///
54  /// # Panics
55  ///
56  /// Panics if `order` is [`Acquire`](Ordering::Acquire) or [`AcqRel`](Ordering::AcqRel).
57  #[inline(always)]
58  pub fn store(&self, val: Option<Duration>, ordering: Ordering) {
59    self.0.store(encode_option_duration(val), ordering)
60  }
61  /// Stores a value into the `AtomicOptionDuration`, returning the old value.
62  ///
63  /// `swap` takes an [`Ordering`] argument which describes the memory ordering
64  /// of this operation.
65  #[inline(always)]
66  pub fn swap(&self, val: Option<Duration>, ordering: Ordering) -> Option<Duration> {
67    decode_option_duration(self.0.swap(encode_option_duration(val), ordering))
68  }
69  /// Stores a value into the `AtomicOptionDuration` if the current value is the same as the
70  /// `current` value.
71  ///
72  /// Unlike [`compare_exchange`], this function is allowed to spuriously fail
73  /// even when the comparison succeeds, which can result in more efficient
74  /// code on some platforms. The return value is a result indicating whether
75  /// the new value was written and containing the previous value.
76  ///
77  /// `compare_exchange` takes two [`Ordering`] arguments to describe the memory
78  /// ordering of this operation. The first describes the required ordering if
79  /// the operation succeeds while the second describes the required ordering
80  /// when the operation fails. The failure ordering can't be [`Release`](Ordering::Release) or
81  /// [`AcqRel`](Ordering::AcqRel) and must be equivalent or weaker than the success ordering.
82  /// success ordering.
83  ///
84  /// [`compare_exchange`]: #method.compare_exchange
85  #[inline(always)]
86  pub fn compare_exchange_weak(
87    &self,
88    current: Option<Duration>,
89    new: Option<Duration>,
90    success: Ordering,
91    failure: Ordering,
92  ) -> Result<Option<Duration>, Option<Duration>> {
93    self
94      .0
95      .compare_exchange_weak(
96        encode_option_duration(current),
97        encode_option_duration(new),
98        success,
99        failure,
100      )
101      .map(decode_option_duration)
102      .map_err(decode_option_duration)
103  }
104  /// Stores a value into the `AtomicOptionDuration` if the current value is the same as the
105  /// `current` value.
106  ///
107  /// The return value is a result indicating whether the new value was
108  /// written and containing the previous value. On success this value is
109  /// guaranteed to be equal to `current`.
110  ///
111  /// [`compare_exchange`] takes two [`Ordering`] arguments to describe the memory
112  /// ordering of this operation. The first describes the required ordering if
113  /// the operation succeeds while the second describes the required ordering
114  /// when the operation fails. The failure ordering can't be [`Release`](Ordering::Release) or
115  /// [`AcqRel`](Ordering::AcqRel) and must be equivalent or weaker than the success ordering.
116  ///
117  /// [`compare_exchange`]: #method.compare_exchange
118  #[inline(always)]
119  pub fn compare_exchange(
120    &self,
121    current: Option<Duration>,
122    new: Option<Duration>,
123    success: Ordering,
124    failure: Ordering,
125  ) -> Result<Option<Duration>, Option<Duration>> {
126    self
127      .0
128      .compare_exchange(
129        encode_option_duration(current),
130        encode_option_duration(new),
131        success,
132        failure,
133      )
134      .map(decode_option_duration)
135      .map_err(decode_option_duration)
136  }
137  /// Fetches the value and applies a function that can choose whether to store
138  /// a new value.
139  ///
140  /// Returns `Ok(previous_value)` when `f` returns `Some(_)`, and
141  /// `Err(previous_value)` when it returns `None`. The outer `Option` is the
142  /// update decision; returning `Some(None)` stores `None`. The closure can run
143  /// more than once when another thread changes the value concurrently, but it
144  /// is applied only once to the value that is stored.
145  ///
146  /// `set_order` describes the ordering of the successful update and
147  /// `fetch_order` describes failed compare-and-exchange loads. They have the
148  /// same requirements as the success and failure orderings of
149  /// [`compare_exchange`](Self::compare_exchange).
150  ///
151  /// # Panics
152  ///
153  /// Panics if the orderings are invalid for
154  /// [`compare_exchange`](Self::compare_exchange).
155  ///
156  /// # Examples
157  ///
158  /// ```rust
159  /// use atomic_time::AtomicOptionDuration;
160  /// use std::{sync::atomic::Ordering, time::Duration};
161  ///
162  /// let x = AtomicOptionDuration::new(Some(Duration::from_secs(7)));
163  /// assert_eq!(x.try_update(Ordering::SeqCst, Ordering::SeqCst, |_| None), Err(Some(Duration::from_secs(7))));
164  /// assert_eq!(x.try_update(Ordering::SeqCst, Ordering::SeqCst, |_| Some(None)), Ok(Some(Duration::from_secs(7))));
165  /// ```
166  #[inline(always)]
167  pub fn try_update<F>(
168    &self,
169    set_order: Ordering,
170    fetch_order: Ordering,
171    mut f: F,
172  ) -> Result<Option<Duration>, Option<Duration>>
173  where
174    F: FnMut(Option<Duration>) -> Option<Option<Duration>>,
175  {
176    self
177      .0
178      .fetch_update(set_order, fetch_order, |d| {
179        f(decode_option_duration(d)).map(encode_option_duration)
180      })
181      .map(decode_option_duration)
182      .map_err(decode_option_duration)
183  }
184  /// Fetches the value, and applies a function to it that returns an optional
185  /// new value. Returns a `Result` of `Ok(previous_value)` if the function returned `Some(_)`, else
186  /// `Err(previous_value)`.
187  ///
188  /// Note: This may call the function multiple times if the value has been changed from other threads in
189  /// the meantime, as long as the function returns `Some(_)`, but the function will have been applied
190  /// only once to the stored value.
191  ///
192  /// `fetch_update` takes two [`Ordering`] arguments to describe the memory ordering of this operation.
193  /// The first describes the required ordering for when the operation finally succeeds while the second
194  /// describes the required ordering for loads. These correspond to the success and failure orderings of
195  /// [`compare_exchange`] respectively.
196  ///
197  /// Using [`Acquire`](Ordering::Acquire) as success ordering makes the store part
198  /// of this operation [`Relaxed`](Ordering::Relaxed), and using [`Release`](Ordering::Release) makes the final successful load
199  /// [`Relaxed`](Ordering::Relaxed). The (failed) load ordering can only be [`SeqCst`](Ordering::SeqCst), [`Acquire`](Ordering::Acquire) or [`Relaxed`](Ordering::Relaxed)
200  /// and must be equivalent to or weaker than the success ordering.
201  ///
202  /// [`compare_exchange`]: #method.compare_exchange
203  ///
204  /// # Examples
205  ///
206  /// ```rust
207  /// use atomic_time::AtomicOptionDuration;
208  /// use std::{time::Duration, sync::atomic::Ordering};
209  ///
210  /// let x = AtomicOptionDuration::new(Some(Duration::from_secs(7)));
211  /// assert_eq!(x.fetch_update(Ordering::SeqCst, Ordering::SeqCst, |_| None), Err(Some(Duration::from_secs(7))));
212  /// assert_eq!(x.fetch_update(Ordering::SeqCst, Ordering::SeqCst, |x| Some(x.map(|val| val + Duration::from_secs(1)))), Ok(Some(Duration::from_secs(7))));
213  /// assert_eq!(x.fetch_update(Ordering::SeqCst, Ordering::SeqCst, |x| Some(x.map(|val| val + Duration::from_secs(1)))), Ok(Some(Duration::from_secs(8))));
214  /// assert_eq!(x.load(Ordering::SeqCst), Some(Duration::from_secs(9)));
215  /// ```
216  #[inline(always)]
217  pub fn fetch_update<F>(
218    &self,
219    set_order: Ordering,
220    fetch_order: Ordering,
221    f: F,
222  ) -> Result<Option<Duration>, Option<Duration>>
223  where
224    F: FnMut(Option<Duration>) -> Option<Option<Duration>>,
225  {
226    self.try_update(set_order, fetch_order, f)
227  }
228
229  /// Fetches the value, applies a function to produce a new value, stores it,
230  /// and returns the previous value.
231  ///
232  /// The closure can run more than once when another thread changes the value
233  /// concurrently, but it is applied only once to the value that is stored.
234  /// `set_order` and `fetch_order` have the same requirements as the success
235  /// and failure orderings of [`compare_exchange`](Self::compare_exchange).
236  ///
237  /// # Panics
238  ///
239  /// Panics if the orderings are invalid for
240  /// [`compare_exchange`](Self::compare_exchange).
241  ///
242  /// # Examples
243  ///
244  /// ```rust
245  /// use atomic_time::AtomicOptionDuration;
246  /// use std::{sync::atomic::Ordering, time::Duration};
247  ///
248  /// let x = AtomicOptionDuration::new(Some(Duration::from_secs(7)));
249  /// assert_eq!(x.update(Ordering::SeqCst, Ordering::SeqCst, |_| None), Some(Duration::from_secs(7)));
250  /// assert_eq!(x.load(Ordering::SeqCst), None);
251  /// ```
252  #[inline(always)]
253  pub fn update<F>(&self, set_order: Ordering, fetch_order: Ordering, mut f: F) -> Option<Duration>
254  where
255    F: FnMut(Option<Duration>) -> Option<Duration>,
256  {
257    let mut current = self.0.load(fetch_order);
258    loop {
259      let new = encode_option_duration(f(decode_option_duration(current)));
260      match self
261        .0
262        .compare_exchange_weak(current, new, set_order, fetch_order)
263      {
264        Ok(previous) => return decode_option_duration(previous),
265        Err(actual) => current = actual,
266      }
267    }
268  }
269
270  /// Atomically stores the smaller of the current value and `val`, returning
271  /// the previous value.
272  ///
273  /// The encoded ordering is `None < Some(duration)`, and `Some` values use
274  /// their natural [`Duration`] ordering. `order` describes the memory ordering
275  /// of the read-modify-write operation.
276  ///
277  /// # Examples
278  ///
279  /// ```rust
280  /// use atomic_time::AtomicOptionDuration;
281  /// use std::{sync::atomic::Ordering, time::Duration};
282  ///
283  /// let x = AtomicOptionDuration::new(Some(Duration::from_secs(7)));
284  /// assert_eq!(x.fetch_min(None, Ordering::SeqCst), Some(Duration::from_secs(7)));
285  /// assert_eq!(x.load(Ordering::SeqCst), None);
286  /// ```
287  #[inline(always)]
288  pub fn fetch_min(&self, val: Option<Duration>, order: Ordering) -> Option<Duration> {
289    decode_option_duration(self.0.fetch_min(encode_option_duration(val), order))
290  }
291
292  /// Atomically stores the larger of the current value and `val`, returning
293  /// the previous value.
294  ///
295  /// The encoded ordering is `None < Some(duration)`, and `Some` values use
296  /// their natural [`Duration`] ordering. `order` describes the memory ordering
297  /// of the read-modify-write operation.
298  ///
299  /// # Examples
300  ///
301  /// ```rust
302  /// use atomic_time::AtomicOptionDuration;
303  /// use std::{sync::atomic::Ordering, time::Duration};
304  ///
305  /// let x = AtomicOptionDuration::new(None);
306  /// assert_eq!(x.fetch_max(Some(Duration::from_secs(7)), Ordering::SeqCst), None);
307  /// assert_eq!(x.load(Ordering::SeqCst), Some(Duration::from_secs(7)));
308  /// ```
309  #[inline(always)]
310  pub fn fetch_max(&self, val: Option<Duration>, order: Ordering) -> Option<Duration> {
311    decode_option_duration(self.0.fetch_max(encode_option_duration(val), order))
312  }
313
314  /// Adds `val` to a contained duration with saturation, returning the previous
315  /// value.
316  ///
317  /// `Some(duration)` is replaced with `Some(duration.saturating_add(val))`.
318  /// `None` remains `None`; it is never treated as [`Duration::ZERO`].
319  ///
320  /// # Examples
321  ///
322  /// ```rust
323  /// use atomic_time::AtomicOptionDuration;
324  /// use std::{sync::atomic::Ordering, time::Duration};
325  ///
326  /// let x = AtomicOptionDuration::none();
327  /// assert_eq!(x.fetch_saturating_add(Duration::from_nanos(1), Ordering::SeqCst), None);
328  /// assert_eq!(x.load(Ordering::SeqCst), None);
329  /// ```
330  #[inline(always)]
331  pub fn fetch_saturating_add(&self, val: Duration, order: Ordering) -> Option<Duration> {
332    self.update(order, Ordering::Relaxed, |old| {
333      old.map(|duration| duration.saturating_add(val))
334    })
335  }
336
337  /// Subtracts `val` from a contained duration with saturation, returning the
338  /// previous value.
339  ///
340  /// `Some(duration)` is replaced with `Some(duration.saturating_sub(val))`.
341  /// `None` remains `None`; it is never treated as [`Duration::ZERO`].
342  ///
343  /// # Examples
344  ///
345  /// ```rust
346  /// use atomic_time::AtomicOptionDuration;
347  /// use std::{sync::atomic::Ordering, time::Duration};
348  ///
349  /// let x = AtomicOptionDuration::none();
350  /// assert_eq!(x.fetch_saturating_sub(Duration::from_nanos(1), Ordering::SeqCst), None);
351  /// assert_eq!(x.load(Ordering::SeqCst), None);
352  /// ```
353  #[inline(always)]
354  pub fn fetch_saturating_sub(&self, val: Duration, order: Ordering) -> Option<Duration> {
355    self.update(order, Ordering::Relaxed, |old| {
356      old.map(|duration| duration.saturating_sub(val))
357    })
358  }
359  /// Consumes the atomic and returns the contained value.
360  ///
361  /// This is safe because passing `self` by value guarantees that no other threads are
362  /// concurrently accessing the atomic data.
363  #[inline(always)]
364  pub fn into_inner(self) -> Option<Duration> {
365    decode_option_duration(self.0.into_inner())
366  }
367
368  /// Returns `true` if operations on values of this type are lock-free.
369  /// If the compiler or the platform doesn't support the necessary
370  /// atomic instructions, global locks for every potentially
371  /// concurrent atomic operation will be used.
372  ///
373  /// # Examples
374  /// ```
375  /// use atomic_time::AtomicOptionDuration;
376  ///
377  /// let is_lock_free = AtomicOptionDuration::is_lock_free();
378  /// ```
379  #[inline(always)]
380  pub fn is_lock_free() -> bool {
381    AtomicU128::is_lock_free()
382  }
383
384  /// Returns whether operations on values of this type are always lock-free.
385  ///
386  /// A `false` result does not preclude lock-free operations selected through
387  /// runtime CPU feature detection; use [`is_lock_free`](Self::is_lock_free)
388  /// to query the current target at runtime.
389  ///
390  /// # Examples
391  ///
392  /// ```rust
393  /// use atomic_time::AtomicOptionDuration;
394  ///
395  /// const ALWAYS_LOCK_FREE: bool = AtomicOptionDuration::is_always_lock_free();
396  /// let _ = ALWAYS_LOCK_FREE;
397  /// ```
398  #[inline(always)]
399  pub const fn is_always_lock_free() -> bool {
400    AtomicU128::is_always_lock_free()
401  }
402}
403
404/// Encode an [`Option<Duration>`] into an [`u128`].
405#[inline(always)]
406pub const fn encode_option_duration(option_duration: Option<Duration>) -> u128 {
407  match option_duration {
408    Some(duration) => {
409      let seconds = duration.as_secs() as u128;
410      let nanos = duration.subsec_nanos() as u128;
411      (1 << 127) | (seconds << 32) | nanos
412    }
413    None => 0,
414  }
415}
416
417/// Decode an [`Option<Duration>`] from an encoded [`u128`].
418///
419/// Accepts non-canonical input without panicking. The `Some(_)`
420/// encoding stores 64 bits of seconds in bits 32..=95 and up to 30
421/// bits of nanoseconds in bits 0..=31 (bit 127 is the Some/None
422/// discriminant, bits 96..=126 are unused). If the decoded nanosecond
423/// count is 10⁹ or more — which `encode_option_duration` never
424/// produces, but which can appear when the encoded value comes from
425/// corrupted storage or untrusted input — the extra whole seconds are
426/// folded into the seconds field; if that push past `u64::MAX`, the
427/// result saturates at [`Duration::MAX`].
428///
429/// This means `decode_option_duration(u128::MAX)` yields
430/// `Some(Duration::MAX)` rather than panicking as the previous
431/// implementation did.
432#[inline(always)]
433pub const fn decode_option_duration(encoded: u128) -> Option<Duration> {
434  if encoded >> 127 == 0 {
435    None
436  } else {
437    let seconds = ((encoded << 1) >> 33) as u64;
438    let raw_nanos = (encoded & 0xFFFFFFFF) as u32;
439    let extra_secs = (raw_nanos / 1_000_000_000) as u64;
440    let nanos = raw_nanos % 1_000_000_000;
441    Some(match seconds.checked_add(extra_secs) {
442      Some(secs) => Duration::new(secs, nanos),
443      None => Duration::new(u64::MAX, 999_999_999),
444    })
445  }
446}
447
448#[cfg(feature = "serde")]
449const _: () = {
450  use serde::{Deserialize, Serialize};
451
452  impl Serialize for AtomicOptionDuration {
453    fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
454      self.load(Ordering::SeqCst).serialize(serializer)
455    }
456  }
457
458  impl<'de> Deserialize<'de> for AtomicOptionDuration {
459    fn deserialize<D: serde::Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
460      Ok(Self::new(Option::<Duration>::deserialize(deserializer)?))
461    }
462  }
463};
464
465#[cfg(test)]
466mod tests {
467  use super::*;
468
469  #[test]
470  fn test_new_atomic_option_duration() {
471    let duration = Duration::from_secs(5);
472    let atomic_duration = AtomicOptionDuration::new(Some(duration));
473    assert_eq!(atomic_duration.load(Ordering::SeqCst), Some(duration));
474  }
475
476  #[test]
477  fn test_atomic_option_duration_load() {
478    let duration = Duration::from_secs(10);
479    let atomic_duration = AtomicOptionDuration::new(Some(duration));
480    assert_eq!(atomic_duration.load(Ordering::SeqCst), Some(duration));
481  }
482
483  #[test]
484  fn test_atomic_option_duration_store() {
485    let initial_duration = Duration::from_secs(3);
486    let new_duration = Duration::from_secs(7);
487    let atomic_duration = AtomicOptionDuration::new(Some(initial_duration));
488    atomic_duration.store(Some(new_duration), Ordering::SeqCst);
489    assert_eq!(atomic_duration.load(Ordering::SeqCst), Some(new_duration));
490  }
491
492  #[test]
493  fn test_atomic_option_duration_store_none() {
494    let initial_duration = Duration::from_secs(3);
495    let atomic_duration = AtomicOptionDuration::new(Some(initial_duration));
496    atomic_duration.store(None, Ordering::SeqCst);
497    assert_eq!(atomic_duration.load(Ordering::SeqCst), None);
498  }
499
500  #[test]
501  fn test_atomic_option_duration_swap() {
502    let initial_duration = Duration::from_secs(2);
503    let new_duration = Duration::from_secs(8);
504    let atomic_duration = AtomicOptionDuration::new(Some(initial_duration));
505    let prev_duration = atomic_duration.swap(Some(new_duration), Ordering::SeqCst);
506    assert_eq!(prev_duration, Some(initial_duration));
507    assert_eq!(atomic_duration.load(Ordering::SeqCst), Some(new_duration));
508  }
509
510  #[test]
511  fn test_atomic_option_duration_compare_exchange_weak() {
512    let initial_duration = Duration::from_secs(4);
513    let atomic_duration = AtomicOptionDuration::new(Some(initial_duration));
514
515    // Successful exchange
516    let mut result;
517    loop {
518      result = atomic_duration.compare_exchange_weak(
519        Some(initial_duration),
520        Some(Duration::from_secs(6)),
521        Ordering::SeqCst,
522        Ordering::SeqCst,
523      );
524
525      if result.is_ok() {
526        break;
527      }
528    }
529
530    assert!(result.is_ok());
531    assert_eq!(result.unwrap(), Some(initial_duration));
532    assert_eq!(
533      atomic_duration.load(Ordering::SeqCst),
534      Some(Duration::from_secs(6))
535    );
536
537    // Failed exchange
538    let result = atomic_duration.compare_exchange_weak(
539      Some(initial_duration),
540      Some(Duration::from_secs(7)),
541      Ordering::SeqCst,
542      Ordering::SeqCst,
543    );
544    assert!(result.is_err());
545    assert_eq!(result.unwrap_err(), Some(Duration::from_secs(6)));
546  }
547
548  #[test]
549  fn test_atomic_option_duration_compare_exchange() {
550    let initial_duration = Duration::from_secs(1);
551    let atomic_duration = AtomicOptionDuration::new(Some(initial_duration));
552
553    // Successful exchange
554    let result = atomic_duration.compare_exchange(
555      Some(initial_duration),
556      Some(Duration::from_secs(5)),
557      Ordering::SeqCst,
558      Ordering::SeqCst,
559    );
560    assert!(result.is_ok());
561    assert_eq!(result.unwrap(), Some(initial_duration));
562    assert_eq!(
563      atomic_duration.load(Ordering::SeqCst),
564      Some(Duration::from_secs(5))
565    );
566
567    // Failed exchange
568    let result = atomic_duration.compare_exchange(
569      Some(initial_duration),
570      Some(Duration::from_secs(6)),
571      Ordering::SeqCst,
572      Ordering::SeqCst,
573    );
574    assert!(result.is_err());
575    assert_eq!(result.unwrap_err(), Some(Duration::from_secs(5)));
576  }
577
578  #[test]
579  fn test_atomic_option_duration_with_none_initially() {
580    let atomic_duration = AtomicOptionDuration::new(None);
581    assert_eq!(atomic_duration.load(Ordering::SeqCst), None);
582  }
583
584  #[test]
585  fn test_atomic_option_duration_store_none_and_then_value() {
586    let atomic_duration = AtomicOptionDuration::new(None);
587    atomic_duration.store(Some(Duration::from_secs(5)), Ordering::SeqCst);
588    assert_eq!(
589      atomic_duration.load(Ordering::SeqCst),
590      Some(Duration::from_secs(5))
591    );
592  }
593
594  #[test]
595  fn test_atomic_option_duration_swap_with_none() {
596    let initial_duration = Duration::from_secs(2);
597    let atomic_duration = AtomicOptionDuration::new(Some(initial_duration));
598    let prev_duration = atomic_duration.swap(None, Ordering::SeqCst);
599    assert_eq!(prev_duration, Some(initial_duration));
600    assert_eq!(atomic_duration.load(Ordering::SeqCst), None);
601  }
602
603  #[test]
604  fn test_atomic_option_duration_compare_exchange_weak_with_none() {
605    let initial_duration = Duration::from_secs(4);
606    let atomic_duration = AtomicOptionDuration::new(Some(initial_duration));
607
608    // Change to None
609    let mut result;
610
611    loop {
612      result = atomic_duration.compare_exchange_weak(
613        Some(initial_duration),
614        None,
615        Ordering::SeqCst,
616        Ordering::SeqCst,
617      );
618
619      if result.is_ok() {
620        break;
621      }
622    }
623
624    assert_eq!(atomic_duration.load(Ordering::SeqCst), None);
625
626    // Change back to Some(Duration)
627    let new_duration = Duration::from_secs(6);
628    let mut result;
629
630    loop {
631      result = atomic_duration.compare_exchange_weak(
632        None,
633        Some(new_duration),
634        Ordering::SeqCst,
635        Ordering::SeqCst,
636      );
637      if result.is_ok() {
638        break;
639      }
640    }
641
642    assert_eq!(atomic_duration.load(Ordering::SeqCst), Some(new_duration));
643  }
644
645  #[test]
646  fn test_atomic_option_duration_compare_exchange_with_none() {
647    let initial_duration = Duration::from_secs(1);
648    let atomic_duration = AtomicOptionDuration::new(Some(initial_duration));
649
650    // Change to None
651    let result = atomic_duration.compare_exchange(
652      Some(initial_duration),
653      None,
654      Ordering::SeqCst,
655      Ordering::SeqCst,
656    );
657    assert!(result.is_ok());
658    assert_eq!(atomic_duration.load(Ordering::SeqCst), None);
659
660    // Change back to Some(Duration)
661    let new_duration = Duration::from_secs(5);
662    let result = atomic_duration.compare_exchange(
663      None,
664      Some(new_duration),
665      Ordering::SeqCst,
666      Ordering::SeqCst,
667    );
668    assert!(result.is_ok());
669    assert_eq!(atomic_duration.load(Ordering::SeqCst), Some(new_duration));
670  }
671
672  #[test]
673  #[cfg(feature = "std")]
674  fn test_atomic_option_duration_thread_safety() {
675    use std::sync::Arc;
676    use std::thread;
677
678    let atomic_duration = Arc::new(AtomicOptionDuration::new(Some(Duration::from_secs(0))));
679    let mut handles = vec![];
680
681    // Spawn multiple threads to increment the duration
682    for _ in 0..10 {
683      let atomic_clone = Arc::clone(&atomic_duration);
684      let handle = thread::spawn(move || {
685        for _ in 0..100 {
686          loop {
687            let current = atomic_clone.load(Ordering::SeqCst);
688            let new_duration = current
689              .map(|d| d + Duration::from_millis(1))
690              .or(Some(Duration::from_millis(1)));
691            match atomic_clone.compare_exchange_weak(
692              current,
693              new_duration,
694              Ordering::SeqCst,
695              Ordering::SeqCst,
696            ) {
697              Ok(_) => break,     // Successfully updated
698              Err(_) => continue, // Spurious failure, retry
699            }
700          }
701        }
702      });
703      handles.push(handle);
704    }
705
706    // Wait for all threads to complete
707    for handle in handles {
708      handle.join().unwrap();
709    }
710
711    // Verify the final value
712    let expected_duration = Some(Duration::from_millis(10 * 100));
713    assert_eq!(atomic_duration.load(Ordering::SeqCst), expected_duration);
714  }
715
716  #[cfg(feature = "std")]
717  #[test]
718  fn test_atomic_option_duration_debug() {
719    let atomic_duration = AtomicOptionDuration::new(Some(Duration::from_secs(1)));
720    let debug_str = format!("{:?}", atomic_duration);
721    assert!(debug_str.contains("AtomicOptionDuration"));
722  }
723
724  #[cfg(feature = "std")]
725  #[test]
726  fn test_atomic_option_duration_debug_none() {
727    let atomic_duration = AtomicOptionDuration::none();
728    let debug_str = format!("{:?}", atomic_duration);
729    assert!(debug_str.contains("AtomicOptionDuration"));
730  }
731
732  #[test]
733  fn test_atomic_option_duration_default() {
734    let atomic_duration = AtomicOptionDuration::default();
735    assert_eq!(atomic_duration.load(Ordering::SeqCst), None);
736  }
737
738  #[test]
739  fn test_atomic_option_duration_from() {
740    let duration = Some(Duration::from_secs(42));
741    let atomic_duration = AtomicOptionDuration::from(duration);
742    assert_eq!(atomic_duration.load(Ordering::SeqCst), duration);
743  }
744
745  #[test]
746  fn test_atomic_option_duration_from_none() {
747    let atomic_duration = AtomicOptionDuration::from(None);
748    assert_eq!(atomic_duration.load(Ordering::SeqCst), None);
749  }
750
751  #[test]
752  fn test_atomic_option_duration_into_inner() {
753    let duration = Some(Duration::from_secs(3));
754    let atomic_duration = AtomicOptionDuration::new(duration);
755    assert_eq!(atomic_duration.into_inner(), duration);
756  }
757
758  #[test]
759  fn test_atomic_option_duration_into_inner_none() {
760    let atomic_duration = AtomicOptionDuration::none();
761    assert_eq!(atomic_duration.into_inner(), None);
762  }
763
764  #[test]
765  fn test_atomic_option_duration_fetch_update() {
766    let initial = Some(Duration::from_secs(4));
767    let atomic_duration = AtomicOptionDuration::new(initial);
768
769    let result = atomic_duration.fetch_update(Ordering::SeqCst, Ordering::SeqCst, |d| {
770      Some(d.map(|val| val + Duration::from_secs(2)))
771    });
772    assert_eq!(result, Ok(initial));
773    assert_eq!(
774      atomic_duration.load(Ordering::SeqCst),
775      Some(Duration::from_secs(6))
776    );
777
778    // fetch_update returning None should fail
779    let result = atomic_duration.fetch_update(Ordering::SeqCst, Ordering::SeqCst, |_| None);
780    assert!(result.is_err());
781  }
782
783  #[test]
784  fn test_atomic_option_duration_try_update() {
785    let initial = Some(Duration::from_secs(4));
786    let atomic_duration = AtomicOptionDuration::new(initial);
787
788    assert_eq!(
789      atomic_duration.try_update(Ordering::SeqCst, Ordering::SeqCst, |_| None),
790      Err(initial)
791    );
792    assert_eq!(
793      atomic_duration.try_update(Ordering::SeqCst, Ordering::SeqCst, |_| Some(None)),
794      Ok(initial)
795    );
796    assert_eq!(atomic_duration.load(Ordering::SeqCst), None);
797  }
798
799  #[test]
800  fn test_atomic_option_duration_update_returns_previous_value() {
801    let initial = Some(Duration::from_secs(4));
802    let atomic_duration = AtomicOptionDuration::new(initial);
803
804    assert_eq!(
805      atomic_duration.update(Ordering::SeqCst, Ordering::SeqCst, |_| None),
806      initial
807    );
808    assert_eq!(atomic_duration.load(Ordering::SeqCst), None);
809  }
810
811  #[test]
812  fn test_atomic_option_duration_fetch_min_and_max() {
813    let low = Some(Duration::from_secs(3));
814    let middle = Some(Duration::from_secs(5));
815    let high = Some(Duration::from_secs(7));
816    let atomic_duration = AtomicOptionDuration::new(middle);
817
818    assert_eq!(atomic_duration.fetch_min(high, Ordering::SeqCst), middle);
819    assert_eq!(atomic_duration.load(Ordering::SeqCst), middle);
820    assert_eq!(atomic_duration.fetch_min(middle, Ordering::SeqCst), middle);
821    assert_eq!(atomic_duration.load(Ordering::SeqCst), middle);
822    assert_eq!(atomic_duration.fetch_min(low, Ordering::SeqCst), middle);
823    assert_eq!(atomic_duration.load(Ordering::SeqCst), low);
824
825    atomic_duration.store(middle, Ordering::SeqCst);
826    assert_eq!(atomic_duration.fetch_max(low, Ordering::SeqCst), middle);
827    assert_eq!(atomic_duration.load(Ordering::SeqCst), middle);
828    assert_eq!(atomic_duration.fetch_max(middle, Ordering::SeqCst), middle);
829    assert_eq!(atomic_duration.load(Ordering::SeqCst), middle);
830    assert_eq!(atomic_duration.fetch_max(high, Ordering::SeqCst), middle);
831    assert_eq!(atomic_duration.load(Ordering::SeqCst), high);
832
833    atomic_duration.store(None, Ordering::SeqCst);
834    assert_eq!(atomic_duration.fetch_min(low, Ordering::SeqCst), None);
835    assert_eq!(atomic_duration.load(Ordering::SeqCst), None);
836    assert_eq!(atomic_duration.fetch_max(low, Ordering::SeqCst), None);
837    assert_eq!(atomic_duration.load(Ordering::SeqCst), low);
838  }
839
840  #[test]
841  fn test_atomic_option_duration_fetch_saturating_arithmetic() {
842    let none = AtomicOptionDuration::none();
843    assert_eq!(
844      none.fetch_saturating_add(Duration::from_nanos(1), Ordering::SeqCst),
845      None
846    );
847    assert_eq!(none.load(Ordering::SeqCst), None);
848    assert_eq!(
849      none.fetch_saturating_sub(Duration::from_nanos(1), Ordering::SeqCst),
850      None
851    );
852    assert_eq!(none.load(Ordering::SeqCst), None);
853
854    let atomic_duration = AtomicOptionDuration::new(Some(Duration::new(0, 999_999_999)));
855    assert_eq!(
856      atomic_duration.fetch_saturating_add(Duration::from_nanos(1), Ordering::SeqCst),
857      Some(Duration::new(0, 999_999_999))
858    );
859    assert_eq!(
860      atomic_duration.load(Ordering::SeqCst),
861      Some(Duration::from_secs(1))
862    );
863
864    atomic_duration.store(Some(Duration::MAX), Ordering::SeqCst);
865    assert_eq!(
866      atomic_duration.fetch_saturating_add(Duration::from_nanos(1), Ordering::SeqCst),
867      Some(Duration::MAX)
868    );
869    assert_eq!(atomic_duration.load(Ordering::SeqCst), Some(Duration::MAX));
870
871    atomic_duration.store(Some(Duration::ZERO), Ordering::SeqCst);
872    assert_eq!(
873      atomic_duration.fetch_saturating_sub(Duration::from_nanos(1), Ordering::SeqCst),
874      Some(Duration::ZERO)
875    );
876    assert_eq!(atomic_duration.load(Ordering::SeqCst), Some(Duration::ZERO));
877  }
878
879  #[test]
880  fn test_atomic_option_duration_always_lock_free_implies_lock_free() {
881    assert!(!AtomicOptionDuration::is_always_lock_free() || AtomicOptionDuration::is_lock_free());
882  }
883
884  #[test]
885  #[cfg(feature = "std")]
886  fn test_atomic_option_duration_saturating_add_is_exact_under_contention() {
887    use std::sync::Arc;
888    use std::thread;
889
890    let atomic_duration = Arc::new(AtomicOptionDuration::new(Some(Duration::ZERO)));
891    let mut handles = vec![];
892
893    for _ in 0..4 {
894      let atomic_clone = Arc::clone(&atomic_duration);
895      handles.push(thread::spawn(move || {
896        for _ in 0..100 {
897          atomic_clone.fetch_saturating_add(Duration::from_nanos(1), Ordering::SeqCst);
898        }
899      }));
900    }
901
902    for handle in handles {
903      handle.join().unwrap();
904    }
905
906    assert_eq!(
907      atomic_duration.load(Ordering::SeqCst),
908      Some(Duration::from_nanos(400))
909    );
910  }
911
912  #[cfg(feature = "serde")]
913  #[test]
914  fn test_atomic_option_duration_serde() {
915    for duration in [Some(Duration::from_secs(5)), None] {
916      let atomic = AtomicOptionDuration::new(duration);
917      let serialized = serde_json::to_string(&atomic).unwrap();
918      let deserialized: AtomicOptionDuration = serde_json::from_str(&serialized).unwrap();
919      assert_eq!(deserialized.load(Ordering::SeqCst), duration);
920    }
921  }
922
923  #[test]
924  fn decode_option_duration_roundtrip() {
925    let cases: [Option<Duration>; 4] = [
926      None,
927      Some(Duration::ZERO),
928      Some(Duration::from_secs(1)),
929      Some(Duration::new(123_456_789, 999_999_999)),
930    ];
931    for d in cases {
932      assert_eq!(decode_option_duration(encode_option_duration(d)), d);
933    }
934  }
935
936  #[test]
937  fn decode_option_duration_saturates_on_non_canonical_input() {
938    // u128::MAX has bit 127 set (= Some), nanos = u32::MAX (> 1e9),
939    // and the extracted seconds = u64::MAX. The old implementation
940    // panicked; the new one saturates.
941    let max = decode_option_duration(u128::MAX);
942    assert_eq!(max, Some(Duration::new(u64::MAX, 999_999_999)));
943
944    // Zero is the None sentinel — verify it stays None even when all
945    // lower bits are clear.
946    assert_eq!(decode_option_duration(0), None);
947
948    // Bit 127 alone = Some with seconds=0, nanos=0.
949    assert_eq!(decode_option_duration(1u128 << 127), Some(Duration::ZERO));
950  }
951}