1use crate::AtomicU128;
2use core::{sync::atomic::Ordering, time::Duration};
3
4#[repr(transparent)]
6pub struct AtomicOptionDuration(AtomicU128);
7impl core::fmt::Debug for AtomicOptionDuration {
8 fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
9 f.debug_tuple("AtomicOptionDuration")
10 .field(&self.load(Ordering::SeqCst))
11 .finish()
12 }
13}
14impl Default for AtomicOptionDuration {
15 #[cfg_attr(not(tarpaulin), inline(always))]
17 fn default() -> Self {
18 Self::none()
19 }
20}
21impl From<Option<Duration>> for AtomicOptionDuration {
22 #[cfg_attr(not(tarpaulin), inline(always))]
23 fn from(duration: Option<Duration>) -> Self {
24 Self::new(duration)
25 }
26}
27impl AtomicOptionDuration {
28 #[cfg_attr(not(tarpaulin), inline(always))]
30 pub const fn none() -> Self {
31 Self(AtomicU128::new(encode_option_duration(None)))
32 }
33
34 #[cfg_attr(not(tarpaulin), inline(always))]
36 pub const fn new(duration: Option<Duration>) -> Self {
37 Self(AtomicU128::new(encode_option_duration(duration)))
38 }
39 #[cfg_attr(not(tarpaulin), inline(always))]
46 pub fn load(&self, ordering: Ordering) -> Option<Duration> {
47 decode_option_duration(self.0.load(ordering))
48 }
49 #[cfg_attr(not(tarpaulin), inline(always))]
58 pub fn store(&self, val: Option<Duration>, ordering: Ordering) {
59 self.0.store(encode_option_duration(val), ordering)
60 }
61 #[cfg_attr(not(tarpaulin), inline(always))]
66 pub fn swap(&self, val: Option<Duration>, ordering: Ordering) -> Option<Duration> {
67 decode_option_duration(self.0.swap(encode_option_duration(val), ordering))
68 }
69 #[cfg_attr(not(tarpaulin), inline(always))]
86 pub fn compare_exchange_weak(
87 &self,
88 current: Option<Duration>,
89 new: Option<Duration>,
90 success: Ordering,
91 failure: Ordering,
92 ) -> Result<Option<Duration>, Option<Duration>> {
93 self
94 .0
95 .compare_exchange_weak(
96 encode_option_duration(current),
97 encode_option_duration(new),
98 success,
99 failure,
100 )
101 .map(decode_option_duration)
102 .map_err(decode_option_duration)
103 }
104 #[cfg_attr(not(tarpaulin), inline(always))]
119 pub fn compare_exchange(
120 &self,
121 current: Option<Duration>,
122 new: Option<Duration>,
123 success: Ordering,
124 failure: Ordering,
125 ) -> Result<Option<Duration>, Option<Duration>> {
126 self
127 .0
128 .compare_exchange(
129 encode_option_duration(current),
130 encode_option_duration(new),
131 success,
132 failure,
133 )
134 .map(decode_option_duration)
135 .map_err(decode_option_duration)
136 }
137 #[cfg_attr(not(tarpaulin), inline(always))]
170 pub fn fetch_update<F>(
171 &self,
172 set_order: Ordering,
173 fetch_order: Ordering,
174 mut f: F,
175 ) -> Result<Option<Duration>, Option<Duration>>
176 where
177 F: FnMut(Option<Duration>) -> Option<Option<Duration>>,
178 {
179 self
180 .0
181 .fetch_update(set_order, fetch_order, |d| {
182 f(decode_option_duration(d)).map(encode_option_duration)
183 })
184 .map(decode_option_duration)
185 .map_err(decode_option_duration)
186 }
187 #[cfg_attr(not(tarpaulin), inline(always))]
192 pub fn into_inner(self) -> Option<Duration> {
193 decode_option_duration(self.0.into_inner())
194 }
195
196 #[cfg_attr(not(tarpaulin), inline(always))]
208 pub fn is_lock_free() -> bool {
209 AtomicU128::is_lock_free()
210 }
211}
212
213#[cfg_attr(not(tarpaulin), inline(always))]
215pub const fn encode_option_duration(option_duration: Option<Duration>) -> u128 {
216 match option_duration {
217 Some(duration) => {
218 let seconds = duration.as_secs() as u128;
219 let nanos = duration.subsec_nanos() as u128;
220 (1 << 127) | (seconds << 32) | nanos
221 }
222 None => 0,
223 }
224}
225
226#[cfg_attr(not(tarpaulin), inline(always))]
242pub const fn decode_option_duration(encoded: u128) -> Option<Duration> {
243 if encoded >> 127 == 0 {
244 None
245 } else {
246 let seconds = ((encoded << 1) >> 33) as u64;
247 let raw_nanos = (encoded & 0xFFFFFFFF) as u32;
248 let extra_secs = (raw_nanos / 1_000_000_000) as u64;
249 let nanos = raw_nanos % 1_000_000_000;
250 Some(match seconds.checked_add(extra_secs) {
251 Some(secs) => Duration::new(secs, nanos),
252 None => Duration::new(u64::MAX, 999_999_999),
253 })
254 }
255}
256
257#[cfg(feature = "serde")]
258const _: () = {
259 use serde::{Deserialize, Serialize};
260
261 impl Serialize for AtomicOptionDuration {
262 fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
263 self.load(Ordering::SeqCst).serialize(serializer)
264 }
265 }
266
267 impl<'de> Deserialize<'de> for AtomicOptionDuration {
268 fn deserialize<D: serde::Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
269 Ok(Self::new(Option::<Duration>::deserialize(deserializer)?))
270 }
271 }
272};
273
274#[cfg(test)]
275mod tests {
276 use super::*;
277
278 #[test]
279 fn test_new_atomic_option_duration() {
280 let duration = Duration::from_secs(5);
281 let atomic_duration = AtomicOptionDuration::new(Some(duration));
282 assert_eq!(atomic_duration.load(Ordering::SeqCst), Some(duration));
283 }
284
285 #[test]
286 fn test_atomic_option_duration_load() {
287 let duration = Duration::from_secs(10);
288 let atomic_duration = AtomicOptionDuration::new(Some(duration));
289 assert_eq!(atomic_duration.load(Ordering::SeqCst), Some(duration));
290 }
291
292 #[test]
293 fn test_atomic_option_duration_store() {
294 let initial_duration = Duration::from_secs(3);
295 let new_duration = Duration::from_secs(7);
296 let atomic_duration = AtomicOptionDuration::new(Some(initial_duration));
297 atomic_duration.store(Some(new_duration), Ordering::SeqCst);
298 assert_eq!(atomic_duration.load(Ordering::SeqCst), Some(new_duration));
299 }
300
301 #[test]
302 fn test_atomic_option_duration_store_none() {
303 let initial_duration = Duration::from_secs(3);
304 let atomic_duration = AtomicOptionDuration::new(Some(initial_duration));
305 atomic_duration.store(None, Ordering::SeqCst);
306 assert_eq!(atomic_duration.load(Ordering::SeqCst), None);
307 }
308
309 #[test]
310 fn test_atomic_option_duration_swap() {
311 let initial_duration = Duration::from_secs(2);
312 let new_duration = Duration::from_secs(8);
313 let atomic_duration = AtomicOptionDuration::new(Some(initial_duration));
314 let prev_duration = atomic_duration.swap(Some(new_duration), Ordering::SeqCst);
315 assert_eq!(prev_duration, Some(initial_duration));
316 assert_eq!(atomic_duration.load(Ordering::SeqCst), Some(new_duration));
317 }
318
319 #[test]
320 fn test_atomic_option_duration_compare_exchange_weak() {
321 let initial_duration = Duration::from_secs(4);
322 let atomic_duration = AtomicOptionDuration::new(Some(initial_duration));
323
324 let mut result;
326 loop {
327 result = atomic_duration.compare_exchange_weak(
328 Some(initial_duration),
329 Some(Duration::from_secs(6)),
330 Ordering::SeqCst,
331 Ordering::SeqCst,
332 );
333
334 if result.is_ok() {
335 break;
336 }
337 }
338
339 assert!(result.is_ok());
340 assert_eq!(result.unwrap(), Some(initial_duration));
341 assert_eq!(
342 atomic_duration.load(Ordering::SeqCst),
343 Some(Duration::from_secs(6))
344 );
345
346 let result = atomic_duration.compare_exchange_weak(
348 Some(initial_duration),
349 Some(Duration::from_secs(7)),
350 Ordering::SeqCst,
351 Ordering::SeqCst,
352 );
353 assert!(result.is_err());
354 assert_eq!(result.unwrap_err(), Some(Duration::from_secs(6)));
355 }
356
357 #[test]
358 fn test_atomic_option_duration_compare_exchange() {
359 let initial_duration = Duration::from_secs(1);
360 let atomic_duration = AtomicOptionDuration::new(Some(initial_duration));
361
362 let result = atomic_duration.compare_exchange(
364 Some(initial_duration),
365 Some(Duration::from_secs(5)),
366 Ordering::SeqCst,
367 Ordering::SeqCst,
368 );
369 assert!(result.is_ok());
370 assert_eq!(result.unwrap(), Some(initial_duration));
371 assert_eq!(
372 atomic_duration.load(Ordering::SeqCst),
373 Some(Duration::from_secs(5))
374 );
375
376 let result = atomic_duration.compare_exchange(
378 Some(initial_duration),
379 Some(Duration::from_secs(6)),
380 Ordering::SeqCst,
381 Ordering::SeqCst,
382 );
383 assert!(result.is_err());
384 assert_eq!(result.unwrap_err(), Some(Duration::from_secs(5)));
385 }
386
387 #[test]
388 fn test_atomic_option_duration_with_none_initially() {
389 let atomic_duration = AtomicOptionDuration::new(None);
390 assert_eq!(atomic_duration.load(Ordering::SeqCst), None);
391 }
392
393 #[test]
394 fn test_atomic_option_duration_store_none_and_then_value() {
395 let atomic_duration = AtomicOptionDuration::new(None);
396 atomic_duration.store(Some(Duration::from_secs(5)), Ordering::SeqCst);
397 assert_eq!(
398 atomic_duration.load(Ordering::SeqCst),
399 Some(Duration::from_secs(5))
400 );
401 }
402
403 #[test]
404 fn test_atomic_option_duration_swap_with_none() {
405 let initial_duration = Duration::from_secs(2);
406 let atomic_duration = AtomicOptionDuration::new(Some(initial_duration));
407 let prev_duration = atomic_duration.swap(None, Ordering::SeqCst);
408 assert_eq!(prev_duration, Some(initial_duration));
409 assert_eq!(atomic_duration.load(Ordering::SeqCst), None);
410 }
411
412 #[test]
413 fn test_atomic_option_duration_compare_exchange_weak_with_none() {
414 let initial_duration = Duration::from_secs(4);
415 let atomic_duration = AtomicOptionDuration::new(Some(initial_duration));
416
417 let mut result;
419
420 loop {
421 result = atomic_duration.compare_exchange_weak(
422 Some(initial_duration),
423 None,
424 Ordering::SeqCst,
425 Ordering::SeqCst,
426 );
427
428 if result.is_ok() {
429 break;
430 }
431 }
432
433 assert_eq!(atomic_duration.load(Ordering::SeqCst), None);
434
435 let new_duration = Duration::from_secs(6);
437 let mut result;
438
439 loop {
440 result = atomic_duration.compare_exchange_weak(
441 None,
442 Some(new_duration),
443 Ordering::SeqCst,
444 Ordering::SeqCst,
445 );
446 if result.is_ok() {
447 break;
448 }
449 }
450
451 assert_eq!(atomic_duration.load(Ordering::SeqCst), Some(new_duration));
452 }
453
454 #[test]
455 fn test_atomic_option_duration_compare_exchange_with_none() {
456 let initial_duration = Duration::from_secs(1);
457 let atomic_duration = AtomicOptionDuration::new(Some(initial_duration));
458
459 let result = atomic_duration.compare_exchange(
461 Some(initial_duration),
462 None,
463 Ordering::SeqCst,
464 Ordering::SeqCst,
465 );
466 assert!(result.is_ok());
467 assert_eq!(atomic_duration.load(Ordering::SeqCst), None);
468
469 let new_duration = Duration::from_secs(5);
471 let result = atomic_duration.compare_exchange(
472 None,
473 Some(new_duration),
474 Ordering::SeqCst,
475 Ordering::SeqCst,
476 );
477 assert!(result.is_ok());
478 assert_eq!(atomic_duration.load(Ordering::SeqCst), Some(new_duration));
479 }
480
481 #[test]
482 #[cfg(feature = "std")]
483 fn test_atomic_option_duration_thread_safety() {
484 use std::sync::Arc;
485 use std::thread;
486
487 let atomic_duration = Arc::new(AtomicOptionDuration::new(Some(Duration::from_secs(0))));
488 let mut handles = vec![];
489
490 for _ in 0..10 {
492 let atomic_clone = Arc::clone(&atomic_duration);
493 let handle = thread::spawn(move || {
494 for _ in 0..100 {
495 loop {
496 let current = atomic_clone.load(Ordering::SeqCst);
497 let new_duration = current
498 .map(|d| d + Duration::from_millis(1))
499 .or(Some(Duration::from_millis(1)));
500 match atomic_clone.compare_exchange_weak(
501 current,
502 new_duration,
503 Ordering::SeqCst,
504 Ordering::SeqCst,
505 ) {
506 Ok(_) => break, Err(_) => continue, }
509 }
510 }
511 });
512 handles.push(handle);
513 }
514
515 for handle in handles {
517 handle.join().unwrap();
518 }
519
520 let expected_duration = Some(Duration::from_millis(10 * 100));
522 assert_eq!(atomic_duration.load(Ordering::SeqCst), expected_duration);
523 }
524
525 #[cfg(feature = "std")]
526 #[test]
527 fn test_atomic_option_duration_debug() {
528 let atomic_duration = AtomicOptionDuration::new(Some(Duration::from_secs(1)));
529 let debug_str = format!("{:?}", atomic_duration);
530 assert!(debug_str.contains("AtomicOptionDuration"));
531 }
532
533 #[cfg(feature = "std")]
534 #[test]
535 fn test_atomic_option_duration_debug_none() {
536 let atomic_duration = AtomicOptionDuration::none();
537 let debug_str = format!("{:?}", atomic_duration);
538 assert!(debug_str.contains("AtomicOptionDuration"));
539 }
540
541 #[test]
542 fn test_atomic_option_duration_default() {
543 let atomic_duration = AtomicOptionDuration::default();
544 assert_eq!(atomic_duration.load(Ordering::SeqCst), None);
545 }
546
547 #[test]
548 fn test_atomic_option_duration_from() {
549 let duration = Some(Duration::from_secs(42));
550 let atomic_duration = AtomicOptionDuration::from(duration);
551 assert_eq!(atomic_duration.load(Ordering::SeqCst), duration);
552 }
553
554 #[test]
555 fn test_atomic_option_duration_from_none() {
556 let atomic_duration = AtomicOptionDuration::from(None);
557 assert_eq!(atomic_duration.load(Ordering::SeqCst), None);
558 }
559
560 #[test]
561 fn test_atomic_option_duration_into_inner() {
562 let duration = Some(Duration::from_secs(3));
563 let atomic_duration = AtomicOptionDuration::new(duration);
564 assert_eq!(atomic_duration.into_inner(), duration);
565 }
566
567 #[test]
568 fn test_atomic_option_duration_into_inner_none() {
569 let atomic_duration = AtomicOptionDuration::none();
570 assert_eq!(atomic_duration.into_inner(), None);
571 }
572
573 #[test]
574 fn test_atomic_option_duration_fetch_update() {
575 let initial = Some(Duration::from_secs(4));
576 let atomic_duration = AtomicOptionDuration::new(initial);
577
578 let result = atomic_duration.fetch_update(Ordering::SeqCst, Ordering::SeqCst, |d| {
579 Some(d.map(|val| val + Duration::from_secs(2)))
580 });
581 assert_eq!(result, Ok(initial));
582 assert_eq!(
583 atomic_duration.load(Ordering::SeqCst),
584 Some(Duration::from_secs(6))
585 );
586
587 let result = atomic_duration.fetch_update(Ordering::SeqCst, Ordering::SeqCst, |_| None);
589 assert!(result.is_err());
590 }
591
592 #[cfg(feature = "serde")]
593 #[test]
594 fn test_atomic_option_duration_serde() {
595 use serde::{Deserialize, Serialize};
596
597 #[derive(Serialize, Deserialize)]
598 struct Test {
599 duration: AtomicOptionDuration,
600 }
601
602 let test = Test {
603 duration: AtomicOptionDuration::new(Some(Duration::from_secs(5))),
604 };
605 let serialized = serde_json::to_string(&test).unwrap();
606 let deserialized: Test = serde_json::from_str(&serialized).unwrap();
607 assert_eq!(
608 deserialized.duration.load(Ordering::SeqCst),
609 Some(Duration::from_secs(5))
610 );
611
612 let test = Test {
613 duration: AtomicOptionDuration::new(None),
614 };
615 let serialized = serde_json::to_string(&test).unwrap();
616 let deserialized: Test = serde_json::from_str(&serialized).unwrap();
617 assert_eq!(deserialized.duration.load(Ordering::SeqCst), None);
618 }
619
620 #[test]
621 fn decode_option_duration_roundtrip() {
622 let cases: [Option<Duration>; 4] = [
623 None,
624 Some(Duration::ZERO),
625 Some(Duration::from_secs(1)),
626 Some(Duration::new(123_456_789, 999_999_999)),
627 ];
628 for d in cases {
629 assert_eq!(decode_option_duration(encode_option_duration(d)), d);
630 }
631 }
632
633 #[test]
634 fn decode_option_duration_saturates_on_non_canonical_input() {
635 let max = decode_option_duration(u128::MAX);
639 assert_eq!(max, Some(Duration::new(u64::MAX, 999_999_999)));
640
641 assert_eq!(decode_option_duration(0), None);
644
645 assert_eq!(decode_option_duration(1u128 << 127), Some(Duration::ZERO));
647 }
648}