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}