atomic_time/
instant.rs

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