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