atomic_time/
system_time.rs

1use core::sync::atomic::Ordering;
2use std::time::SystemTime;
3
4use crate::AtomicDuration;
5
6/// An atomic version of [`std::time::SystemTime`].
7#[repr(transparent)]
8pub struct AtomicSystemTime(AtomicDuration);
9
10impl AtomicSystemTime {
11  /// Returns the system time corresponding to "now".
12  ///
13  /// # Examples
14  /// ```
15  /// use atomic_time::AtomicSystemTime;
16  ///
17  /// let sys_time = AtomicSystemTime::now();
18  /// ```
19  pub fn now() -> Self {
20    Self::new(SystemTime::now())
21  }
22
23  /// Creates a new `AtomicSystemTime` with the given `SystemTime` value.
24  ///
25  /// # Panics
26  ///
27  /// If the given `SystemTime` value is earlier than the [`UNIX_EPOCH`](SystemTime::UNIX_EPOCH).
28  pub fn new(system_time: SystemTime) -> Self {
29    Self(AtomicDuration::new(
30      system_time.duration_since(SystemTime::UNIX_EPOCH).unwrap(),
31    ))
32  }
33
34  /// Loads a value from the atomic system time.
35  pub fn load(&self, order: Ordering) -> SystemTime {
36    SystemTime::UNIX_EPOCH + self.0.load(order)
37  }
38
39  /// Stores a value into the atomic system time.
40  ///
41  /// # Panics
42  ///
43  /// If the given `SystemTime` value is earlier than the [`UNIX_EPOCH`](SystemTime::UNIX_EPOCH).
44  pub fn store(&self, system_time: SystemTime, order: Ordering) {
45    self.0.store(
46      system_time.duration_since(SystemTime::UNIX_EPOCH).unwrap(),
47      order,
48    )
49  }
50
51  /// Stores a value into the atomic system time, returning the previous value.
52  ///
53  /// # Panics
54  ///
55  /// If the given `SystemTime` value is earlier than the [`UNIX_EPOCH`](SystemTime::UNIX_EPOCH).
56  pub fn swap(&self, system_time: SystemTime, order: Ordering) -> SystemTime {
57    SystemTime::UNIX_EPOCH
58      + self.0.swap(
59        system_time.duration_since(SystemTime::UNIX_EPOCH).unwrap(),
60        order,
61      )
62  }
63
64  /// Stores a value into the atomic system time if the current value is the same as the `current`
65  /// value.
66  ///
67  /// # Panics
68  ///
69  /// If the given `SystemTime` value is earlier than the [`UNIX_EPOCH`](SystemTime::UNIX_EPOCH).
70  pub fn compare_exchange(
71    &self,
72    current: SystemTime,
73    new: SystemTime,
74    success: Ordering,
75    failure: Ordering,
76  ) -> Result<SystemTime, SystemTime> {
77    match self.0.compare_exchange(
78      current.duration_since(SystemTime::UNIX_EPOCH).unwrap(),
79      new.duration_since(SystemTime::UNIX_EPOCH).unwrap(),
80      success,
81      failure,
82    ) {
83      Ok(duration) => Ok(SystemTime::UNIX_EPOCH + duration),
84      Err(duration) => Err(SystemTime::UNIX_EPOCH + duration),
85    }
86  }
87
88  /// Stores a value into the atomic system time if the current value is the same as the `current`
89  /// value.
90  ///
91  /// # Panics
92  ///
93  /// If the given `SystemTime` value is earlier than the [`UNIX_EPOCH`](SystemTime::UNIX_EPOCH).
94  pub fn compare_exchange_weak(
95    &self,
96    current: SystemTime,
97    new: SystemTime,
98    success: Ordering,
99    failure: Ordering,
100  ) -> Result<SystemTime, SystemTime> {
101    match self.0.compare_exchange_weak(
102      current.duration_since(SystemTime::UNIX_EPOCH).unwrap(),
103      new.duration_since(SystemTime::UNIX_EPOCH).unwrap(),
104      success,
105      failure,
106    ) {
107      Ok(duration) => Ok(SystemTime::UNIX_EPOCH + duration),
108      Err(duration) => Err(SystemTime::UNIX_EPOCH + duration),
109    }
110  }
111
112  /// Fetches the value, and applies a function to it that returns an optional
113  /// new value. Returns a `Result` of `Ok(previous_value)` if the function returned `Some(_)`, else
114  /// `Err(previous_value)`.
115  ///
116  /// Note: This may call the function multiple times if the value has been changed from other threads in
117  /// the meantime, as long as the function returns `Some(_)`, but the function will have been applied
118  /// only once to the stored value.
119  ///
120  /// `fetch_update` takes two [`Ordering`] arguments to describe the memory ordering of this operation.
121  /// The first describes the required ordering for when the operation finally succeeds while the second
122  /// describes the required ordering for loads. These correspond to the success and failure orderings of
123  /// [`compare_exchange`] respectively.
124  ///
125  /// Using [`Acquire`](Ordering::Acquire) as success ordering makes the store part
126  /// of this operation [`Relaxed`](Ordering::Relaxed), and using [`Release`](Ordering::Release) makes the final successful load
127  /// [`Relaxed`](Ordering::Relaxed). The (failed) load ordering can only be [`SeqCst`](Ordering::SeqCst), [`Acquire`](Ordering::Acquire) or [`Relaxed`](Ordering::Release)
128  /// and must be equivalent to or weaker than the success ordering.
129  ///
130  /// [`compare_exchange`]: #method.compare_exchange
131  ///
132  /// # Panics
133  ///
134  /// If the given `SystemTime` value is earlier than the [`UNIX_EPOCH`](SystemTime::UNIX_EPOCH).
135  ///
136  /// # Examples
137  ///
138  /// ```rust
139  /// use atomic_time::AtomicSystemTime;
140  /// use std::{time::{Duration, SystemTime}, sync::atomic::Ordering};
141  ///
142  /// let now = SystemTime::now();
143  /// let x = AtomicSystemTime::new(now);
144  /// assert_eq!(x.fetch_update(Ordering::SeqCst, Ordering::SeqCst, |_| None), Err(now));
145  ///
146  /// assert_eq!(x.fetch_update(Ordering::SeqCst, Ordering::SeqCst, |x| Some(x + Duration::from_secs(1))), Ok(now));
147  /// assert_eq!(x.fetch_update(Ordering::SeqCst, Ordering::SeqCst, |x| Some(x + Duration::from_secs(1))), Ok(now + Duration::from_secs(1)));
148  /// assert_eq!(x.load(Ordering::SeqCst), now + Duration::from_secs(2));
149  /// ```
150  pub fn fetch_update<F>(
151    &self,
152    set_order: Ordering,
153    fetch_order: Ordering,
154    mut f: F,
155  ) -> Result<SystemTime, SystemTime>
156  where
157    F: FnMut(SystemTime) -> Option<SystemTime>,
158  {
159    self
160      .0
161      .fetch_update(set_order, fetch_order, |duration| {
162        f(SystemTime::UNIX_EPOCH + duration)
163          .map(|system_time| system_time.duration_since(SystemTime::UNIX_EPOCH).unwrap())
164      })
165      .map(|duration| SystemTime::UNIX_EPOCH + duration)
166      .map_err(|duration| SystemTime::UNIX_EPOCH + duration)
167  }
168
169  /// Returns `true` if operations on values of this type are lock-free.
170  /// If the compiler or the platform doesn't support the necessary
171  /// atomic instructions, global locks for every potentially
172  /// concurrent atomic operation will be used.
173  ///
174  /// # Examples
175  /// ```
176  /// use atomic_time::AtomicSystemTime;
177  ///
178  /// let is_lock_free = AtomicSystemTime::is_lock_free();
179  /// ```
180  #[inline]
181  pub fn is_lock_free() -> bool {
182    AtomicDuration::is_lock_free()
183  }
184}
185
186#[cfg(feature = "serde")]
187const _: () = {
188  use serde::{Deserialize, Serialize};
189
190  impl Serialize for AtomicSystemTime {
191    fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
192      self.load(Ordering::SeqCst).serialize(serializer)
193    }
194  }
195
196  impl<'de> Deserialize<'de> for AtomicSystemTime {
197    fn deserialize<D: serde::Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
198      Ok(Self::new(SystemTime::deserialize(deserializer)?))
199    }
200  }
201};
202
203#[cfg(test)]
204mod tests {
205  use super::*;
206  use std::time::Duration;
207
208  #[test]
209  fn test_atomic_system_time_now() {
210    let atomic_time = AtomicSystemTime::now();
211    // As SystemTime::now is very precise, this test needs to be lenient with timing.
212    // Just check that the time is not the UNIX_EPOCH.
213    assert_ne!(atomic_time.load(Ordering::SeqCst), SystemTime::UNIX_EPOCH);
214  }
215
216  #[test]
217  fn test_atomic_system_time_new_and_load() {
218    let now = SystemTime::now();
219    let atomic_time = AtomicSystemTime::new(now);
220    assert_eq!(atomic_time.load(Ordering::SeqCst), now);
221  }
222
223  #[test]
224  fn test_atomic_system_time_store_and_load() {
225    let now = SystemTime::now();
226    let after_one_sec = now + Duration::from_secs(1);
227    let atomic_time = AtomicSystemTime::new(now);
228    atomic_time.store(after_one_sec, Ordering::SeqCst);
229    assert_eq!(atomic_time.load(Ordering::SeqCst), after_one_sec);
230  }
231
232  #[test]
233  fn test_atomic_system_time_swap() {
234    let now = SystemTime::now();
235    let after_one_sec = now + Duration::from_secs(1);
236    let atomic_time = AtomicSystemTime::new(now);
237    let prev_time = atomic_time.swap(after_one_sec, Ordering::SeqCst);
238    assert_eq!(prev_time, now);
239    assert_eq!(atomic_time.load(Ordering::SeqCst), after_one_sec);
240  }
241
242  #[test]
243  fn test_atomic_system_time_compare_exchange_weak() {
244    let now = SystemTime::now();
245    let after_one_sec = now + Duration::from_secs(1);
246    let after_two_secs = now + Duration::from_secs(2);
247    let atomic_time = AtomicSystemTime::new(now);
248
249    // Successful compare_exchange_weak
250    let mut result;
251    loop {
252      result =
253        atomic_time.compare_exchange_weak(now, after_one_sec, Ordering::SeqCst, Ordering::SeqCst);
254      if result.is_ok() {
255        break;
256      }
257    }
258    assert!(result.is_ok());
259    assert_eq!(atomic_time.load(Ordering::SeqCst), after_one_sec);
260
261    // Failed compare_exchange_weak
262    let result =
263      atomic_time.compare_exchange_weak(now, after_two_secs, Ordering::SeqCst, Ordering::SeqCst);
264    assert!(result.is_err());
265    assert_eq!(result.unwrap_err(), after_one_sec);
266  }
267
268  #[test]
269  fn test_atomic_system_time_compare_exchange() {
270    let now = SystemTime::now();
271    let after_one_sec = now + Duration::from_secs(1);
272    let atomic_time = AtomicSystemTime::new(now);
273    let result =
274      atomic_time.compare_exchange(now, after_one_sec, Ordering::SeqCst, Ordering::SeqCst);
275    assert!(result.is_ok());
276    assert_eq!(atomic_time.load(Ordering::SeqCst), after_one_sec);
277  }
278
279  #[test]
280  fn test_atomic_system_time_fetch_update() {
281    let now = SystemTime::now();
282    let atomic_time = AtomicSystemTime::new(now);
283
284    // Update the time by adding 1 second
285    let result = atomic_time.fetch_update(Ordering::SeqCst, Ordering::SeqCst, |prev| {
286      Some(prev + Duration::from_secs(1))
287    });
288    assert!(result.is_ok());
289    assert_eq!(result.unwrap(), now);
290    assert_eq!(
291      atomic_time.load(Ordering::SeqCst),
292      now + Duration::from_secs(1)
293    );
294
295    // Trying an update that returns None, should not change the value
296    let result = atomic_time.fetch_update(Ordering::SeqCst, Ordering::SeqCst, |_| None);
297    assert!(result.is_err());
298    assert_eq!(result.unwrap_err(), now + Duration::from_secs(1));
299    assert_eq!(
300      atomic_time.load(Ordering::SeqCst),
301      now + Duration::from_secs(1)
302    );
303  }
304
305  #[test]
306  #[cfg(feature = "std")]
307  fn test_atomic_system_time_thread_safety() {
308    use std::sync::Arc;
309    use std::thread;
310
311    let atomic_time = Arc::new(AtomicSystemTime::now());
312    let mut handles = vec![];
313
314    for _ in 0..4 {
315      let atomic_clone = atomic_time.clone();
316      let handle = thread::spawn(move || {
317        let current = atomic_clone.load(Ordering::SeqCst);
318        let new = current + Duration::from_secs(1);
319        atomic_clone.store(new, Ordering::SeqCst);
320      });
321      handles.push(handle);
322    }
323
324    for handle in handles {
325      handle.join().unwrap();
326    }
327
328    // This checks that the time has advanced, but it's not precise about how much,
329    // due to the non-deterministic nature of thread execution order and timing.
330    assert!(atomic_time.load(Ordering::SeqCst) > SystemTime::now());
331  }
332
333  #[cfg(feature = "serde")]
334  #[test]
335  fn test_atomic_system_time_serde() {
336    use serde::{Deserialize, Serialize};
337
338    #[derive(Serialize, Deserialize)]
339    struct Test {
340      time: AtomicSystemTime,
341    }
342
343    let now = SystemTime::now();
344    let test = Test {
345      time: AtomicSystemTime::new(now),
346    };
347    let serialized = serde_json::to_string(&test).unwrap();
348    let deserialized: Test = serde_json::from_str(&serialized).unwrap();
349    assert_eq!(deserialized.time.load(Ordering::SeqCst), now,);
350  }
351}