Skip to main content

atomic_time/
option_system_time.rs

1use core::sync::atomic::Ordering;
2use std::time::SystemTime;
3
4use crate::AtomicOptionDuration;
5
6/// An atomic version of [`Option<std::time::SystemTime>`].
7///
8/// `Some` values must be at or after [`SystemTime::UNIX_EPOCH`].
9#[repr(transparent)]
10pub struct AtomicOptionSystemTime(AtomicOptionDuration);
11
12impl core::fmt::Debug for AtomicOptionSystemTime {
13  fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
14    f.debug_tuple("AtomicOptionSystemTime")
15      .field(&self.load(Ordering::SeqCst))
16      .finish()
17  }
18}
19impl Default for AtomicOptionSystemTime {
20  /// Equivalent to `Option::<SystemTime>::None`.
21  #[inline(always)]
22  fn default() -> Self {
23    Self::none()
24  }
25}
26impl From<Option<SystemTime>> for AtomicOptionSystemTime {
27  /// # Panics
28  ///
29  /// Panics if the given `SystemTime` value is earlier than [`UNIX_EPOCH`](SystemTime::UNIX_EPOCH).
30  #[inline(always)]
31  fn from(system_time: Option<SystemTime>) -> Self {
32    Self::new(system_time)
33  }
34}
35
36impl AtomicOptionSystemTime {
37  /// Equivalent to atomic version `Option::<SystemTime>::None`.
38  ///
39  /// # Examples
40  ///
41  /// ```rust
42  /// use atomic_time::AtomicOptionSystemTime;
43  ///
44  /// let none = AtomicOptionSystemTime::none();
45  /// assert_eq!(none.load(std::sync::atomic::Ordering::SeqCst), None);
46  /// ```
47  #[inline(always)]
48  pub const fn none() -> Self {
49    Self(AtomicOptionDuration::new(None))
50  }
51
52  /// Returns the system time corresponding to "now".
53  ///
54  /// # Examples
55  /// ```
56  /// use atomic_time::AtomicOptionSystemTime;
57  ///
58  /// let sys_time = AtomicOptionSystemTime::now();
59  /// ```
60  #[inline(always)]
61  pub fn now() -> Self {
62    Self::new(Some(SystemTime::now()))
63  }
64
65  /// Creates a new `AtomicOptionSystemTime` with the given `SystemTime` value.
66  ///
67  /// # Panics
68  ///
69  /// If the given `SystemTime` value is earlier than the [`UNIX_EPOCH`](SystemTime::UNIX_EPOCH).
70  #[inline(always)]
71  pub fn new(system_time: Option<SystemTime>) -> Self {
72    Self(AtomicOptionDuration::new(
73      system_time.map(|d| d.duration_since(SystemTime::UNIX_EPOCH).unwrap()),
74    ))
75  }
76
77  /// Loads a value from the atomic system time.
78  #[inline(always)]
79  pub fn load(&self, order: Ordering) -> Option<SystemTime> {
80    self.0.load(order).map(|val| SystemTime::UNIX_EPOCH + val)
81  }
82
83  /// Stores a value into the atomic system time.
84  ///
85  /// # Panics
86  ///
87  /// If the given `SystemTime` value is earlier than the [`UNIX_EPOCH`](SystemTime::UNIX_EPOCH).
88  #[inline(always)]
89  pub fn store(&self, system_time: Option<SystemTime>, order: Ordering) {
90    self.0.store(
91      system_time.map(|val| val.duration_since(SystemTime::UNIX_EPOCH).unwrap()),
92      order,
93    )
94  }
95
96  /// Stores a value into the atomic system time, returning the previous value.
97  ///
98  /// # Panics
99  ///
100  /// If the given `SystemTime` value is earlier than the [`UNIX_EPOCH`](SystemTime::UNIX_EPOCH).
101  #[inline(always)]
102  pub fn swap(&self, system_time: Option<SystemTime>, order: Ordering) -> Option<SystemTime> {
103    self
104      .0
105      .swap(
106        system_time.map(|val| val.duration_since(SystemTime::UNIX_EPOCH).unwrap()),
107        order,
108      )
109      .map(|val| SystemTime::UNIX_EPOCH + val)
110  }
111
112  /// Stores a value into the atomic system time if the current value is the same as the `current`
113  /// value.
114  ///
115  /// # Panics
116  ///
117  /// If the given `SystemTime` value is earlier than the [`UNIX_EPOCH`](SystemTime::UNIX_EPOCH).
118  #[inline(always)]
119  pub fn compare_exchange(
120    &self,
121    current: Option<SystemTime>,
122    new: Option<SystemTime>,
123    success: Ordering,
124    failure: Ordering,
125  ) -> Result<Option<SystemTime>, Option<SystemTime>> {
126    match self.0.compare_exchange(
127      current.map(|d| d.duration_since(SystemTime::UNIX_EPOCH).unwrap()),
128      new.map(|d| d.duration_since(SystemTime::UNIX_EPOCH).unwrap()),
129      success,
130      failure,
131    ) {
132      Ok(duration) => Ok(duration.map(|d| SystemTime::UNIX_EPOCH + d)),
133      Err(duration) => Err(duration.map(|d| SystemTime::UNIX_EPOCH + d)),
134    }
135  }
136
137  /// Stores a value into the atomic system time if the current value is the same as the `current`
138  /// value.
139  ///
140  /// # Panics
141  ///
142  /// If the given `SystemTime` value is earlier than the [`UNIX_EPOCH`](SystemTime::UNIX_EPOCH).
143  #[inline(always)]
144  pub fn compare_exchange_weak(
145    &self,
146    current: Option<SystemTime>,
147    new: Option<SystemTime>,
148    success: Ordering,
149    failure: Ordering,
150  ) -> Result<Option<SystemTime>, Option<SystemTime>> {
151    match self.0.compare_exchange_weak(
152      current.map(|d| d.duration_since(SystemTime::UNIX_EPOCH).unwrap()),
153      new.map(|d| d.duration_since(SystemTime::UNIX_EPOCH).unwrap()),
154      success,
155      failure,
156    ) {
157      Ok(duration) => Ok(duration.map(|d| SystemTime::UNIX_EPOCH + d)),
158      Err(duration) => Err(duration.map(|d| SystemTime::UNIX_EPOCH + d)),
159    }
160  }
161
162  /// Fetches the value and applies a function that can choose whether to store
163  /// a new value.
164  ///
165  /// Returns `Ok(previous_value)` when `f` returns `Some(_)`, and
166  /// `Err(previous_value)` when it returns `None`. The outer `Option` is the
167  /// update decision; returning `Some(None)` stores `None`. The closure can run
168  /// more than once when another thread changes the value concurrently, but it
169  /// is applied only once to the value that is stored.
170  ///
171  /// `set_order` describes the ordering of the successful update and
172  /// `fetch_order` describes failed compare-and-exchange loads. They have the
173  /// same requirements as the success and failure orderings of
174  /// [`compare_exchange`](Self::compare_exchange).
175  ///
176  /// # Panics
177  ///
178  /// Panics if the orderings are invalid for
179  /// [`compare_exchange`](Self::compare_exchange), or if a `Some` value passed
180  /// to or returned from the function is earlier than
181  /// [`UNIX_EPOCH`](SystemTime::UNIX_EPOCH).
182  ///
183  /// # Examples
184  ///
185  /// ```rust
186  /// use atomic_time::AtomicOptionSystemTime;
187  /// use std::{sync::atomic::Ordering, time::{Duration, SystemTime}};
188  ///
189  /// let start = SystemTime::UNIX_EPOCH + Duration::from_secs(7);
190  /// let x = AtomicOptionSystemTime::new(Some(start));
191  /// assert_eq!(x.try_update(Ordering::SeqCst, Ordering::SeqCst, |_| None), Err(Some(start)));
192  /// assert_eq!(x.try_update(Ordering::SeqCst, Ordering::SeqCst, |old| Some(old.map(|time| time + Duration::from_secs(1)))), Ok(Some(start)));
193  /// ```
194  #[inline(always)]
195  pub fn try_update<F>(
196    &self,
197    set_order: Ordering,
198    fetch_order: Ordering,
199    mut f: F,
200  ) -> Result<Option<SystemTime>, Option<SystemTime>>
201  where
202    F: FnMut(Option<SystemTime>) -> Option<Option<SystemTime>>,
203  {
204    self
205      .0
206      .try_update(set_order, fetch_order, |duration| {
207        f(duration.map(|d| SystemTime::UNIX_EPOCH + d))
208          .map(|system_time| system_time.map(|d| d.duration_since(SystemTime::UNIX_EPOCH).unwrap()))
209      })
210      .map(|duration| duration.map(|d| SystemTime::UNIX_EPOCH + d))
211      .map_err(|duration| duration.map(|d| SystemTime::UNIX_EPOCH + d))
212  }
213
214  /// Fetches the value, and applies a function to it that returns an optional
215  /// new value. Returns a `Result` of `Ok(previous_value)` if the function returned `Some(_)`, else
216  /// `Err(previous_value)`.
217  ///
218  /// Note: This may call the function multiple times if the value has been changed from other threads in
219  /// the meantime, as long as the function returns `Some(_)`, but the function will have been applied
220  /// only once to the stored value.
221  ///
222  /// `fetch_update` takes two [`Ordering`] arguments to describe the memory ordering of this operation.
223  /// The first describes the required ordering for when the operation finally succeeds while the second
224  /// describes the required ordering for loads. These correspond to the success and failure orderings of
225  /// [`compare_exchange`] respectively.
226  ///
227  /// Using [`Acquire`](Ordering::Acquire) as success ordering makes the store part
228  /// of this operation [`Relaxed`](Ordering::Relaxed), and using [`Release`](Ordering::Release) makes the final successful load
229  /// [`Relaxed`](Ordering::Relaxed). The (failed) load ordering can only be [`SeqCst`](Ordering::SeqCst), [`Acquire`](Ordering::Acquire) or [`Relaxed`](Ordering::Relaxed)
230  /// and must be equivalent to or weaker than the success ordering.
231  ///
232  /// [`compare_exchange`]: #method.compare_exchange
233  ///
234  /// # Panics
235  ///
236  /// If the given `SystemTime` value is earlier than the [`UNIX_EPOCH`](SystemTime::UNIX_EPOCH).
237  ///
238  /// # Examples
239  ///
240  /// ```rust
241  /// use atomic_time::AtomicOptionSystemTime;
242  /// use std::{time::{Duration, SystemTime}, sync::atomic::Ordering};
243  ///
244  /// let now = SystemTime::now();
245  /// let x = AtomicOptionSystemTime::none();
246  /// assert_eq!(x.fetch_update(Ordering::SeqCst, Ordering::SeqCst, |_| None), Err(None));
247  /// x.store(Some(now), Ordering::SeqCst);
248  /// assert_eq!(x.fetch_update(Ordering::SeqCst, Ordering::SeqCst, |x| Some(x.map(|val| val + Duration::from_secs(1)))), Ok(Some(now)));
249  /// 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))));
250  /// 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))));
251  /// assert_eq!(x.load(Ordering::SeqCst), Some(now + Duration::from_secs(3)));
252  /// ```
253  #[inline(always)]
254  pub fn fetch_update<F>(
255    &self,
256    set_order: Ordering,
257    fetch_order: Ordering,
258    f: F,
259  ) -> Result<Option<SystemTime>, Option<SystemTime>>
260  where
261    F: FnMut(Option<SystemTime>) -> Option<Option<SystemTime>>,
262  {
263    self.try_update(set_order, fetch_order, f)
264  }
265
266  /// Fetches the value, applies a function to produce a new value, stores it,
267  /// and returns the previous value.
268  ///
269  /// The closure can run more than once when another thread changes the value
270  /// concurrently, but it is applied only once to the value that is stored.
271  /// `set_order` and `fetch_order` have the same requirements as the success
272  /// and failure orderings of [`compare_exchange`](Self::compare_exchange).
273  ///
274  /// # Panics
275  ///
276  /// Panics if the orderings are invalid for
277  /// [`compare_exchange`](Self::compare_exchange), or if a `Some` value passed
278  /// to or returned from the function is earlier than
279  /// [`UNIX_EPOCH`](SystemTime::UNIX_EPOCH).
280  ///
281  /// # Examples
282  ///
283  /// ```rust
284  /// use atomic_time::AtomicOptionSystemTime;
285  /// use std::{sync::atomic::Ordering, time::{Duration, SystemTime}};
286  ///
287  /// let start = SystemTime::UNIX_EPOCH + Duration::from_secs(7);
288  /// let x = AtomicOptionSystemTime::new(Some(start));
289  /// assert_eq!(x.update(Ordering::SeqCst, Ordering::SeqCst, |old| old.map(|time| time + Duration::from_secs(1))), Some(start));
290  /// assert_eq!(x.load(Ordering::SeqCst), Some(start + Duration::from_secs(1)));
291  /// ```
292  #[inline(always)]
293  pub fn update<F>(
294    &self,
295    set_order: Ordering,
296    fetch_order: Ordering,
297    mut f: F,
298  ) -> Option<SystemTime>
299  where
300    F: FnMut(Option<SystemTime>) -> Option<SystemTime>,
301  {
302    self
303      .0
304      .update(set_order, fetch_order, |duration| {
305        f(duration.map(|d| SystemTime::UNIX_EPOCH + d))
306          .map(|d| d.duration_since(SystemTime::UNIX_EPOCH).unwrap())
307      })
308      .map(|d| SystemTime::UNIX_EPOCH + d)
309  }
310
311  /// Atomically stores the smaller of the current value and `val`, returning
312  /// the previous value.
313  ///
314  /// `None` is ordered before every `Some` value, and `Some` values are ordered
315  /// by their duration from [`UNIX_EPOCH`](SystemTime::UNIX_EPOCH). `order`
316  /// describes the memory ordering of the read-modify-write operation. Using
317  /// [`Acquire`](Ordering::Acquire) makes its store part relaxed, and using
318  /// [`Release`](Ordering::Release) makes its load part relaxed.
319  ///
320  /// # Panics
321  ///
322  /// Panics if a `Some` value in `val` is earlier than
323  /// [`UNIX_EPOCH`](SystemTime::UNIX_EPOCH).
324  ///
325  /// # Examples
326  ///
327  /// ```rust
328  /// use atomic_time::AtomicOptionSystemTime;
329  /// use std::{sync::atomic::Ordering, time::{Duration, SystemTime}};
330  ///
331  /// let low = SystemTime::UNIX_EPOCH + Duration::from_secs(5);
332  /// let middle = low + Duration::from_secs(1);
333  /// let high = middle + Duration::from_secs(1);
334  /// let x = AtomicOptionSystemTime::new(Some(middle));
335  /// assert_eq!(x.fetch_min(Some(high), Ordering::SeqCst), Some(middle));
336  /// assert_eq!(x.fetch_min(Some(low), Ordering::SeqCst), Some(middle));
337  /// assert_eq!(x.load(Ordering::SeqCst), Some(low));
338  /// ```
339  #[inline(always)]
340  pub fn fetch_min(&self, val: Option<SystemTime>, order: Ordering) -> Option<SystemTime> {
341    self
342      .0
343      .fetch_min(
344        val.map(|d| d.duration_since(SystemTime::UNIX_EPOCH).unwrap()),
345        order,
346      )
347      .map(|d| SystemTime::UNIX_EPOCH + d)
348  }
349
350  /// Atomically stores the larger of the current value and `val`, returning
351  /// the previous value.
352  ///
353  /// `None` is ordered before every `Some` value, and `Some` values are ordered
354  /// by their duration from [`UNIX_EPOCH`](SystemTime::UNIX_EPOCH). `order`
355  /// describes the memory ordering of the read-modify-write operation. Using
356  /// [`Acquire`](Ordering::Acquire) makes its store part relaxed, and using
357  /// [`Release`](Ordering::Release) makes its load part relaxed.
358  ///
359  /// # Panics
360  ///
361  /// Panics if a `Some` value in `val` is earlier than
362  /// [`UNIX_EPOCH`](SystemTime::UNIX_EPOCH).
363  ///
364  /// # Examples
365  ///
366  /// ```rust
367  /// use atomic_time::AtomicOptionSystemTime;
368  /// use std::{sync::atomic::Ordering, time::{Duration, SystemTime}};
369  ///
370  /// let high = SystemTime::UNIX_EPOCH + Duration::from_secs(7);
371  /// let x = AtomicOptionSystemTime::none();
372  /// assert_eq!(x.fetch_max(None, Ordering::SeqCst), None);
373  /// assert_eq!(x.fetch_max(Some(high), Ordering::SeqCst), None);
374  /// assert_eq!(x.load(Ordering::SeqCst), Some(high));
375  /// ```
376  #[inline(always)]
377  pub fn fetch_max(&self, val: Option<SystemTime>, order: Ordering) -> Option<SystemTime> {
378    self
379      .0
380      .fetch_max(
381        val.map(|d| d.duration_since(SystemTime::UNIX_EPOCH).unwrap()),
382        order,
383      )
384      .map(|d| SystemTime::UNIX_EPOCH + d)
385  }
386
387  /// Returns `true` if operations on values of this type are lock-free.
388  /// If the compiler or the platform doesn't support the necessary
389  /// atomic instructions, global locks for every potentially
390  /// concurrent atomic operation will be used.
391  ///
392  /// # Examples
393  /// ```
394  /// use atomic_time::AtomicOptionSystemTime;
395  ///
396  /// let is_lock_free = AtomicOptionSystemTime::is_lock_free();
397  /// ```
398  #[inline(always)]
399  pub fn is_lock_free() -> bool {
400    AtomicOptionDuration::is_lock_free()
401  }
402
403  /// Returns whether operations on values of this type are always lock-free.
404  ///
405  /// A `false` result does not preclude lock-free operations selected through
406  /// runtime CPU feature detection; use [`is_lock_free`](Self::is_lock_free)
407  /// to query the current target at runtime.
408  ///
409  /// # Examples
410  ///
411  /// ```rust
412  /// use atomic_time::AtomicOptionSystemTime;
413  ///
414  /// const ALWAYS_LOCK_FREE: bool = AtomicOptionSystemTime::is_always_lock_free();
415  /// let _ = ALWAYS_LOCK_FREE;
416  /// ```
417  #[inline(always)]
418  pub const fn is_always_lock_free() -> bool {
419    AtomicOptionDuration::is_always_lock_free()
420  }
421
422  /// Consumes the atomic and returns the contained value.
423  ///
424  /// This is safe because passing `self` by value guarantees that no other threads are
425  /// concurrently accessing the atomic data.
426  #[inline(always)]
427  pub fn into_inner(self) -> Option<SystemTime> {
428    self.0.into_inner().map(|d| SystemTime::UNIX_EPOCH + d)
429  }
430}
431
432#[cfg(feature = "serde")]
433const _: () = {
434  use serde::{Deserialize, Serialize};
435
436  impl Serialize for AtomicOptionSystemTime {
437    fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
438      self.load(Ordering::SeqCst).serialize(serializer)
439    }
440  }
441
442  impl<'de> Deserialize<'de> for AtomicOptionSystemTime {
443    fn deserialize<D: serde::Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
444      Ok(Self::new(Option::<SystemTime>::deserialize(deserializer)?))
445    }
446  }
447};
448
449#[cfg(test)]
450mod tests {
451  use super::*;
452  use std::time::Duration;
453
454  #[test]
455  fn test_atomic_option_system_time_now() {
456    let atomic_time = AtomicOptionSystemTime::now();
457    // Check that the time is not None and close to the current time.
458    assert!(atomic_time.load(Ordering::SeqCst).is_some());
459  }
460
461  #[test]
462  fn test_atomic_option_system_time_none() {
463    let atomic_time = AtomicOptionSystemTime::none();
464    assert_eq!(atomic_time.load(Ordering::SeqCst), None);
465  }
466
467  #[test]
468  fn test_atomic_option_system_time_new_and_load() {
469    let now = SystemTime::now();
470    let atomic_time = AtomicOptionSystemTime::new(Some(now));
471    assert_eq!(atomic_time.load(Ordering::SeqCst), Some(now));
472  }
473
474  #[test]
475  fn test_atomic_option_system_time_store_and_load() {
476    let now = SystemTime::now();
477    let after_one_sec = now + Duration::from_secs(1);
478    let atomic_time = AtomicOptionSystemTime::new(Some(now));
479    atomic_time.store(Some(after_one_sec), Ordering::SeqCst);
480    assert_eq!(atomic_time.load(Ordering::SeqCst), Some(after_one_sec));
481  }
482
483  #[test]
484  fn test_atomic_option_system_time_swap() {
485    let now = SystemTime::now();
486    let after_one_sec = now + Duration::from_secs(1);
487    let atomic_time = AtomicOptionSystemTime::new(Some(now));
488    let prev_time = atomic_time.swap(Some(after_one_sec), Ordering::SeqCst);
489    assert_eq!(prev_time, Some(now));
490    assert_eq!(atomic_time.load(Ordering::SeqCst), Some(after_one_sec));
491  }
492
493  #[test]
494  fn test_atomic_option_system_time_compare_exchange() {
495    let now = SystemTime::now();
496    let after_one_sec = now + Duration::from_secs(1);
497    let atomic_time = AtomicOptionSystemTime::new(Some(now));
498    let result = atomic_time.compare_exchange(
499      Some(now),
500      Some(after_one_sec),
501      Ordering::SeqCst,
502      Ordering::SeqCst,
503    );
504    assert!(result.is_ok());
505    assert_eq!(atomic_time.load(Ordering::SeqCst), Some(after_one_sec));
506  }
507
508  #[test]
509  fn test_atomic_option_system_time_compare_exchange_weak() {
510    let now = SystemTime::now();
511    let after_one_sec = now + Duration::from_secs(1);
512    let atomic_time = AtomicOptionSystemTime::new(Some(now));
513
514    let mut result;
515    loop {
516      result = atomic_time.compare_exchange_weak(
517        Some(now),
518        Some(after_one_sec),
519        Ordering::SeqCst,
520        Ordering::SeqCst,
521      );
522      if result.is_ok() {
523        break;
524      }
525    }
526    assert!(result.is_ok());
527    assert_eq!(atomic_time.load(Ordering::SeqCst), Some(after_one_sec));
528  }
529
530  #[test]
531  fn test_atomic_option_system_time_fetch_update() {
532    let now = SystemTime::now();
533    let atomic_time = AtomicOptionSystemTime::new(Some(now));
534
535    let result = atomic_time.fetch_update(Ordering::SeqCst, Ordering::SeqCst, |prev| {
536      Some(prev.map(|val| val + Duration::from_secs(1)))
537    });
538    assert!(result.is_ok());
539    assert_eq!(result.unwrap(), Some(now));
540    assert_eq!(
541      atomic_time.load(Ordering::SeqCst),
542      Some(now + Duration::from_secs(1))
543    );
544  }
545
546  #[test]
547  fn test_atomic_option_system_time_try_update() {
548    let start = SystemTime::UNIX_EPOCH + Duration::from_secs(10);
549    let atomic_time = AtomicOptionSystemTime::new(Some(start));
550
551    assert_eq!(
552      atomic_time.try_update(Ordering::SeqCst, Ordering::SeqCst, |_| None),
553      Err(Some(start))
554    );
555    assert_eq!(
556      atomic_time.try_update(Ordering::SeqCst, Ordering::SeqCst, |current| {
557        Some(current.map(|time| time + Duration::from_secs(2)))
558      }),
559      Ok(Some(start))
560    );
561    assert_eq!(
562      atomic_time.load(Ordering::SeqCst),
563      Some(start + Duration::from_secs(2))
564    );
565  }
566
567  #[test]
568  fn test_atomic_option_system_time_update_returns_previous_value() {
569    let start = SystemTime::UNIX_EPOCH + Duration::from_secs(10);
570    let atomic_time = AtomicOptionSystemTime::new(Some(start));
571
572    assert_eq!(
573      atomic_time.update(Ordering::SeqCst, Ordering::SeqCst, |current| {
574        current.map(|time| time + Duration::from_secs(2))
575      }),
576      Some(start)
577    );
578    assert_eq!(
579      atomic_time.load(Ordering::SeqCst),
580      Some(start + Duration::from_secs(2))
581    );
582  }
583
584  #[test]
585  fn test_atomic_option_system_time_fetch_min_and_max() {
586    let low = SystemTime::UNIX_EPOCH + Duration::from_secs(10);
587    let middle = low + Duration::from_secs(5);
588    let high = middle + Duration::from_secs(5);
589    let atomic_time = AtomicOptionSystemTime::new(Some(middle));
590
591    assert_eq!(
592      atomic_time.fetch_min(Some(high), Ordering::SeqCst),
593      Some(middle)
594    );
595    assert_eq!(atomic_time.load(Ordering::SeqCst), Some(middle));
596    assert_eq!(
597      atomic_time.fetch_min(Some(middle), Ordering::SeqCst),
598      Some(middle)
599    );
600    assert_eq!(atomic_time.load(Ordering::SeqCst), Some(middle));
601    assert_eq!(
602      atomic_time.fetch_min(Some(low), Ordering::SeqCst),
603      Some(middle)
604    );
605    assert_eq!(atomic_time.load(Ordering::SeqCst), Some(low));
606
607    atomic_time.store(Some(middle), Ordering::SeqCst);
608    assert_eq!(atomic_time.fetch_max(None, Ordering::SeqCst), Some(middle));
609    assert_eq!(atomic_time.load(Ordering::SeqCst), Some(middle));
610    assert_eq!(
611      atomic_time.fetch_max(Some(middle), Ordering::SeqCst),
612      Some(middle)
613    );
614    assert_eq!(atomic_time.load(Ordering::SeqCst), Some(middle));
615    assert_eq!(
616      atomic_time.fetch_max(Some(high), Ordering::SeqCst),
617      Some(middle)
618    );
619    assert_eq!(atomic_time.load(Ordering::SeqCst), Some(high));
620
621    atomic_time.store(None, Ordering::SeqCst);
622    assert_eq!(atomic_time.fetch_min(None, Ordering::SeqCst), None);
623    assert_eq!(atomic_time.load(Ordering::SeqCst), None);
624    assert_eq!(atomic_time.fetch_max(None, Ordering::SeqCst), None);
625    assert_eq!(atomic_time.load(Ordering::SeqCst), None);
626    assert_eq!(atomic_time.fetch_max(Some(low), Ordering::SeqCst), None);
627    assert_eq!(atomic_time.load(Ordering::SeqCst), Some(low));
628  }
629
630  #[test]
631  fn test_atomic_option_system_time_always_lock_free_implies_lock_free() {
632    assert!(
633      !AtomicOptionSystemTime::is_always_lock_free() || AtomicOptionSystemTime::is_lock_free()
634    );
635  }
636
637  #[test]
638  #[should_panic]
639  fn test_atomic_option_system_time_fetch_min_before_epoch_panics() {
640    let atomic_time = AtomicOptionSystemTime::none();
641    atomic_time.fetch_min(
642      Some(SystemTime::UNIX_EPOCH - Duration::from_secs(1)),
643      Ordering::SeqCst,
644    );
645  }
646
647  #[test]
648  fn test_atomic_option_system_time_thread_safety() {
649    use std::sync::Arc;
650    use std::thread;
651
652    // Fixed starting value + CAS loop = exact final result. The
653    // earlier implementation did `load + add + store` and asserted
654    // only that the final time was "within 4 seconds of now" — a
655    // bound that holds even when every concurrent update was lost.
656    let start = SystemTime::now();
657    let atomic_time = Arc::new(AtomicOptionSystemTime::new(Some(start)));
658    let mut handles = vec![];
659
660    for _ in 0..4 {
661      let atomic_clone = Arc::clone(&atomic_time);
662      let handle = thread::spawn(move || {
663        atomic_clone
664          .fetch_update(Ordering::SeqCst, Ordering::SeqCst, |current| {
665            current.map(|t| Some(t + Duration::from_secs(1)))
666          })
667          .expect("atomic is always Some in this test");
668      });
669      handles.push(handle);
670    }
671
672    for handle in handles {
673      handle.join().unwrap();
674    }
675
676    // 4 threads × 1 second = 4 seconds, no lost updates.
677    assert_eq!(
678      atomic_time.load(Ordering::SeqCst),
679      Some(start + Duration::from_secs(4))
680    );
681  }
682
683  #[test]
684  fn test_atomic_option_system_time_debug() {
685    let atomic_time = AtomicOptionSystemTime::now();
686    let debug_str = format!("{:?}", atomic_time);
687    assert!(debug_str.contains("AtomicOptionSystemTime"));
688  }
689
690  #[test]
691  fn test_atomic_option_system_time_default() {
692    let atomic_time = AtomicOptionSystemTime::default();
693    assert_eq!(atomic_time.load(Ordering::SeqCst), None);
694  }
695
696  #[test]
697  fn test_atomic_option_system_time_from() {
698    let now = Some(SystemTime::now());
699    let atomic_time = AtomicOptionSystemTime::from(now);
700    assert_eq!(atomic_time.load(Ordering::SeqCst), now);
701  }
702
703  #[test]
704  fn test_atomic_option_system_time_from_none() {
705    let atomic_time = AtomicOptionSystemTime::from(None);
706    assert_eq!(atomic_time.load(Ordering::SeqCst), None);
707  }
708
709  #[test]
710  fn test_atomic_option_system_time_into_inner() {
711    let now = Some(SystemTime::now());
712    let atomic_time = AtomicOptionSystemTime::new(now);
713    assert_eq!(atomic_time.into_inner(), now);
714  }
715
716  #[test]
717  fn test_atomic_option_system_time_into_inner_none() {
718    let atomic_time = AtomicOptionSystemTime::none();
719    assert_eq!(atomic_time.into_inner(), None);
720  }
721
722  #[test]
723  fn test_atomic_option_system_time_compare_exchange_failure() {
724    let now = Some(SystemTime::now());
725    let other = now.map(|t| t + Duration::from_secs(5));
726    let atomic_time = AtomicOptionSystemTime::new(now);
727    let result = atomic_time.compare_exchange(other, other, Ordering::SeqCst, Ordering::SeqCst);
728    assert!(result.is_err());
729    assert_eq!(result.unwrap_err(), now);
730  }
731
732  #[test]
733  fn test_atomic_option_system_time_compare_exchange_weak_failure() {
734    let now = Some(SystemTime::now());
735    let other = now.map(|t| t + Duration::from_secs(5));
736    let atomic_time = AtomicOptionSystemTime::new(now);
737    let result =
738      atomic_time.compare_exchange_weak(other, other, Ordering::SeqCst, Ordering::SeqCst);
739    assert!(result.is_err());
740  }
741
742  #[test]
743  fn test_atomic_option_system_time_fetch_update_failure() {
744    let now = Some(SystemTime::now());
745    let atomic_time = AtomicOptionSystemTime::new(now);
746    let result = atomic_time.fetch_update(Ordering::SeqCst, Ordering::SeqCst, |_| None);
747    assert!(result.is_err());
748    assert_eq!(result.unwrap_err(), now);
749  }
750
751  #[cfg(feature = "serde")]
752  #[test]
753  fn test_atomic_option_system_time_serde() {
754    let now = SystemTime::now();
755    let atomic = AtomicOptionSystemTime::new(Some(now));
756    let serialized = serde_json::to_string(&atomic).unwrap();
757    let deserialized: AtomicOptionSystemTime = serde_json::from_str(&serialized).unwrap();
758    assert_eq!(deserialized.load(Ordering::SeqCst), Some(now));
759  }
760}