rpkt_time/instant.rs
1// This file is taken from minstant at https://github.com/tikv/minstant and modified
2// for compatibility with the code in lib.rs.
3
4// Copyright 2021 TiKV Project Authors. Licensed under Apache-2.0.
5
6use crate::tsc;
7
8use std::{
9 ops::{Add, AddAssign, Sub, SubAssign},
10 time::{Duration, SystemTime, UNIX_EPOCH},
11};
12
13/// A measurement of a monotonically nondecreasing clock. Similar to
14/// [`std::time::Instant`](std::time::Instant) but is faster and more
15/// accurate with stable TSC.
16#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
17pub struct Instant(u64);
18
19impl Instant {
20 /// Returns the raw value of the tsc counter.
21 ///
22 /// # Examples
23 /// ```
24 /// use rdtsc_time::Instant;
25 ///
26 /// let ddl = Instant::now().raw().checked_add(rdtsc_time::cycles_per_sec()).unwrap();
27 /// while Instant::now().raw() <= ddl {}
28 ///
29 /// println!("1s has passed");
30 /// ```
31 #[inline]
32 pub fn raw(&self) -> u64 {
33 self.0
34 }
35
36 /// Returns an instant corresponding to "now".
37 ///
38 /// # Examples
39 ///
40 /// ```
41 /// use minstant::Instant;
42 ///
43 /// let now = Instant::now();
44 /// ```
45 #[inline]
46 pub fn now() -> Instant {
47 Instant(tsc::tsc_from_ref())
48 }
49
50 /// Returns the amount of time elapsed from another instant to this one,
51 /// or zero duration if that instant is later than this one.
52 ///
53 /// # Panics
54 ///
55 /// Previously we panicked if `earlier` was later than `self`. Currently this method saturates
56 /// to follow the behavior of the standard library. Future versions may reintroduce the panic
57 /// in some circumstances.
58 ///
59 /// # Examples
60 ///
61 /// ```
62 /// use std::time::Duration;
63 /// use std::thread::sleep;
64 ///
65 /// use minstant::Instant;
66 ///
67 /// let now = Instant::now();
68 /// sleep(Duration::new(1, 0));
69 /// let new_now = Instant::now();
70 /// println!("{:?}", new_now.duration_since(now));
71 /// println!("{:?}", now.duration_since(new_now)); // 0ns
72 /// ```
73 #[inline]
74 pub fn duration_since(&self, earlier: Instant) -> Duration {
75 self.checked_duration_since(earlier).unwrap_or_default()
76 }
77
78 /// Returns the amount of time elapsed from another instant to this one,
79 /// or None if that instant is later than this one.
80 ///
81 /// # Examples
82 ///
83 /// ```
84 /// use std::time::Duration;
85 /// use std::thread::sleep;
86 ///
87 /// use minstant::Instant;
88 ///
89 /// let now = Instant::now();
90 /// sleep(Duration::new(1, 0));
91 /// let new_now = Instant::now();
92 /// println!("{:?}", new_now.checked_duration_since(now));
93 /// println!("{:?}", now.checked_duration_since(new_now)); // None
94 /// ```
95 #[inline]
96 pub fn checked_duration_since(&self, earlier: Instant) -> Option<Duration> {
97 Some(Duration::from_nanos(
98 (self.0.checked_sub(earlier.0)? as f64 * tsc::nanos_per_cycle()) as u64,
99 ))
100 }
101
102 /// Returns the amount of time elapsed from another instant to this one,
103 /// or zero duration if that instant is later than this one.
104 ///
105 /// # Examples
106 ///
107 /// ```
108 /// use std::time::Duration;
109 /// use std::thread::sleep;
110 ///
111 /// use minstant::Instant;
112 ///
113 /// let now = Instant::now();
114 /// sleep(Duration::new(1, 0));
115 /// let new_now = Instant::now();
116 /// println!("{:?}", new_now.saturating_duration_since(now));
117 /// println!("{:?}", now.saturating_duration_since(new_now)); // 0ns
118 /// ```
119 #[inline]
120 pub fn saturating_duration_since(&self, earlier: Instant) -> Duration {
121 self.checked_duration_since(earlier).unwrap_or_default()
122 }
123
124 /// Returns the amount of time elapsed since this instant was created.
125 ///
126 /// # Panics
127 ///
128 /// This function may panic if the current time is earlier than this
129 /// instant, which is something that can happen if an `Instant` is
130 /// produced synthetically.
131 ///
132 /// # Examples
133 ///
134 /// ```
135 /// use std::time::Duration;
136 /// use std::thread::sleep;
137 ///
138 /// use minstant::Instant;
139 ///
140 /// let instant = Instant::now();
141 /// let three_secs = Duration::from_secs(3);
142 /// sleep(three_secs);
143 /// assert!(instant.elapsed() >= three_secs);
144 /// ```
145 #[inline]
146 pub fn elapsed(&self) -> Duration {
147 Instant::now() - *self
148 }
149
150 /// Returns `Some(t)` where `t` is the time `self + duration` if `t` can be represented as
151 /// `Instant` (which means it's inside the bounds of the underlying data structure), `None`
152 /// otherwise.
153 #[inline]
154 pub fn checked_add(&self, duration: Duration) -> Option<Instant> {
155 self.0
156 .checked_add((duration.as_nanos() as u64 as f64 / tsc::nanos_per_cycle()) as u64)
157 .map(Instant)
158 }
159
160 /// Returns `Some(t)` where `t` is the time `self - duration` if `t` can be represented as
161 /// `Instant` (which means it's inside the bounds of the underlying data structure), `None`
162 /// otherwise.
163 #[inline]
164 pub fn checked_sub(&self, duration: Duration) -> Option<Instant> {
165 self.0
166 .checked_sub((duration.as_nanos() as u64 as f64 / tsc::nanos_per_cycle()) as u64)
167 .map(Instant)
168 }
169
170 /// Convert interal clocking counter into a UNIX timestamp represented as the
171 /// nanoseconds elapsed from [UNIX_EPOCH](std::time::UNIX_EPOCH).
172 ///
173 /// [`Anchor`](crate::Anchor) contains the necessary calibration data for conversion.
174 /// Typically, initializing an [`Anchor`](crate::Anchor) takes about 50 nano seconds, so
175 /// try to reuse it for a batch of `Instant`.
176 ///
177 /// # Examples
178 ///
179 /// ```
180 /// use std::time::UNIX_EPOCH;
181 /// use minstant::{Instant, Anchor};
182 ///
183 /// let anchor = Anchor::new();
184 /// let instant = Instant::now();
185 ///
186 /// let expected = UNIX_EPOCH.elapsed().unwrap().as_nanos();
187 /// assert!((instant.as_unix_nanos(&anchor) as i64 - expected as i64).abs() < 1_000_000);
188 /// ```
189 pub fn as_unix_nanos(&self, anchor: &Anchor) -> u64 {
190 if self.0 > anchor.cycle {
191 let forward_ns = ((self.0 - anchor.cycle) as f64 * tsc::nanos_per_cycle()) as u64;
192 anchor.unix_time_ns + forward_ns
193 } else {
194 let backward_ns = ((anchor.cycle - self.0) as f64 * tsc::nanos_per_cycle()) as u64;
195 anchor.unix_time_ns - backward_ns
196 }
197 }
198}
199
200impl Add<Duration> for Instant {
201 type Output = Instant;
202
203 #[inline]
204 fn add(self, other: Duration) -> Instant {
205 self.checked_add(other)
206 .expect("overflow when adding duration to instant")
207 }
208}
209
210impl AddAssign<Duration> for Instant {
211 #[inline]
212 fn add_assign(&mut self, other: Duration) {
213 *self = *self + other;
214 }
215}
216
217impl Sub<Duration> for Instant {
218 type Output = Instant;
219
220 #[inline]
221 fn sub(self, other: Duration) -> Instant {
222 self.checked_sub(other)
223 .expect("overflow when subtracting duration from instant")
224 }
225}
226
227impl SubAssign<Duration> for Instant {
228 #[inline]
229 fn sub_assign(&mut self, other: Duration) {
230 *self = *self - other;
231 }
232}
233
234impl Sub<Instant> for Instant {
235 type Output = Duration;
236
237 /// Returns the amount of time elapsed from another instant to this one,
238 /// or zero duration if that instant is later than this one.
239 ///
240 /// # Panics
241 ///
242 /// Previously we panicked if `other` was later than `self`. Currently this method saturates
243 /// to follow the behavior of the standard library. Future versions may reintroduce the panic
244 /// in some circumstances.
245 #[inline]
246 fn sub(self, other: Instant) -> Duration {
247 self.duration_since(other)
248 }
249}
250
251impl std::fmt::Debug for Instant {
252 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
253 self.0.fmt(f)
254 }
255}
256
257/// An anchor which can be used to convert internal clocking counter into system timestamp.
258///
259/// *[See also the `Instant::as_unix_nanos()`](crate::Instant::as_unix_nanos).*
260#[derive(Copy, Clone)]
261pub struct Anchor {
262 unix_time_ns: u64,
263 cycle: u64,
264}
265
266impl Default for Anchor {
267 fn default() -> Self {
268 Self::new()
269 }
270}
271
272impl Anchor {
273 pub fn new() -> Anchor {
274 let unix_time_ns = SystemTime::now()
275 .duration_since(UNIX_EPOCH)
276 .expect("unexpected time drift")
277 .as_nanos() as u64;
278 Anchor {
279 unix_time_ns,
280 cycle: tsc::tsc_from_ref(),
281 }
282 }
283}