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}