tarantool/time.rs
1//! Provides a custom [`Instant`] implementation, based on tarantool fiber API.
2
3use crate::ffi::tarantool as ffi;
4use std::mem::MaybeUninit;
5use std::ops::{Add, AddAssign, Sub, SubAssign};
6use std::time::Duration;
7
8pub use crate::clock::INFINITY;
9
10/// A measurement of a monotonically nondecreasing clock.
11/// Opaque and useful only with [`Duration`].
12///
13/// Instants are guaranteed to be no less than any previously
14/// measured instant when created, and are often useful for tasks such as measuring
15/// benchmarks or timing how long an operation takes.
16///
17/// Note, however, that instants are **not** guaranteed to be **steady**. In other
18/// words, each tick of the underlying clock might not be the same length (e.g.
19/// some seconds may be longer than others). An instant may jump forwards or
20/// experience time dilation (slow down or speed up), but it will never go
21/// backwards.
22///
23/// Instants should generally be condsidered as opaque types that can only be compared to one another.
24/// Though there is a method to get "the number of seconds" from an instant it is implementation dependent
25/// and should be used with knowledge of how this particular `Instant` was constructed.
26/// Instead, prefer using other operations, such as measuring the duration between two instants, comparing two
27/// instants, adding and subtracting `Duration`.
28///
29/// This struct is almost identical to [`std::time::Instant`] but provides
30/// some additional saturating methods. And it can also be constructed with
31/// [`fiber::clock`](crate::fiber::clock), in which case it behaves in a tarantool specific way.
32#[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Debug, Hash)]
33pub struct Instant(pub(crate) Duration);
34
35impl Instant {
36 /// Equivalent to [`Self::now_accurate`].
37 #[must_use]
38 #[inline(always)]
39 #[deprecated = "use `now_fiber` or `now_accurate` instead"]
40 pub fn now() -> Self {
41 Self::now_accurate()
42 }
43
44 /// Returns an instant corresponding to "now". Uses monotonic clock.
45 ///
46 /// Use this function when duration accuracy is required, for example when
47 /// timing the execution of different parts of your program (benchmarking).
48 ///
49 /// If you need to compute timeouts for yielding operations you should use
50 /// [`Self::now_fiber`] instead.
51 ///
52 /// # Examples
53 ///
54 /// ```no_run
55 /// # fn expensive_computation() {}
56 /// use tarantool::time::Instant;
57 ///
58 /// let start = Instant::now_accurate();
59 /// expensive_computation();
60 /// println!("expensive_computation took {:?}", start.elapsed());
61 /// ```
62 #[must_use]
63 #[inline]
64 pub fn now_accurate() -> Self {
65 unsafe {
66 let mut timespec = MaybeUninit::<libc::timespec>::zeroed().assume_init();
67 if libc::clock_gettime(libc::CLOCK_MONOTONIC, (&mut timespec) as *mut _) != 0 {
68 let err = std::io::Error::last_os_error();
69 panic!("failed to get time: {}", err)
70 }
71 Self(Duration::new(
72 timespec.tv_sec as u64,
73 timespec.tv_nsec as u32,
74 ))
75 }
76 }
77
78 /// Returns an instant corresponding to event loop iteration begin time.
79 /// Uses monotonic clock.
80 ///
81 /// Use this function when computing timeouts for tasks which may result in
82 /// fiber yields. It is important that this function is used, because the
83 /// tarantool uses this value for it's internal event loop timers, and using
84 /// [`Self::now_accurate`] may result in unexpected results.
85 ///
86 /// If instead you're timing how long things execute, use [`Self::now_accurate`].
87 ///
88 /// # Examples
89 ///
90 /// ```no_run
91 /// # fn do_some_yielding_task() {}
92 /// use tarantool::time::Instant;
93 /// use std::time::Duration;
94 ///
95 /// let timeout = Duration::from_secs(3);
96 /// let deadline = Instant::now_fiber().saturating_add(timeout);
97 /// while Instant::now_fiber() < deadline {
98 /// do_some_yielding_task();
99 /// }
100 /// ```
101 #[must_use]
102 #[inline(always)]
103 pub fn now_fiber() -> Self {
104 // Safety: always safe
105 let secs = unsafe { ffi::fiber_clock() };
106 Self(Duration::from_secs_f64(secs))
107 }
108
109 /// Returns the amount of time elapsed since this instant was created.
110 ///
111 /// Uses [`Self::now_accurate`] to determine the instant of "now".
112 ///
113 /// # Examples
114 ///
115 /// ```no_run
116 /// use std::time::Duration;
117 /// use tarantool::time::Instant;
118 /// use tarantool::fiber;
119 ///
120 /// let instant = Instant::now_accurate();
121 /// let three_secs = Duration::from_secs(3);
122 /// fiber::sleep(three_secs);
123 /// assert!(instant.elapsed() >= three_secs);
124 /// ```
125 #[must_use]
126 #[inline]
127 pub fn elapsed(&self) -> Duration {
128 Self::now_accurate().duration_since(*self)
129 }
130
131 /// Returns `Some(t)` where `t` is the time `self + duration` if `t` can be represented as
132 /// `Instant` (which means it's inside the bounds of the underlying representation), `None`
133 /// otherwise.
134 #[must_use]
135 #[inline]
136 pub fn checked_add(&self, duration: Duration) -> Option<Instant> {
137 self.0.checked_add(duration).map(Instant)
138 }
139
140 /// Returns `Some(t)` where `t` is the time `self - duration` if `t` can be represented as
141 /// `Instant` (which means it's inside the bounds of the underlying representation), `None`
142 /// otherwise.
143 #[must_use]
144 #[inline]
145 pub fn checked_sub(&self, duration: Duration) -> Option<Instant> {
146 self.0.checked_sub(duration).map(Instant)
147 }
148
149 /// Saturating addition. Computes `self + duration`, returning maximal possible
150 /// instant (allowed by the underlying representaion) if overflow occurred.
151 #[must_use]
152 #[inline]
153 pub fn saturating_add(&self, duration: Duration) -> Instant {
154 Self(self.0.saturating_add(duration))
155 }
156
157 /// Saturating subtraction. Computes `self - duration`, returning minimal possible
158 /// instant (allowed by the underlying representaion) if overflow occurred.
159 #[must_use]
160 #[inline]
161 pub fn saturating_sub(&self, duration: Duration) -> Instant {
162 Self(self.0.saturating_sub(duration))
163 }
164
165 /// Returns the amount of time elapsed from another instant to this one,
166 /// or None if that instant is later than this one.
167 ///
168 /// # Examples
169 ///
170 /// ```no_run
171 /// use std::time::Duration;
172 /// use std::thread::sleep;
173 /// use tarantool::time::Instant;
174 ///
175 /// let now = Instant::now();
176 /// sleep(Duration::new(1, 0));
177 /// let new_now = Instant::now();
178 /// println!("{:?}", new_now.checked_duration_since(now));
179 /// println!("{:?}", now.checked_duration_since(new_now)); // None
180 /// ```
181 #[must_use]
182 #[inline]
183 pub fn checked_duration_since(&self, earlier: Instant) -> Option<Duration> {
184 self.0.checked_sub(earlier.0)
185 }
186
187 /// Returns the amount of time elapsed from another instant to this one,
188 /// or zero duration if that instant is later than this one.
189 ///
190 /// # Examples
191 ///
192 /// ```no_run
193 /// use std::time::Duration;
194 /// use std::thread::sleep;
195 /// use tarantool::time::Instant;
196 ///
197 /// let now = Instant::now();
198 /// sleep(Duration::new(1, 0));
199 /// let new_now = Instant::now();
200 /// println!("{:?}", new_now.duration_since(now));
201 /// println!("{:?}", now.duration_since(new_now)); // 0ns
202 /// ```
203 #[must_use]
204 #[inline]
205 pub fn duration_since(&self, earlier: Instant) -> Duration {
206 self.0.saturating_sub(earlier.0)
207 }
208
209 /// Get the inner representation of an `Instant`.
210 ///
211 /// # Warning
212 /// The inner representation of an instant is implementation dependent
213 /// and should be used with knowledge of how this particular `Instant` was constructed.
214 ///
215 /// If possible prefer working with `Instant` and `Duration` directly without
216 /// getting its inner representation.
217 #[inline(always)]
218 pub fn as_secs(&self) -> u64 {
219 self.0.as_secs()
220 }
221
222 /// Get the inner representation of an `Instant`.
223 ///
224 /// # Warning
225 /// The inner representation of an instant is implementation dependent
226 /// and should be used with knowledge of how this particular `Instant` was constructed.
227 ///
228 /// If possible prefer working with `Instant` and `Duration` directly without
229 /// getting its inner representation.
230 #[inline(always)]
231 pub fn as_secs_f64(&self) -> f64 {
232 self.0.as_secs_f64()
233 }
234
235 /// Get the inner representation of an `Instant`.
236 ///
237 /// # Warning
238 /// The inner representation of an instant is implementation dependent
239 /// and should be used with knowledge of how this particular `Instant` was constructed.
240 ///
241 /// If possible prefer working with `Instant` and `Duration` directly without
242 /// getting its inner representation.
243 #[inline(always)]
244 pub fn as_secs_f32(&self) -> f32 {
245 self.0.as_secs_f32()
246 }
247}
248
249impl Add<Duration> for Instant {
250 type Output = Instant;
251
252 /// # Panics
253 ///
254 /// This function may panic if the resulting point in time cannot be represented by the
255 /// underlying data structure. See [`Instant::checked_add`] for a version without panic.
256 fn add(self, other: Duration) -> Instant {
257 self.checked_add(other)
258 .expect("overflow when adding duration to instant")
259 }
260}
261
262impl AddAssign<Duration> for Instant {
263 fn add_assign(&mut self, other: Duration) {
264 *self = *self + other;
265 }
266}
267
268impl Sub<Duration> for Instant {
269 type Output = Instant;
270
271 fn sub(self, other: Duration) -> Instant {
272 self.checked_sub(other)
273 .expect("overflow when subtracting duration from instant")
274 }
275}
276
277impl SubAssign<Duration> for Instant {
278 fn sub_assign(&mut self, other: Duration) {
279 *self = *self - other;
280 }
281}
282
283impl Sub<Instant> for Instant {
284 type Output = Duration;
285
286 /// Returns the amount of time elapsed from another instant to this one,
287 /// or zero duration if that instant is later than this one.
288 fn sub(self, other: Instant) -> Duration {
289 self.duration_since(other)
290 }
291}
292
293#[cfg(test)]
294mod tests {
295 use super::Instant;
296 use std::time::Duration;
297
298 #[test]
299 fn addition() {
300 let now = Instant::now_accurate();
301
302 assert_eq!(now.checked_add(Duration::MAX), None);
303 assert_eq!(now.saturating_add(Duration::MAX), Instant(Duration::MAX));
304
305 let plus_second = now.checked_add(Duration::from_secs(1)).unwrap();
306 assert_eq!(plus_second, now.saturating_add(Duration::from_secs(1)));
307 assert_eq!(plus_second, now + Duration::from_secs(1));
308 assert!(plus_second > now);
309 }
310
311 #[test]
312 fn subtraction() {
313 let now = Instant::now_accurate();
314
315 assert_eq!(now.checked_sub(Duration::MAX), None);
316 assert_eq!(now.saturating_sub(Duration::MAX), Instant(Duration::ZERO));
317
318 let minus_second = now.checked_sub(Duration::from_secs(1)).unwrap();
319 assert_eq!(minus_second, now.saturating_sub(Duration::from_secs(1)));
320 assert_eq!(minus_second, now - Duration::from_secs(1));
321 assert!(minus_second < now);
322 }
323
324 #[test]
325 fn duration_since() {
326 let now = Instant::now_accurate();
327 let plus_second = now + Duration::from_secs(1);
328 let minus_second = now - Duration::from_secs(1);
329
330 assert_eq!(
331 plus_second.duration_since(minus_second),
332 Duration::from_secs(2)
333 );
334 assert_eq!(
335 plus_second.checked_duration_since(minus_second),
336 Some(Duration::from_secs(2))
337 );
338
339 assert_eq!(minus_second.duration_since(plus_second), Duration::ZERO);
340 assert_eq!(minus_second.checked_duration_since(plus_second), None);
341 }
342}
343
344#[cfg(feature = "internal_test")]
345mod test {
346 use super::*;
347
348 #[crate::test(tarantool = "crate")]
349 fn thread_sleep() {
350 let now_fiber = Instant::now_fiber();
351 let t0 = Instant::now_accurate();
352
353 // Block the whole event loop
354 std::thread::sleep(Duration::from_millis(100));
355
356 // Event loop knows nothing about the nap we just took
357 assert_eq!(now_fiber, Instant::now_fiber());
358 // But there's still a way to find out if we needed
359 assert!(t0 < Instant::now_accurate());
360 }
361}