Skip to main content

atomic_time/
option_instant.rs

1use std::time::Instant;
2
3use super::*;
4
5/// Atomic version of [`Option<Instant>`].
6#[repr(transparent)]
7pub struct AtomicOptionInstant(AtomicOptionDuration);
8
9impl core::fmt::Debug for AtomicOptionInstant {
10  fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
11    f.debug_tuple("AtomicOptionInstant")
12      .field(&self.load(Ordering::SeqCst))
13      .finish()
14  }
15}
16impl Default for AtomicOptionInstant {
17  /// Equivalent to `Option::<Instant>::None`.
18  #[inline]
19  fn default() -> Self {
20    Self::none()
21  }
22}
23impl From<Option<Instant>> for AtomicOptionInstant {
24  #[inline]
25  fn from(instant: Option<Instant>) -> Self {
26    Self::new(instant)
27  }
28}
29
30impl AtomicOptionInstant {
31  /// Equivalent to atomic version `Option::<Instant>::None`.
32  ///
33  /// # Examples
34  ///
35  /// ```rust
36  /// use atomic_time::AtomicOptionInstant;
37  ///
38  /// let none = AtomicOptionInstant::none();
39  /// assert_eq!(none.load(std::sync::atomic::Ordering::SeqCst), None);
40  /// ```
41  #[inline]
42  pub const fn none() -> Self {
43    Self(AtomicOptionDuration::new(None))
44  }
45
46  /// Returns the instant corresponding to "now".
47  ///
48  /// # Examples
49  /// ```
50  /// use atomic_time::AtomicOptionInstant;
51  ///
52  /// let now = AtomicOptionInstant::now();
53  /// ```
54  pub fn now() -> Self {
55    Self::new(Some(Instant::now()))
56  }
57
58  /// Creates a new `AtomicOptionInstant` with the given `Instant` value.
59  pub fn new(instant: Option<Instant>) -> Self {
60    Self(AtomicOptionDuration::new(
61      instant.map(encode_instant_to_duration),
62    ))
63  }
64
65  /// Loads a value from the atomic instant.
66  pub fn load(&self, order: Ordering) -> Option<Instant> {
67    self.0.load(order).map(decode_instant_from_duration)
68  }
69
70  /// Stores a value into the atomic instant.
71  pub fn store(&self, instant: Option<Instant>, order: Ordering) {
72    self.0.store(instant.map(encode_instant_to_duration), order)
73  }
74
75  /// Stores a value into the atomic instant, returning the previous value.
76  pub fn swap(&self, instant: Option<Instant>, order: Ordering) -> Option<Instant> {
77    self
78      .0
79      .swap(instant.map(encode_instant_to_duration), order)
80      .map(decode_instant_from_duration)
81  }
82
83  /// Stores a value into the atomic instant if the current value is the same as the `current`
84  /// value.
85  pub fn compare_exchange(
86    &self,
87    current: Option<Instant>,
88    new: Option<Instant>,
89    success: Ordering,
90    failure: Ordering,
91  ) -> Result<Option<Instant>, Option<Instant>> {
92    match self.0.compare_exchange(
93      current.map(encode_instant_to_duration),
94      new.map(encode_instant_to_duration),
95      success,
96      failure,
97    ) {
98      Ok(duration) => Ok(duration.map(decode_instant_from_duration)),
99      Err(duration) => Err(duration.map(decode_instant_from_duration)),
100    }
101  }
102
103  /// Stores a value into the atomic instant if the current value is the same as the `current`
104  /// value.
105  pub fn compare_exchange_weak(
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_weak(
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  /// Fetches the value, and applies a function to it that returns an optional
124  /// new value. Returns a `Result` of `Ok(previous_value)` if the function returned `Some(_)`, else
125  /// `Err(previous_value)`.
126  ///
127  /// Note: This may call the function multiple times if the value has been changed from other threads in
128  /// the meantime, as long as the function returns `Some(_)`, but the function will have been applied
129  /// only once to the stored value.
130  ///
131  /// `fetch_update` takes two [`Ordering`] arguments to describe the memory ordering of this operation.
132  /// The first describes the required ordering for when the operation finally succeeds while the second
133  /// describes the required ordering for loads. These correspond to the success and failure orderings of
134  /// [`compare_exchange`] respectively.
135  ///
136  /// Using [`Acquire`](Ordering::Acquire) as success ordering makes the store part
137  /// of this operation [`Relaxed`](Ordering::Relaxed), and using [`Release`](Ordering::Release) makes the final successful load
138  /// [`Relaxed`](Ordering::Relaxed). The (failed) load ordering can only be [`SeqCst`](Ordering::SeqCst), [`Acquire`](Ordering::Acquire) or [`Relaxed`](Ordering::Release)
139  /// and must be equivalent to or weaker than the success ordering.
140  ///
141  /// [`compare_exchange`]: #method.compare_exchange
142  ///
143  /// # Examples
144  ///
145  /// ```rust
146  /// use atomic_time::AtomicOptionInstant;
147  /// use std::{time::{Duration, Instant}, sync::atomic::Ordering};
148  ///
149  /// let now = Instant::now();
150  /// let x = AtomicOptionInstant::none();
151  /// assert_eq!(x.fetch_update(Ordering::SeqCst, Ordering::SeqCst, |_| None), Err(None));
152  /// x.store(Some(now), Ordering::SeqCst);
153  /// assert_eq!(x.fetch_update(Ordering::SeqCst, Ordering::SeqCst, |x| Some(x.map(|val| val + Duration::from_secs(1)))), Ok(Some(now)));
154  /// 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))));
155  /// 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))));
156  /// assert_eq!(x.load(Ordering::SeqCst), Some(now + Duration::from_secs(3)));
157  /// ```
158  pub fn fetch_update<F>(
159    &self,
160    set_order: Ordering,
161    fetch_order: Ordering,
162    mut f: F,
163  ) -> Result<Option<Instant>, Option<Instant>>
164  where
165    F: FnMut(Option<Instant>) -> Option<Option<Instant>>,
166  {
167    self
168      .0
169      .fetch_update(set_order, fetch_order, |duration| {
170        f(duration.map(decode_instant_from_duration))
171          .map(|system_time| system_time.map(encode_instant_to_duration))
172      })
173      .map(|duration| duration.map(decode_instant_from_duration))
174      .map_err(|duration| duration.map(decode_instant_from_duration))
175  }
176
177  /// Returns `true` if operations on values of this type are lock-free.
178  /// If the compiler or the platform doesn't support the necessary
179  /// atomic instructions, global locks for every potentially
180  /// concurrent atomic operation will be used.
181  ///
182  /// # Examples
183  /// ```
184  /// use atomic_time::AtomicOptionInstant;
185  ///
186  /// let is_lock_free = AtomicOptionInstant::is_lock_free();
187  /// ```
188  #[inline]
189  pub fn is_lock_free() -> bool {
190    AtomicU128::is_lock_free()
191  }
192
193  /// Consumes the atomic and returns the contained value.
194  ///
195  /// This is safe because passing `self` by value guarantees that no other threads are
196  /// concurrently accessing the atomic data.
197  #[inline]
198  pub fn into_inner(self) -> Option<Instant> {
199    self.0.into_inner().map(decode_instant_from_duration)
200  }
201}
202
203#[cfg(feature = "serde")]
204const _: () = {
205  use core::time::Duration;
206  use serde::{Deserialize, Serialize};
207
208  impl Serialize for AtomicOptionInstant {
209    fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
210      self.0.load(Ordering::SeqCst).serialize(serializer)
211    }
212  }
213
214  impl<'de> Deserialize<'de> for AtomicOptionInstant {
215    fn deserialize<D: serde::Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
216      Ok(Self::new(
217        Option::<Duration>::deserialize(deserializer)?.map(decode_instant_from_duration),
218      ))
219    }
220  }
221};
222
223#[cfg(test)]
224mod tests {
225  use super::*;
226  use std::time::Duration;
227
228  #[test]
229  fn test_atomic_option_instant_none() {
230    let atomic_instant = AtomicOptionInstant::none();
231    assert_eq!(atomic_instant.load(Ordering::SeqCst), None);
232  }
233
234  #[test]
235  fn test_atomic_option_instant_now() {
236    let atomic_instant = AtomicOptionInstant::now();
237    let now = Instant::now();
238    if let Some(loaded_instant) = atomic_instant.load(Ordering::SeqCst) {
239      assert!(loaded_instant <= now);
240      assert!(loaded_instant >= now - Duration::from_secs(1));
241    } else {
242      panic!("AtomicOptionInstant::now() should not be None");
243    }
244  }
245
246  #[test]
247  fn test_atomic_option_instant_new_and_load() {
248    let now = Some(Instant::now());
249    let atomic_instant = AtomicOptionInstant::new(now);
250    assert_eq!(atomic_instant.load(Ordering::SeqCst), now);
251  }
252
253  #[test]
254  fn test_atomic_option_instant_store_and_load() {
255    let now = Some(Instant::now());
256    let after_one_sec = now.map(|t| t + Duration::from_secs(1));
257    let atomic_instant = AtomicOptionInstant::new(now);
258    atomic_instant.store(after_one_sec, Ordering::SeqCst);
259    assert_eq!(atomic_instant.load(Ordering::SeqCst), after_one_sec);
260  }
261
262  #[test]
263  fn test_atomic_option_instant_swap() {
264    let now = Some(Instant::now());
265    let after_one_sec = now.map(|t| t + Duration::from_secs(1));
266    let atomic_instant = AtomicOptionInstant::new(now);
267    let prev_instant = atomic_instant.swap(after_one_sec, Ordering::SeqCst);
268    assert_eq!(prev_instant, now);
269    assert_eq!(atomic_instant.load(Ordering::SeqCst), after_one_sec);
270  }
271
272  #[test]
273  fn test_atomic_option_instant_compare_exchange() {
274    let now = Some(Instant::now());
275    let after_one_sec = now.map(|t| t + Duration::from_secs(1));
276    let atomic_instant = AtomicOptionInstant::new(now);
277    let result =
278      atomic_instant.compare_exchange(now, after_one_sec, Ordering::SeqCst, Ordering::SeqCst);
279    assert!(result.is_ok());
280    assert_eq!(atomic_instant.load(Ordering::SeqCst), after_one_sec);
281  }
282
283  #[test]
284  fn test_atomic_option_instant_compare_exchange_weak() {
285    let now = Some(Instant::now());
286    let after_one_sec = now.map(|t| t + Duration::from_secs(1));
287    let atomic_instant = AtomicOptionInstant::new(now);
288
289    let mut result;
290    loop {
291      result = atomic_instant.compare_exchange_weak(
292        now,
293        after_one_sec,
294        Ordering::SeqCst,
295        Ordering::SeqCst,
296      );
297      if result.is_ok() {
298        break;
299      }
300    }
301    assert!(result.is_ok());
302    assert_eq!(atomic_instant.load(Ordering::SeqCst), after_one_sec);
303  }
304
305  #[test]
306  fn test_atomic_option_instant_fetch_update() {
307    let now = Some(Instant::now());
308    let atomic_instant = AtomicOptionInstant::new(now);
309
310    let result = atomic_instant.fetch_update(Ordering::SeqCst, Ordering::SeqCst, |prev| {
311      Some(prev.map(|t| t + Duration::from_secs(1)))
312    });
313    assert!(result.is_ok());
314    assert_eq!(result.unwrap(), now);
315    assert_eq!(
316      atomic_instant.load(Ordering::SeqCst),
317      now.map(|t| t + Duration::from_secs(1))
318    );
319  }
320
321  #[test]
322  fn test_atomic_option_instant_thread_safety() {
323    use std::sync::Arc;
324    use std::thread;
325
326    let atomic_time = Arc::new(AtomicOptionInstant::now());
327    let mut handles = vec![];
328
329    // Spawn multiple threads to update the time
330    for _ in 0..4 {
331      let atomic_clone = Arc::clone(&atomic_time);
332      let handle = thread::spawn(move || {
333        let current = atomic_clone.load(Ordering::SeqCst);
334        if let Some(current_time) = current {
335          // Adding 1 second to the current time
336          let new_time = current_time + Duration::from_secs(1);
337          atomic_clone.store(Some(new_time), Ordering::SeqCst);
338        }
339      });
340      handles.push(handle);
341    }
342
343    // Wait for all threads to complete
344    for handle in handles {
345      handle.join().unwrap();
346    }
347
348    // Verify that the time has been updated
349    if let Some(updated_time) = atomic_time.load(Ordering::SeqCst) {
350      assert!(updated_time > Instant::now() - Duration::from_secs(4));
351    } else {
352      panic!("AtomicOptionInstant should not be None");
353    }
354  }
355
356  #[cfg(feature = "serde")]
357  #[test]
358  fn test_atomic_option_instant_serde() {
359    use serde::{Deserialize, Serialize};
360
361    #[derive(Serialize, Deserialize)]
362    struct Test {
363      time: AtomicOptionInstant,
364    }
365
366    let now = Instant::now();
367    let test = Test {
368      time: AtomicOptionInstant::new(Some(now)),
369    };
370    let serialized = serde_json::to_string(&test).unwrap();
371    let deserialized: Test = serde_json::from_str(&serialized).unwrap();
372    assert_eq!(deserialized.time.load(Ordering::SeqCst), Some(now));
373  }
374}