boot_time/
time.rs

1//! Reimplementation of `std::time::Instant` for supported platforms
2use core::time::Duration;
3use std::fmt;
4use std::ops::{Add, AddAssign, Sub, SubAssign};
5
6use crate::sys;
7
8/// A measurement of a suspend-aware monotonically nondecreasing clock.
9/// Opaque and useful only with [`Duration`].
10///
11/// Instants are always guaranteed, barring [platform bugs], to be no less than any previously
12/// measured instant when created, and are often useful for tasks such as measuring
13/// benchmarks or timing how long an operation takes.
14///
15/// Note, however, that instants are **not** guaranteed to be **steady**. In other
16/// words, each tick of the underlying clock might not be the same length (e.g.
17/// some seconds may be longer than others). An instant may jump forwards or
18/// experience time dilation (slow down or speed up), but it will never go
19/// backwards.
20///
21/// Instants are opaque types that can only be compared to one another. There is
22/// no method to get "the number of seconds" from an instant. Instead, it only
23/// allows measuring the duration between two instants (or comparing two
24/// instants).
25///
26/// The size of an `Instant` struct may vary depending on the target operating
27/// system.
28///
29/// Example:
30///
31/// ```no_run
32/// use boot_time::{Duration, Instant};
33/// use std::thread::sleep;
34///
35/// fn main() {
36///    let now = Instant::now();
37///
38///    // we sleep for 2 seconds
39///    sleep(Duration::new(2, 0));
40///    // it prints '2'
41///    println!("{}", now.elapsed().as_secs());
42/// }
43/// ```
44///
45/// [platform bugs]: Instant#monotonicity
46///
47/// # OS-specific behaviors
48///
49/// An `Instant` is a wrapper around system-specific types and it may behave
50/// differently depending on the underlying operating system. For example,
51/// the following snippet is fine on Linux but panics on macOS:
52///
53/// ```no_run
54/// use std::time::{Instant, Duration};
55///
56/// let now = Instant::now();
57/// let max_seconds = u64::MAX / 1_000_000_000;
58/// let duration = Duration::new(max_seconds, 0);
59/// println!("{:?}", now + duration);
60/// ```
61///
62/// # Underlying System calls
63///
64/// The following system calls are [currently] being used by `now()` to find out
65/// the current time:
66///
67/// |  Platform                         |               System call                                            |
68/// |-----------------------------------|----------------------------------------------------------------------|
69/// | UNIX (Linux/L4Re/Android/openBSD) | [clock_gettime (Monotonic Clock with CLOCK_BOOTTIME source)]         |
70/// | UNIX (other)                      | [clock_gettime (Monotonic Clock with CLOCK_MONOTONIC source)]        |
71/// | Darwin                            | [mach_continuous_time]                                               |
72/// | Windows                           | [QueryPerformanceCounter]                                                                    |
73///
74/// [currently]: crate::io#platform-specific-behavior
75/// [QueryPerformanceCounter]: https://docs.microsoft.com/en-us/windows/win32/api/profileapi/nf-profileapi-queryperformancecounter
76/// [clock_gettime (Monotonic Clock)]: https://linux.die.net/man/3/clock_gettime
77/// FreeBSD doesn't take into account suspended time in CLOCK_BOOTIME. So CLOCK_MONOTONIC is used.
78/// [mach_absolute_time]: https://developer.apple.com/documentation/kernel/1646199-mach_continuous_time
79///
80/// **Disclaimer:** These system calls might change over time.
81///
82/// > Note: mathematical operations like [`add`] may panic if the underlying
83/// > structure cannot represent the new point in time.
84///
85/// [`add`]: Instant::add
86///
87/// ## Monotonicity
88///
89/// On all platforms `Instant` will try to use an OS API that guarantees monotonic behavior
90/// if available, which is the case for all [tier 1] platforms.
91/// In practice such guarantees are – under rare circumstances – broken by hardware, virtualization
92/// or operating system bugs. To work around these bugs and platforms not offering monotonic clocks
93/// [`duration_since`], [`elapsed`] and [`sub`] saturate to zero. In older Rust versions this
94/// lead to a panic instead. [`checked_duration_since`] can be used to detect and handle situations
95/// where monotonicity is violated, or `Instant`s are subtracted in the wrong order.
96///
97/// This workaround obscures programming errors where earlier and later instants are accidentally
98/// swapped. For this reason future rust versions may reintroduce panics.
99///
100/// [tier 1]: https://doc.rust-lang.org/rustc/platform-support.html
101/// [`duration_since`]: Instant::duration_since
102/// [`elapsed`]: Instant::elapsed
103/// [`sub`]: Instant::sub
104/// [`checked_duration_since`]: Instant::checked_duration_since
105///
106#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
107pub struct Instant(sys::Instant);
108
109impl Instant {
110    /// Returns an instant corresponding to "now".
111    ///
112    /// # Examples
113    ///
114    /// ```
115    /// use boot_time::Instant;
116    ///
117    /// let now = Instant::now();
118    /// ```
119    #[must_use]
120    pub fn now() -> Instant {
121        Instant(sys::Instant::now())
122    }
123
124    /// Returns the amount of time elapsed from another instant to this one,
125    /// or zero duration if that instant is later than this one.
126    ///
127    /// # Panics
128    ///
129    /// Previous rust versions panicked when `earlier` was later than `self`. Currently this
130    /// method saturates. Future versions may reintroduce the panic in some circumstances.
131    /// See [Monotonicity].
132    ///
133    /// [Monotonicity]: Instant#monotonicity
134    ///
135    /// # Examples
136    ///
137    /// ```no_run
138    /// use boot_time::{Duration, Instant};
139    /// use std::thread::sleep;
140    ///
141    /// let now = Instant::now();
142    /// sleep(Duration::new(1, 0));
143    /// let new_now = Instant::now();
144    /// println!("{:?}", new_now.duration_since(now));
145    /// println!("{:?}", now.duration_since(new_now)); // 0ns
146    /// ```
147    #[must_use]
148    pub fn duration_since(&self, earlier: Instant) -> Duration {
149        self.checked_duration_since(earlier).unwrap_or_default()
150    }
151
152    /// Returns the amount of time elapsed from another instant to this one,
153    /// or None if that instant is later than this one.
154    ///
155    /// Due to [monotonicity bugs], even under correct logical ordering of the passed `Instant`s,
156    /// this method can return `None`.
157    ///
158    /// [monotonicity bugs]: Instant#monotonicity
159    ///
160    /// # Examples
161    ///
162    /// ```no_run
163    /// use boot_time::{Duration, Instant};
164    /// use std::thread::sleep;
165    ///
166    /// let now = Instant::now();
167    /// sleep(Duration::new(1, 0));
168    /// let new_now = Instant::now();
169    /// println!("{:?}", new_now.checked_duration_since(now));
170    /// println!("{:?}", now.checked_duration_since(new_now)); // None
171    /// ```
172    #[must_use]
173    pub fn checked_duration_since(&self, earlier: Instant) -> Option<Duration> {
174        self.0.checked_sub_instant(&earlier.0)
175    }
176
177    /// Returns the amount of time elapsed from another instant to this one,
178    /// or zero duration if that instant is later than this one.
179    ///
180    /// # Examples
181    ///
182    /// ```no_run
183    /// use boot_time::{Duration, Instant};
184    /// use std::thread::sleep;
185    ///
186    /// let now = Instant::now();
187    /// sleep(Duration::new(1, 0));
188    /// let new_now = Instant::now();
189    /// println!("{:?}", new_now.saturating_duration_since(now));
190    /// println!("{:?}", now.saturating_duration_since(new_now)); // 0ns
191    /// ```
192    #[must_use]
193    pub fn saturating_duration_since(&self, earlier: Instant) -> Duration {
194        self.checked_duration_since(earlier).unwrap_or_default()
195    }
196
197    /// Returns the amount of time elapsed since this instant.
198    ///
199    /// # Panics
200    ///
201    /// Previous rust versions panicked when the current time was earlier than self. Currently this
202    /// method returns a Duration of zero in that case. Future versions may reintroduce the panic.
203    /// See [Monotonicity].
204    ///
205    /// [Monotonicity]: Instant#monotonicity
206    ///
207    /// # Examples
208    ///
209    /// ```no_run
210    /// use std::thread::sleep;
211    /// use boot_time::{Duration, Instant};
212    ///
213    /// let instant = Instant::now();
214    /// let three_secs = Duration::from_secs(3);
215    /// sleep(three_secs);
216    /// assert!(instant.elapsed() >= three_secs);
217    /// ```
218    #[must_use]
219    pub fn elapsed(&self) -> Duration {
220        Instant::now() - *self
221    }
222
223    /// Returns `Some(t)` where `t` is the time `self + duration` if `t` can be represented as
224    /// `Instant` (which means it's inside the bounds of the underlying data structure), `None`
225    /// otherwise.
226    pub fn checked_add(&self, duration: Duration) -> Option<Instant> {
227        self.0.checked_add_duration(&duration).map(Instant)
228    }
229
230    /// Returns `Some(t)` where `t` is the time `self - duration` if `t` can be represented as
231    /// `Instant` (which means it's inside the bounds of the underlying data structure), `None`
232    /// otherwise.
233    pub fn checked_sub(&self, duration: Duration) -> Option<Instant> {
234        self.0.checked_sub_duration(&duration).map(Instant)
235    }
236}
237
238impl Add<Duration> for Instant {
239    type Output = Instant;
240
241    /// # Panics
242    ///
243    /// This function may panic if the resulting point in time cannot be represented by the
244    /// underlying data structure. See [`Instant::checked_add`] for a version without panic.
245    fn add(self, other: Duration) -> Instant {
246        self.checked_add(other)
247            .expect("overflow when adding duration to instant")
248    }
249}
250
251impl AddAssign<Duration> for Instant {
252    fn add_assign(&mut self, other: Duration) {
253        *self = *self + other;
254    }
255}
256
257impl Sub<Duration> for Instant {
258    type Output = Instant;
259
260    fn sub(self, other: Duration) -> Instant {
261        self.checked_sub(other)
262            .expect("overflow when subtracting duration from instant")
263    }
264}
265
266impl SubAssign<Duration> for Instant {
267    fn sub_assign(&mut self, other: Duration) {
268        *self = *self - other;
269    }
270}
271
272impl Sub<Instant> for Instant {
273    type Output = Duration;
274
275    /// Returns the amount of time elapsed from another instant to this one,
276    /// or zero duration if that instant is later than this one.
277    ///
278    /// # Panics
279    ///
280    /// Previous rust versions panicked when `other` was later than `self`. Currently this
281    /// method saturates. Future versions may reintroduce the panic in some circumstances.
282    /// See [Monotonicity].
283    ///
284    /// [Monotonicity]: Instant#monotonicity
285    fn sub(self, other: Instant) -> Duration {
286        self.duration_since(other)
287    }
288}
289
290impl fmt::Debug for Instant {
291    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
292        self.0.fmt(f)
293    }
294}