1use core::sync::atomic::Ordering;
2use std::time::SystemTime;
3
4use crate::AtomicOptionDuration;
5
6#[repr(transparent)]
8pub struct AtomicOptionSystemTime(AtomicOptionDuration);
9
10impl core::fmt::Debug for AtomicOptionSystemTime {
11 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
12 f.debug_tuple("AtomicOptionSystemTime")
13 .field(&self.load(Ordering::SeqCst))
14 .finish()
15 }
16}
17impl Default for AtomicOptionSystemTime {
18 #[cfg_attr(not(tarpaulin), inline(always))]
20 fn default() -> Self {
21 Self::none()
22 }
23}
24impl From<Option<SystemTime>> for AtomicOptionSystemTime {
25 #[cfg_attr(not(tarpaulin), inline(always))]
29 fn from(system_time: Option<SystemTime>) -> Self {
30 Self::new(system_time)
31 }
32}
33
34impl AtomicOptionSystemTime {
35 #[cfg_attr(not(tarpaulin), inline(always))]
46 pub const fn none() -> Self {
47 Self(AtomicOptionDuration::new(None))
48 }
49
50 #[cfg_attr(not(tarpaulin), inline(always))]
59 pub fn now() -> Self {
60 Self::new(Some(SystemTime::now()))
61 }
62
63 #[cfg_attr(not(tarpaulin), inline(always))]
69 pub fn new(system_time: Option<SystemTime>) -> Self {
70 Self(AtomicOptionDuration::new(
71 system_time.map(|d| d.duration_since(SystemTime::UNIX_EPOCH).unwrap()),
72 ))
73 }
74
75 #[cfg_attr(not(tarpaulin), inline(always))]
77 pub fn load(&self, order: Ordering) -> Option<SystemTime> {
78 self.0.load(order).map(|val| SystemTime::UNIX_EPOCH + val)
79 }
80
81 #[cfg_attr(not(tarpaulin), inline(always))]
87 pub fn store(&self, system_time: Option<SystemTime>, order: Ordering) {
88 self.0.store(
89 system_time.map(|val| val.duration_since(SystemTime::UNIX_EPOCH).unwrap()),
90 order,
91 )
92 }
93
94 #[cfg_attr(not(tarpaulin), inline(always))]
100 pub fn swap(&self, system_time: Option<SystemTime>, order: Ordering) -> Option<SystemTime> {
101 self
102 .0
103 .swap(
104 system_time.map(|val| val.duration_since(SystemTime::UNIX_EPOCH).unwrap()),
105 order,
106 )
107 .map(|val| SystemTime::UNIX_EPOCH + val)
108 }
109
110 #[cfg_attr(not(tarpaulin), inline(always))]
117 pub fn compare_exchange(
118 &self,
119 current: Option<SystemTime>,
120 new: Option<SystemTime>,
121 success: Ordering,
122 failure: Ordering,
123 ) -> Result<Option<SystemTime>, Option<SystemTime>> {
124 match self.0.compare_exchange(
125 current.map(|d| d.duration_since(SystemTime::UNIX_EPOCH).unwrap()),
126 new.map(|d| d.duration_since(SystemTime::UNIX_EPOCH).unwrap()),
127 success,
128 failure,
129 ) {
130 Ok(duration) => Ok(duration.map(|d| SystemTime::UNIX_EPOCH + d)),
131 Err(duration) => Err(duration.map(|d| SystemTime::UNIX_EPOCH + d)),
132 }
133 }
134
135 #[cfg_attr(not(tarpaulin), inline(always))]
142 pub fn compare_exchange_weak(
143 &self,
144 current: Option<SystemTime>,
145 new: Option<SystemTime>,
146 success: Ordering,
147 failure: Ordering,
148 ) -> Result<Option<SystemTime>, Option<SystemTime>> {
149 match self.0.compare_exchange_weak(
150 current.map(|d| d.duration_since(SystemTime::UNIX_EPOCH).unwrap()),
151 new.map(|d| d.duration_since(SystemTime::UNIX_EPOCH).unwrap()),
152 success,
153 failure,
154 ) {
155 Ok(duration) => Ok(duration.map(|d| SystemTime::UNIX_EPOCH + d)),
156 Err(duration) => Err(duration.map(|d| SystemTime::UNIX_EPOCH + d)),
157 }
158 }
159
160 #[cfg_attr(not(tarpaulin), inline(always))]
200 pub fn fetch_update<F>(
201 &self,
202 set_order: Ordering,
203 fetch_order: Ordering,
204 mut f: F,
205 ) -> Result<Option<SystemTime>, Option<SystemTime>>
206 where
207 F: FnMut(Option<SystemTime>) -> Option<Option<SystemTime>>,
208 {
209 self
210 .0
211 .fetch_update(set_order, fetch_order, |duration| {
212 f(duration.map(|d| SystemTime::UNIX_EPOCH + d))
213 .map(|system_time| system_time.map(|d| d.duration_since(SystemTime::UNIX_EPOCH).unwrap()))
214 })
215 .map(|duration| duration.map(|d| SystemTime::UNIX_EPOCH + d))
216 .map_err(|duration| duration.map(|d| SystemTime::UNIX_EPOCH + d))
217 }
218
219 #[cfg_attr(not(tarpaulin), inline(always))]
231 pub fn is_lock_free() -> bool {
232 AtomicOptionDuration::is_lock_free()
233 }
234
235 #[cfg_attr(not(tarpaulin), inline(always))]
240 pub fn into_inner(self) -> Option<SystemTime> {
241 self.0.into_inner().map(|d| SystemTime::UNIX_EPOCH + d)
242 }
243}
244
245#[cfg(feature = "serde")]
246const _: () = {
247 use serde::{Deserialize, Serialize};
248
249 impl Serialize for AtomicOptionSystemTime {
250 fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
251 self.load(Ordering::SeqCst).serialize(serializer)
252 }
253 }
254
255 impl<'de> Deserialize<'de> for AtomicOptionSystemTime {
256 fn deserialize<D: serde::Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
257 Ok(Self::new(Option::<SystemTime>::deserialize(deserializer)?))
258 }
259 }
260};
261
262#[cfg(test)]
263mod tests {
264 use super::*;
265 use std::time::Duration;
266
267 #[test]
268 fn test_atomic_option_system_time_now() {
269 let atomic_time = AtomicOptionSystemTime::now();
270 assert!(atomic_time.load(Ordering::SeqCst).is_some());
272 }
273
274 #[test]
275 fn test_atomic_option_system_time_none() {
276 let atomic_time = AtomicOptionSystemTime::none();
277 assert_eq!(atomic_time.load(Ordering::SeqCst), None);
278 }
279
280 #[test]
281 fn test_atomic_option_system_time_new_and_load() {
282 let now = SystemTime::now();
283 let atomic_time = AtomicOptionSystemTime::new(Some(now));
284 assert_eq!(atomic_time.load(Ordering::SeqCst), Some(now));
285 }
286
287 #[test]
288 fn test_atomic_option_system_time_store_and_load() {
289 let now = SystemTime::now();
290 let after_one_sec = now + Duration::from_secs(1);
291 let atomic_time = AtomicOptionSystemTime::new(Some(now));
292 atomic_time.store(Some(after_one_sec), Ordering::SeqCst);
293 assert_eq!(atomic_time.load(Ordering::SeqCst), Some(after_one_sec));
294 }
295
296 #[test]
297 fn test_atomic_option_system_time_swap() {
298 let now = SystemTime::now();
299 let after_one_sec = now + Duration::from_secs(1);
300 let atomic_time = AtomicOptionSystemTime::new(Some(now));
301 let prev_time = atomic_time.swap(Some(after_one_sec), Ordering::SeqCst);
302 assert_eq!(prev_time, Some(now));
303 assert_eq!(atomic_time.load(Ordering::SeqCst), Some(after_one_sec));
304 }
305
306 #[test]
307 fn test_atomic_option_system_time_compare_exchange() {
308 let now = SystemTime::now();
309 let after_one_sec = now + Duration::from_secs(1);
310 let atomic_time = AtomicOptionSystemTime::new(Some(now));
311 let result = atomic_time.compare_exchange(
312 Some(now),
313 Some(after_one_sec),
314 Ordering::SeqCst,
315 Ordering::SeqCst,
316 );
317 assert!(result.is_ok());
318 assert_eq!(atomic_time.load(Ordering::SeqCst), Some(after_one_sec));
319 }
320
321 #[test]
322 fn test_atomic_option_system_time_compare_exchange_weak() {
323 let now = SystemTime::now();
324 let after_one_sec = now + Duration::from_secs(1);
325 let atomic_time = AtomicOptionSystemTime::new(Some(now));
326
327 let mut result;
328 loop {
329 result = atomic_time.compare_exchange_weak(
330 Some(now),
331 Some(after_one_sec),
332 Ordering::SeqCst,
333 Ordering::SeqCst,
334 );
335 if result.is_ok() {
336 break;
337 }
338 }
339 assert!(result.is_ok());
340 assert_eq!(atomic_time.load(Ordering::SeqCst), Some(after_one_sec));
341 }
342
343 #[test]
344 fn test_atomic_option_system_time_fetch_update() {
345 let now = SystemTime::now();
346 let atomic_time = AtomicOptionSystemTime::new(Some(now));
347
348 let result = atomic_time.fetch_update(Ordering::SeqCst, Ordering::SeqCst, |prev| {
349 Some(prev.map(|val| val + Duration::from_secs(1)))
350 });
351 assert!(result.is_ok());
352 assert_eq!(result.unwrap(), Some(now));
353 assert_eq!(
354 atomic_time.load(Ordering::SeqCst),
355 Some(now + Duration::from_secs(1))
356 );
357 }
358
359 #[test]
360 fn test_atomic_option_system_time_thread_safety() {
361 use std::sync::Arc;
362 use std::thread;
363
364 let start = SystemTime::now();
369 let atomic_time = Arc::new(AtomicOptionSystemTime::new(Some(start)));
370 let mut handles = vec![];
371
372 for _ in 0..4 {
373 let atomic_clone = Arc::clone(&atomic_time);
374 let handle = thread::spawn(move || {
375 atomic_clone
376 .fetch_update(Ordering::SeqCst, Ordering::SeqCst, |current| {
377 current.map(|t| Some(t + Duration::from_secs(1)))
378 })
379 .expect("atomic is always Some in this test");
380 });
381 handles.push(handle);
382 }
383
384 for handle in handles {
385 handle.join().unwrap();
386 }
387
388 assert_eq!(
390 atomic_time.load(Ordering::SeqCst),
391 Some(start + Duration::from_secs(4))
392 );
393 }
394
395 #[test]
396 fn test_atomic_option_system_time_debug() {
397 let atomic_time = AtomicOptionSystemTime::now();
398 let debug_str = format!("{:?}", atomic_time);
399 assert!(debug_str.contains("AtomicOptionSystemTime"));
400 }
401
402 #[test]
403 fn test_atomic_option_system_time_default() {
404 let atomic_time = AtomicOptionSystemTime::default();
405 assert_eq!(atomic_time.load(Ordering::SeqCst), None);
406 }
407
408 #[test]
409 fn test_atomic_option_system_time_from() {
410 let now = Some(SystemTime::now());
411 let atomic_time = AtomicOptionSystemTime::from(now);
412 assert_eq!(atomic_time.load(Ordering::SeqCst), now);
413 }
414
415 #[test]
416 fn test_atomic_option_system_time_from_none() {
417 let atomic_time = AtomicOptionSystemTime::from(None);
418 assert_eq!(atomic_time.load(Ordering::SeqCst), None);
419 }
420
421 #[test]
422 fn test_atomic_option_system_time_into_inner() {
423 let now = Some(SystemTime::now());
424 let atomic_time = AtomicOptionSystemTime::new(now);
425 assert_eq!(atomic_time.into_inner(), now);
426 }
427
428 #[test]
429 fn test_atomic_option_system_time_into_inner_none() {
430 let atomic_time = AtomicOptionSystemTime::none();
431 assert_eq!(atomic_time.into_inner(), None);
432 }
433
434 #[test]
435 fn test_atomic_option_system_time_compare_exchange_failure() {
436 let now = Some(SystemTime::now());
437 let other = now.map(|t| t + Duration::from_secs(5));
438 let atomic_time = AtomicOptionSystemTime::new(now);
439 let result = atomic_time.compare_exchange(other, other, Ordering::SeqCst, Ordering::SeqCst);
440 assert!(result.is_err());
441 assert_eq!(result.unwrap_err(), now);
442 }
443
444 #[test]
445 fn test_atomic_option_system_time_compare_exchange_weak_failure() {
446 let now = Some(SystemTime::now());
447 let other = now.map(|t| t + Duration::from_secs(5));
448 let atomic_time = AtomicOptionSystemTime::new(now);
449 let result =
450 atomic_time.compare_exchange_weak(other, other, Ordering::SeqCst, Ordering::SeqCst);
451 assert!(result.is_err());
452 }
453
454 #[test]
455 fn test_atomic_option_system_time_fetch_update_failure() {
456 let now = Some(SystemTime::now());
457 let atomic_time = AtomicOptionSystemTime::new(now);
458 let result = atomic_time.fetch_update(Ordering::SeqCst, Ordering::SeqCst, |_| None);
459 assert!(result.is_err());
460 assert_eq!(result.unwrap_err(), now);
461 }
462
463 #[cfg(feature = "serde")]
464 #[test]
465 fn test_atomic_system_time_serde() {
466 use serde::{Deserialize, Serialize};
467
468 #[derive(Serialize, Deserialize)]
469 struct Test {
470 time: AtomicOptionSystemTime,
471 }
472
473 let now = SystemTime::now();
474 let test = Test {
475 time: AtomicOptionSystemTime::new(Some(now)),
476 };
477 let serialized = serde_json::to_string(&test).unwrap();
478 let deserialized: Test = serde_json::from_str(&serialized).unwrap();
479 assert_eq!(deserialized.time.load(Ordering::SeqCst), Some(now));
480 }
481}