1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
//! Wrapper for coarsetime.
//!
// (Note that this is the doc comment for a private module,
// not public API docs.
// So this describes the implementation and rationale.)
//
//! We want to be able to mock coarsetime in tor-rtmock,
//! so we need coarse time provision to to be part of a `Runtime`.
//!
//! (We can't just use `coarsetime`'s mocking facilities,
//! because they still use a process-wide global for the current time.)
//!
//! We use [`coarsetime::Instant::now`],
//! which in turn calls the OS's
//! `CLOCK_MONOTONIC_COARSE`, `CLOCK_MONOTONIC_FAST`, or similar.
//!
//! We don't use the non-updating coarsetime methods
//! such as `coarsetime::Instant:: now_without_cache_update`.
//! So, nor do we start a [`coarsetime::Updater`] update thread
//! (that would wake up frequently).
//!
//! We don't use (or expose any way to use) `coarsetime::Clock`;
//! we don't think that's a useful thing.
//!
//! ### Future possibilities
//!
//! If we ever need to mix-and-match coarse time values
//! from low-level crates like tor-proto,
//! with the wrapped-up coarsetime we have here,
//! we have the following options:
//!
//!  a. move much of this (perhaps the whole module) to a lower-layer crate
//!    (let's call it tor-coarsetime) and have everyone use that.
//!    Even the `CoarseTimeProvider` trait could be moved down,
//!    since it doesn't depend on anything else from tor-rtcompat.
//!
//!  b1. semver-expose coarsetime here in tor-rtcompat,
//!    perhaps optionally, by exposing a conversions
//!    with coarsetime::Instant.
//!
//!  b2. abolish the newtypes and instead make the types
//!    here aliases for coarsetime

use std::ops::{Add, AddAssign, Sub, SubAssign};
use std::time;

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

use crate::traits::CoarseTimeProvider;

/// A duration with reduced precision, and, in the future, saturating arithmetic
///
/// This type represents a (nonnegative) period
/// between two [`CoarseInstant`]s.
///
/// This is (slightly lossily) interconvertible with `std::time::Duration`.
///
/// ### Range and precision
///
/// A `CoarseDuration` can represent at least 2^31 seconds,
/// at a granularity of at least 1 second.
// We may want to promise a better precision; that would be fine.
///
/// ### Panics
///
/// Currently, operations on `CoarseDuration` (including conversions)
/// can panic on under/overflow.
/// We regard this as a bug.
/// The intent is that all operations will saturate.
//
// Currently this type's API is a bit anaemic.
// If that turns out to be annoying, we might want to add
// methods like `from_secs`, `as_secs` etc.
#[derive(Copy, Clone, Debug, Ord, PartialOrd, Eq, PartialEq, Hash)] //
#[derive(Add, Sub, AddAssign, SubAssign)]
pub struct CoarseDuration(coarsetime::Duration);

/// A monotonic timestamp with reduced precision, and, in the future, saturating arithmetic
///
/// Like `std::time::Instant`, but:
///
///  - [`RealCoarseTimeProvider::now_coarse()`] is cheap on all platforms,
///    unlike `std::time::Instant::now`.
///
///  - **Not true yet**: Arithmetic is saturating (so, it's panic-free).
///
///  - Precision and accuracy are reduced.
///
///  - *Cannot* be compared with, or converted to/from, `std::time::Instant`.
///    It has a completely different timescale to `Instant`.
///
/// You can obtain this (only) from `CoarseTimeProvider::now_coarse`.
///
/// ### Range and precision
///
/// The range of a `CoarseInstant` is not directly visible,
/// since the absolute value isn't.
/// `CoarseInstant`s are valid only within the context of one program execution (process).
///
/// Correct behaviour with processes that run for more than 2^31 seconds (about 30 years)
/// is not guaranteed.
///
/// The precision is no worse than 1 second.
// We may want to promise a better precision; that would be fine.
///
/// ### Panics
///
/// Currently, operations on `CoarseInstant` and `CoarseDuration`
/// can panic on under/overflow.
/// We regard this as a bug.
/// The intent is that all operations will saturate.
#[derive(Copy, Clone, Debug, Ord, PartialOrd, Eq, PartialEq, Hash)] //
pub struct CoarseInstant(coarsetime::Instant);

impl From<time::Duration> for CoarseDuration {
    fn from(td: time::Duration) -> CoarseDuration {
        CoarseDuration(td.into())
    }
}
impl From<CoarseDuration> for time::Duration {
    fn from(cd: CoarseDuration) -> time::Duration {
        cd.0.into()
    }
}
/// implement `$AddSub<CoarseDuration> for CoarseInstant`, and `*Assign`
macro_rules! impl_add_sub { { $($AddSub:ident),* $(,)? } => { paste! { $(
    impl $AddSub<CoarseDuration> for CoarseInstant {
        type Output = CoarseInstant;
        fn [< $AddSub:lower >](self, rhs: CoarseDuration) -> CoarseInstant {
            CoarseInstant(self.0. [< $AddSub:lower >]( rhs.0 ))
        }
    }
    impl [< $AddSub Assign >]<CoarseDuration> for CoarseInstant {
        fn [< $AddSub:lower _assign >](&mut self, rhs: CoarseDuration) {
            *self = self.[< $AddSub:lower >](rhs);
        }
    }
)* } } }
impl_add_sub!(Add, Sub);

/// Provider of reduced-precision timestamps using the real OS clock
///
/// This is a ZST.
#[derive(Default, Clone, Debug)]
#[non_exhaustive]
pub struct RealCoarseTimeProvider {}

impl RealCoarseTimeProvider {
    /// Returns a new `RealCoarseTimeProvider`
    ///
    /// All `RealCoarseTimeProvider`s are equivalent.
    #[inline]
    pub fn new() -> Self {
        RealCoarseTimeProvider::default()
    }
}

impl CoarseTimeProvider for RealCoarseTimeProvider {
    fn now_coarse(&self) -> CoarseInstant {
        CoarseInstant(coarsetime::Instant::now())
    }
}

#[cfg(test)]
mod test {
    // @@ begin test lint list maintained by maint/add_warning @@
    #![allow(clippy::bool_assert_comparison)]
    #![allow(clippy::clone_on_copy)]
    #![allow(clippy::dbg_macro)]
    #![allow(clippy::mixed_attributes_style)]
    #![allow(clippy::print_stderr)]
    #![allow(clippy::print_stdout)]
    #![allow(clippy::single_char_pattern)]
    #![allow(clippy::unwrap_used)]
    #![allow(clippy::unchecked_duration_subtraction)]
    #![allow(clippy::useless_vec)]
    #![allow(clippy::needless_pass_by_value)]
    //! <!-- @@ end test lint list maintained by maint/add_warning @@ -->
    #![allow(clippy::erasing_op)]
    use super::*;

    #[test]
    fn basic() {
        let t1 = RealCoarseTimeProvider::new().now_coarse();
        let t2 = t1 + CoarseDuration::from(time::Duration::from_secs(10));
        let t0 = t1 - CoarseDuration::from(time::Duration::from_secs(10));

        assert!(t0 < t1);
        assert!(t0 < t2);
        assert!(t1 < t2);
    }
}