tor_rtcompat/
coarse_time.rs

1//! Wrapper for coarsetime.
2//!
3// (Note that this is the doc comment for a private module,
4// not public API docs.
5// So this describes the implementation and rationale.)
6//
7//! We want to be able to mock coarsetime in tor-rtmock,
8//! so we need coarse time provision to to be part of a `Runtime`.
9//!
10//! (We can't just use `coarsetime`'s mocking facilities,
11//! because they still use a process-wide global for the current time.)
12//!
13//! We use [`coarsetime::Instant::now`],
14//! which in turn calls the OS's
15//! `CLOCK_MONOTONIC_COARSE`, `CLOCK_MONOTONIC_FAST`, or similar.
16//!
17//! We don't use the non-updating coarsetime methods
18//! such as `coarsetime::Instant:: now_without_cache_update`.
19//! So, nor do we start a [`coarsetime::Updater`] update thread
20//! (that would wake up frequently).
21//!
22//! We don't use (or expose any way to use) `coarsetime::Clock`;
23//! we don't think that's a useful thing.
24//!
25//! ### Future possibilities
26//!
27//! If we ever need to mix-and-match coarse time values
28//! from low-level crates like tor-proto,
29//! with the wrapped-up coarsetime we have here,
30//! we have the following options:
31//!
32//!  a. move much of this (perhaps the whole module) to a lower-layer crate
33//!    (let's call it tor-coarsetime) and have everyone use that.
34//!    Even the `CoarseTimeProvider` trait could be moved down,
35//!    since it doesn't depend on anything else from tor-rtcompat.
36//!
37//!  b1. semver-expose coarsetime here in tor-rtcompat,
38//!    perhaps optionally, by exposing a conversions
39//!    with coarsetime::Instant.
40//!
41//!  b2. abolish the newtypes and instead make the types
42//!    here aliases for coarsetime
43
44use std::time;
45
46use derive_more::{Add, AddAssign, Sub, SubAssign};
47use paste::paste;
48
49use crate::traits::CoarseTimeProvider;
50
51/// A duration with reduced precision, and, in the future, saturating arithmetic
52///
53/// This type represents a (nonnegative) period
54/// between two [`CoarseInstant`]s.
55///
56/// This is (slightly lossily) interconvertible with `std::time::Duration`.
57///
58/// ### Range and precision
59///
60/// A `CoarseDuration` can represent at least 2^31 seconds,
61/// at a granularity of at least 1 second.
62// We may want to promise a better precision; that would be fine.
63///
64/// ### Panics
65///
66/// Currently, operations on `CoarseDuration` (including conversions)
67/// can panic on under/overflow.
68/// We regard this as a bug.
69/// The intent is that all operations will saturate.
70//
71// Currently this type's API is a bit anaemic.
72// If that turns out to be annoying, we might want to add
73// methods like `from_secs`, `as_secs` etc.
74#[derive(Copy, Clone, Debug, Ord, PartialOrd, Eq, PartialEq, Hash)] //
75#[derive(Add, Sub, AddAssign, SubAssign)]
76pub struct CoarseDuration(coarsetime::Duration);
77
78/// A monotonic timestamp with reduced precision, and, in the future, saturating arithmetic
79///
80/// Like `std::time::Instant`, but:
81///
82///  - [`RealCoarseTimeProvider::now_coarse()`] is cheap on all platforms,
83///    unlike `std::time::Instant::now`.
84///
85///  - **Not true yet**: Arithmetic is saturating (so, it's panic-free).
86///
87///  - Precision and accuracy are reduced.
88///
89///  - *Cannot* be compared with, or converted to/from, `std::time::Instant`.
90///    It has a completely different timescale to `Instant`.
91///
92/// You can obtain this (only) from `CoarseTimeProvider::now_coarse`.
93///
94/// ### Range and precision
95///
96/// The range of a `CoarseInstant` is not directly visible,
97/// since the absolute value isn't.
98/// `CoarseInstant`s are valid only within the context of one program execution (process).
99///
100/// Correct behaviour with processes that run for more than 2^31 seconds (about 30 years)
101/// is not guaranteed.
102///
103/// The precision is no worse than 1 second.
104// We may want to promise a better precision; that would be fine.
105///
106/// ### Panics
107///
108/// Currently, operations on `CoarseInstant` and `CoarseDuration`
109/// can panic on under/overflow.
110/// We regard this as a bug.
111/// The intent is that all operations will saturate.
112#[derive(Copy, Clone, Debug, Ord, PartialOrd, Eq, PartialEq, Hash)] //
113pub struct CoarseInstant(coarsetime::Instant);
114
115impl From<time::Duration> for CoarseDuration {
116    fn from(td: time::Duration) -> CoarseDuration {
117        CoarseDuration(td.into())
118    }
119}
120impl From<CoarseDuration> for time::Duration {
121    fn from(cd: CoarseDuration) -> time::Duration {
122        cd.0.into()
123    }
124}
125/// implement `$AddSub<CoarseDuration> for CoarseInstant`, and `*Assign`
126macro_rules! impl_add_sub { { $($AddSub:ident),* $(,)? } => { paste! { $(
127    impl std::ops::$AddSub<CoarseDuration> for CoarseInstant {
128        type Output = CoarseInstant;
129        fn [< $AddSub:lower >](self, rhs: CoarseDuration) -> CoarseInstant {
130            CoarseInstant(self.0. [< $AddSub:lower >]( rhs.0 ))
131        }
132    }
133    impl std::ops::[< $AddSub Assign >]<CoarseDuration> for CoarseInstant {
134        fn [< $AddSub:lower _assign >](&mut self, rhs: CoarseDuration) {
135            use std::ops::$AddSub;
136            *self = self.[< $AddSub:lower >](rhs);
137        }
138    }
139)* } } }
140impl_add_sub!(Add, Sub);
141
142/// Provider of reduced-precision timestamps using the real OS clock
143///
144/// This is a ZST.
145#[derive(Default, Clone, Debug)]
146#[non_exhaustive]
147pub struct RealCoarseTimeProvider {}
148
149impl RealCoarseTimeProvider {
150    /// Returns a new `RealCoarseTimeProvider`
151    ///
152    /// All `RealCoarseTimeProvider`s are equivalent.
153    #[inline]
154    pub fn new() -> Self {
155        RealCoarseTimeProvider::default()
156    }
157}
158
159impl CoarseTimeProvider for RealCoarseTimeProvider {
160    fn now_coarse(&self) -> CoarseInstant {
161        CoarseInstant(coarsetime::Instant::now())
162    }
163}
164
165#[cfg(not(miri))] // TODO coarse_time subtracts with overflow!
166#[cfg(test)]
167mod test {
168    // @@ begin test lint list maintained by maint/add_warning @@
169    #![allow(clippy::bool_assert_comparison)]
170    #![allow(clippy::clone_on_copy)]
171    #![allow(clippy::dbg_macro)]
172    #![allow(clippy::mixed_attributes_style)]
173    #![allow(clippy::print_stderr)]
174    #![allow(clippy::print_stdout)]
175    #![allow(clippy::single_char_pattern)]
176    #![allow(clippy::unwrap_used)]
177    #![allow(clippy::unchecked_duration_subtraction)]
178    #![allow(clippy::useless_vec)]
179    #![allow(clippy::needless_pass_by_value)]
180    //! <!-- @@ end test lint list maintained by maint/add_warning @@ -->
181    #![allow(clippy::erasing_op)]
182    use super::*;
183
184    #[test]
185    fn basic() {
186        let t1 = RealCoarseTimeProvider::new().now_coarse();
187        let t2 = t1 + CoarseDuration::from(time::Duration::from_secs(10));
188        let t0 = t1 - CoarseDuration::from(time::Duration::from_secs(10));
189
190        assert!(t0 < t1);
191        assert!(t0 < t2);
192        assert!(t1 < t2);
193    }
194}