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

            
44
use std::time;
45

            
46
use derive_more::{Add, AddAssign, Sub, SubAssign};
47
use paste::paste;
48

            
49
use 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)]
76
pub 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)] //
113
pub struct CoarseInstant(coarsetime::Instant);
114

            
115
impl From<time::Duration> for CoarseDuration {
116
206001
    fn from(td: time::Duration) -> CoarseDuration {
117
206001
        CoarseDuration(td.into())
118
206001
    }
119
}
120
impl 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`
126
macro_rules! impl_add_sub { { $($AddSub:ident),* $(,)? } => { paste! { $(
127
    impl std::ops::$AddSub<CoarseDuration> for CoarseInstant {
128
        type Output = CoarseInstant;
129
206001
        fn [< $AddSub:lower >](self, rhs: CoarseDuration) -> CoarseInstant {
130
206001
            CoarseInstant(self.0. [< $AddSub:lower >]( rhs.0 ))
131
206001
        }
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
)* } } }
140
impl_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]
147
pub struct RealCoarseTimeProvider {}
148

            
149
impl RealCoarseTimeProvider {
150
    /// Returns a new `RealCoarseTimeProvider`
151
    ///
152
    /// All `RealCoarseTimeProvider`s are equivalent.
153
    #[inline]
154
79030
    pub fn new() -> Self {
155
79030
        RealCoarseTimeProvider::default()
156
79030
    }
157
}
158

            
159
impl CoarseTimeProvider for RealCoarseTimeProvider {
160
119623
    fn now_coarse(&self) -> CoarseInstant {
161
119623
        CoarseInstant(coarsetime::Instant::now())
162
119623
    }
163
}
164

            
165
#[cfg(not(miri))] // TODO coarse_time subtracts with overflow!
166
#[cfg(test)]
167
mod 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
}