1use core::sync::atomic::Ordering;
2use std::time::SystemTime;
3
4use crate::AtomicDuration;
5
6#[repr(transparent)]
8pub struct AtomicSystemTime(AtomicDuration);
9
10impl core::fmt::Debug for AtomicSystemTime {
11 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
12 f.debug_tuple("AtomicSystemTime")
13 .field(&self.load(Ordering::SeqCst))
14 .finish()
15 }
16}
17
18impl From<SystemTime> for AtomicSystemTime {
19 #[cfg_attr(not(tarpaulin), inline(always))]
23 fn from(system_time: SystemTime) -> Self {
24 Self::new(system_time)
25 }
26}
27
28impl AtomicSystemTime {
29 #[cfg_attr(not(tarpaulin), inline(always))]
38 pub fn now() -> Self {
39 Self::new(SystemTime::now())
40 }
41
42 #[cfg_attr(not(tarpaulin), inline(always))]
48 pub fn new(system_time: SystemTime) -> Self {
49 Self(AtomicDuration::new(
50 system_time.duration_since(SystemTime::UNIX_EPOCH).unwrap(),
51 ))
52 }
53
54 #[cfg_attr(not(tarpaulin), inline(always))]
56 pub fn load(&self, order: Ordering) -> SystemTime {
57 SystemTime::UNIX_EPOCH + self.0.load(order)
58 }
59
60 #[cfg_attr(not(tarpaulin), inline(always))]
66 pub fn store(&self, system_time: SystemTime, order: Ordering) {
67 self.0.store(
68 system_time.duration_since(SystemTime::UNIX_EPOCH).unwrap(),
69 order,
70 )
71 }
72
73 #[cfg_attr(not(tarpaulin), inline(always))]
79 pub fn swap(&self, system_time: SystemTime, order: Ordering) -> SystemTime {
80 SystemTime::UNIX_EPOCH
81 + self.0.swap(
82 system_time.duration_since(SystemTime::UNIX_EPOCH).unwrap(),
83 order,
84 )
85 }
86
87 #[cfg_attr(not(tarpaulin), inline(always))]
94 pub fn compare_exchange(
95 &self,
96 current: SystemTime,
97 new: SystemTime,
98 success: Ordering,
99 failure: Ordering,
100 ) -> Result<SystemTime, SystemTime> {
101 match self.0.compare_exchange(
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 #[cfg_attr(not(tarpaulin), inline(always))]
119 pub fn compare_exchange_weak(
120 &self,
121 current: SystemTime,
122 new: SystemTime,
123 success: Ordering,
124 failure: Ordering,
125 ) -> Result<SystemTime, SystemTime> {
126 match self.0.compare_exchange_weak(
127 current.duration_since(SystemTime::UNIX_EPOCH).unwrap(),
128 new.duration_since(SystemTime::UNIX_EPOCH).unwrap(),
129 success,
130 failure,
131 ) {
132 Ok(duration) => Ok(SystemTime::UNIX_EPOCH + duration),
133 Err(duration) => Err(SystemTime::UNIX_EPOCH + duration),
134 }
135 }
136
137 #[cfg_attr(not(tarpaulin), inline(always))]
176 pub fn fetch_update<F>(
177 &self,
178 set_order: Ordering,
179 fetch_order: Ordering,
180 mut f: F,
181 ) -> Result<SystemTime, SystemTime>
182 where
183 F: FnMut(SystemTime) -> Option<SystemTime>,
184 {
185 self
186 .0
187 .fetch_update(set_order, fetch_order, |duration| {
188 f(SystemTime::UNIX_EPOCH + duration)
189 .map(|system_time| system_time.duration_since(SystemTime::UNIX_EPOCH).unwrap())
190 })
191 .map(|duration| SystemTime::UNIX_EPOCH + duration)
192 .map_err(|duration| SystemTime::UNIX_EPOCH + duration)
193 }
194
195 #[cfg_attr(not(tarpaulin), inline(always))]
207 pub fn is_lock_free() -> bool {
208 AtomicDuration::is_lock_free()
209 }
210
211 #[cfg_attr(not(tarpaulin), inline(always))]
216 pub fn into_inner(self) -> SystemTime {
217 SystemTime::UNIX_EPOCH + self.0.into_inner()
218 }
219}
220
221#[cfg(feature = "serde")]
222const _: () = {
223 use serde::{Deserialize, Serialize};
224
225 impl Serialize for AtomicSystemTime {
226 fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
227 self.load(Ordering::SeqCst).serialize(serializer)
228 }
229 }
230
231 impl<'de> Deserialize<'de> for AtomicSystemTime {
232 fn deserialize<D: serde::Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
233 Ok(Self::new(SystemTime::deserialize(deserializer)?))
234 }
235 }
236};
237
238#[cfg(test)]
239mod tests {
240 use super::*;
241 use std::time::Duration;
242
243 #[test]
244 fn test_atomic_system_time_now() {
245 let atomic_time = AtomicSystemTime::now();
246 assert_ne!(atomic_time.load(Ordering::SeqCst), SystemTime::UNIX_EPOCH);
249 }
250
251 #[test]
252 fn test_atomic_system_time_new_and_load() {
253 let now = SystemTime::now();
254 let atomic_time = AtomicSystemTime::new(now);
255 assert_eq!(atomic_time.load(Ordering::SeqCst), now);
256 }
257
258 #[test]
259 fn test_atomic_system_time_store_and_load() {
260 let now = SystemTime::now();
261 let after_one_sec = now + Duration::from_secs(1);
262 let atomic_time = AtomicSystemTime::new(now);
263 atomic_time.store(after_one_sec, Ordering::SeqCst);
264 assert_eq!(atomic_time.load(Ordering::SeqCst), after_one_sec);
265 }
266
267 #[test]
268 fn test_atomic_system_time_swap() {
269 let now = SystemTime::now();
270 let after_one_sec = now + Duration::from_secs(1);
271 let atomic_time = AtomicSystemTime::new(now);
272 let prev_time = atomic_time.swap(after_one_sec, Ordering::SeqCst);
273 assert_eq!(prev_time, now);
274 assert_eq!(atomic_time.load(Ordering::SeqCst), after_one_sec);
275 }
276
277 #[test]
278 fn test_atomic_system_time_compare_exchange_weak() {
279 let now = SystemTime::now();
280 let after_one_sec = now + Duration::from_secs(1);
281 let after_two_secs = now + Duration::from_secs(2);
282 let atomic_time = AtomicSystemTime::new(now);
283
284 let mut result;
286 loop {
287 result =
288 atomic_time.compare_exchange_weak(now, after_one_sec, Ordering::SeqCst, Ordering::SeqCst);
289 if result.is_ok() {
290 break;
291 }
292 }
293 assert!(result.is_ok());
294 assert_eq!(atomic_time.load(Ordering::SeqCst), after_one_sec);
295
296 let result =
298 atomic_time.compare_exchange_weak(now, after_two_secs, Ordering::SeqCst, Ordering::SeqCst);
299 assert!(result.is_err());
300 assert_eq!(result.unwrap_err(), after_one_sec);
301 }
302
303 #[test]
304 fn test_atomic_system_time_compare_exchange() {
305 let now = SystemTime::now();
306 let after_one_sec = now + Duration::from_secs(1);
307 let atomic_time = AtomicSystemTime::new(now);
308 let result =
309 atomic_time.compare_exchange(now, after_one_sec, Ordering::SeqCst, Ordering::SeqCst);
310 assert!(result.is_ok());
311 assert_eq!(atomic_time.load(Ordering::SeqCst), after_one_sec);
312 }
313
314 #[test]
315 fn test_atomic_system_time_fetch_update() {
316 let now = SystemTime::now();
317 let atomic_time = AtomicSystemTime::new(now);
318
319 let result = atomic_time.fetch_update(Ordering::SeqCst, Ordering::SeqCst, |prev| {
321 Some(prev + Duration::from_secs(1))
322 });
323 assert!(result.is_ok());
324 assert_eq!(result.unwrap(), now);
325 assert_eq!(
326 atomic_time.load(Ordering::SeqCst),
327 now + Duration::from_secs(1)
328 );
329
330 let result = atomic_time.fetch_update(Ordering::SeqCst, Ordering::SeqCst, |_| None);
332 assert!(result.is_err());
333 assert_eq!(result.unwrap_err(), now + Duration::from_secs(1));
334 assert_eq!(
335 atomic_time.load(Ordering::SeqCst),
336 now + Duration::from_secs(1)
337 );
338 }
339
340 #[test]
341 #[cfg(feature = "std")]
342 fn test_atomic_system_time_thread_safety() {
343 use std::sync::Arc;
344 use std::thread;
345
346 let start = SystemTime::now();
352 let atomic_time = Arc::new(AtomicSystemTime::new(start));
353 let mut handles = vec![];
354
355 for _ in 0..4 {
356 let atomic_clone = atomic_time.clone();
357 let handle = thread::spawn(move || {
358 atomic_clone
359 .fetch_update(Ordering::SeqCst, Ordering::SeqCst, |current| {
360 Some(current + Duration::from_secs(1))
361 })
362 .expect("closure never returns None");
363 });
364 handles.push(handle);
365 }
366
367 for handle in handles {
368 handle.join().unwrap();
369 }
370
371 assert_eq!(
373 atomic_time.load(Ordering::SeqCst),
374 start + Duration::from_secs(4)
375 );
376 }
377
378 #[test]
379 fn test_atomic_system_time_debug() {
380 let atomic_time = AtomicSystemTime::now();
381 let debug_str = format!("{:?}", atomic_time);
382 assert!(debug_str.contains("AtomicSystemTime"));
383 }
384
385 #[test]
386 fn test_atomic_system_time_from() {
387 let now = SystemTime::now();
388 let atomic_time = AtomicSystemTime::from(now);
389 assert_eq!(atomic_time.load(Ordering::SeqCst), now);
390 }
391
392 #[test]
393 fn test_atomic_system_time_into_inner() {
394 let now = SystemTime::now();
395 let atomic_time = AtomicSystemTime::new(now);
396 assert_eq!(atomic_time.into_inner(), now);
397 }
398
399 #[test]
400 fn test_atomic_system_time_compare_exchange_failure() {
401 let now = SystemTime::now();
402 let other = now + Duration::from_secs(5);
403 let atomic_time = AtomicSystemTime::new(now);
404 let result = atomic_time.compare_exchange(other, other, Ordering::SeqCst, Ordering::SeqCst);
405 assert!(result.is_err());
406 assert_eq!(result.unwrap_err(), now);
407 }
408
409 #[test]
410 fn test_atomic_system_time_compare_exchange_weak_failure() {
411 let now = SystemTime::now();
412 let other = now + Duration::from_secs(5);
413 let atomic_time = AtomicSystemTime::new(now);
414 let result =
415 atomic_time.compare_exchange_weak(other, other, Ordering::SeqCst, Ordering::SeqCst);
416 assert!(result.is_err());
417 assert_eq!(result.unwrap_err(), now);
418 }
419
420 #[cfg(feature = "serde")]
421 #[test]
422 fn test_atomic_system_time_serde() {
423 use serde::{Deserialize, Serialize};
424
425 #[derive(Serialize, Deserialize)]
426 struct Test {
427 time: AtomicSystemTime,
428 }
429
430 let now = SystemTime::now();
431 let test = Test {
432 time: AtomicSystemTime::new(now),
433 };
434 let serialized = serde_json::to_string(&test).unwrap();
435 let deserialized: Test = serde_json::from_str(&serialized).unwrap();
436 assert_eq!(deserialized.time.load(Ordering::SeqCst), now,);
437 }
438}