Skip to main content

atomic_time/
lib.rs

1//! Atomic time types.
2//!
3//! All types are thread-safe and use `portable_atomic::AtomicU128` under the
4//! hood. Operations are lock-free on platforms with native `AtomicU128`
5//! support; otherwise, `portable-atomic` may fall back to a global lock. Each
6//! type exposes an `is_lock_free` method to query the behavior of the current
7//! target.
8//!
9//! The `std` feature controls the `SystemTime` and `Instant` types. Disable it
10//! for `no_std` builds to use the duration types without the standard library.
11//!
12//! The optional `arbitrary`, `quickcheck`, and `proptest` features provide
13//! fuzzing and property-testing support for the duration and system-time
14//! atomic types. All three require `std`: the current upstream `arbitrary` v1
15//! crate itself requires `std`. `quickcheck` additionally implements [`Clone`]
16//! as a `SeqCst` snapshot: when another thread writes concurrently, the clone
17//! is the value observed at its atomic linearization point.
18//!
19//! [`AtomicInstant`] and [`AtomicOptionInstant`] intentionally do not implement
20//! these generation traits. Their values use a process-local baseline and
21//! cannot be generated from a deterministic, portable cross-run corpus.
22#![cfg_attr(not(feature = "std"), no_std)]
23#![cfg_attr(docsrs, feature(doc_cfg))]
24#![cfg_attr(docsrs, allow(unused_attributes))]
25#![deny(missing_docs)]
26#![forbid(unsafe_code)]
27
28pub use core::sync::atomic::Ordering;
29
30use portable_atomic::AtomicU128;
31
32mod duration;
33pub use duration::AtomicDuration;
34mod option_duration;
35pub use option_duration::AtomicOptionDuration;
36
37/// Utility functions for encoding and decoding [`core::time::Duration`] values.
38pub mod utils {
39  #[cfg(feature = "std")]
40  use std::time::{Duration, Instant, SystemTime};
41
42  #[cfg(feature = "std")]
43  fn init() -> (Duration, Instant) {
44    static ONCE: std::sync::OnceLock<(Duration, Instant)> = std::sync::OnceLock::new();
45
46    *ONCE.get_or_init(|| {
47      let epoch_dur = SystemTime::now()
48        .duration_since(SystemTime::UNIX_EPOCH)
49        .unwrap();
50      let instant_now = Instant::now();
51      (epoch_dur, instant_now)
52    })
53  }
54
55  /// Encodes an [`Instant`] into a [`Duration`] using a process-local baseline.
56  ///
57  /// The mapping is exact for instants in the same process that are representable
58  /// relative to that baseline. The baseline pairs a wall-clock duration with a
59  /// monotonic instant once; wall-clock adjustments made afterwards do not change
60  /// the mapping. Whether time advances while the machine sleeps inherits the
61  /// platform behavior of [`Instant`].
62  ///
63  /// This encoding is not a durable deadline format. Values decoded in another
64  /// process or after a restart are only wall-clock approximations.
65  ///
66  /// # Panics
67  ///
68  /// Panics if `instant` cannot be represented as a duration relative to the
69  /// process-local baseline.
70  #[cfg(feature = "std")]
71  #[cfg_attr(docsrs, doc(cfg(feature = "std")))]
72  #[inline(always)]
73  pub fn encode_instant_to_duration(instant: Instant) -> Duration {
74    let (epoch_dur, instant_now) = init();
75    if instant <= instant_now {
76      epoch_dur - (instant_now - instant)
77    } else {
78      epoch_dur + (instant - instant_now)
79    }
80  }
81
82  /// Decodes an [`Instant`] from a [`Duration`] using the process-local baseline.
83  ///
84  /// The mapping is exact for values encoded by this process within the platform's
85  /// representable `Instant` range. Values outside that range fall back to the
86  /// baseline instant; this is a fallback, not saturation. The behavior during
87  /// system sleep inherits the platform behavior of [`Instant`]. Encodings from
88  /// another process or a previous run are only wall-clock approximations and are
89  /// not durable deadlines.
90  #[cfg(feature = "std")]
91  #[cfg_attr(docsrs, doc(cfg(feature = "std")))]
92  #[inline(always)]
93  pub fn decode_instant_from_duration(duration: Duration) -> Instant {
94    let (epoch_dur, instant_now) = init();
95    if duration >= epoch_dur {
96      let delta = duration - epoch_dur;
97      instant_now.checked_add(delta).unwrap_or(instant_now)
98    } else {
99      let delta = epoch_dur - duration;
100      instant_now.checked_sub(delta).unwrap_or(instant_now)
101    }
102  }
103
104  pub use super::duration::{decode_duration, encode_duration};
105  pub use super::option_duration::{decode_option_duration, encode_option_duration};
106}
107
108#[cfg(feature = "std")]
109mod system_time;
110
111#[cfg(feature = "std")]
112#[cfg_attr(docsrs, doc(cfg(feature = "std")))]
113pub use system_time::AtomicSystemTime;
114
115#[cfg(feature = "std")]
116mod option_system_time;
117#[cfg(feature = "std")]
118#[cfg_attr(docsrs, doc(cfg(feature = "std")))]
119pub use option_system_time::AtomicOptionSystemTime;
120
121#[cfg(feature = "std")]
122mod instant;
123#[cfg(feature = "std")]
124#[cfg_attr(docsrs, doc(cfg(feature = "std")))]
125pub use instant::AtomicInstant;
126
127#[cfg(feature = "std")]
128mod option_instant;
129#[cfg(feature = "std")]
130#[cfg_attr(docsrs, doc(cfg(feature = "std")))]
131pub use option_instant::AtomicOptionInstant;
132
133#[cfg(feature = "std")]
134use utils::{decode_instant_from_duration, encode_instant_to_duration};
135
136#[cfg(feature = "arbitrary")]
137mod arbitrary;
138
139#[cfg(feature = "quickcheck")]
140mod quickcheck;
141
142#[cfg(feature = "proptest")]
143mod proptest;