1
//! type-erased time provider
2

            
3
use std::future::Future;
4
use std::mem::{self, MaybeUninit};
5
use std::pin::Pin;
6
use std::time::{Duration, Instant, SystemTime};
7

            
8
use dyn_clone::DynClone;
9
use educe::Educe;
10
use paste::paste;
11

            
12
use crate::{CoarseInstant, CoarseTimeProvider, SleepProvider};
13

            
14
//-------------------- handle PreferredRuntime maybe not existing ----------
15

            
16
// TODO use this more widely, eg in tor-rtcompat/lib.rs
17

            
18
/// See the other implementation
19
#[allow(unused_macros)] // Will be redefined if there *is* a preferred runtime
20
macro_rules! if_preferred_runtime {{ [$($y:tt)*] [$($n:tt)*] } => { $($n)* }}
21
#[cfg(all(
22
    any(feature = "native-tls", feature = "rustls"),
23
    any(feature = "async-std", feature = "tokio")
24
))]
25
/// `if_preferred_runtime!{[ Y ] [ N ]}` expands to `Y` (if there's `PreferredRuntime`) or `N`
26
macro_rules! if_preferred_runtime {{ [$($y:tt)*] [$($n:tt)*] } => { $($y)* }}
27

            
28
if_preferred_runtime! {[
29
    use crate::PreferredRuntime;
30
] [
31
    /// Dummy value that makes the variant uninhabited
32
    #[derive(Clone, Debug)]
33
    enum PreferredRuntime {}
34
]}
35
/// `with_preferred_runtime!( R; EXPR )` expands to `EXPR`, or to `match *R {}`.
36
macro_rules! with_preferred_runtime {{ $p:ident; $($then:tt)* } => {
37
    if_preferred_runtime!([ $($then)* ] [ match *$p {} ])
38
}}
39

            
40
//---------- principal types ----------
41

            
42
/// Convenience alias for a boxed sleep future
43
type DynSleepFuture = Pin<Box<dyn Future<Output = ()> + Send + 'static>>;
44

            
45
/// Object-safe version of `SleepProvider` and `CoarseTimeProvider`
46
///
47
/// The methods mirror those in `SleepProvider` and `CoarseTimeProvider`
48
#[allow(clippy::missing_docs_in_private_items)]
49
trait DynProvider: DynClone + Send + Sync + 'static {
50
    // SleepProvider principal methods
51
    fn dyn_now(&self) -> Instant;
52
    fn dyn_wallclock(&self) -> SystemTime;
53
    fn dyn_sleep(&self, duration: Duration) -> DynSleepFuture;
54

            
55
    // SleepProvider testing stuff
56
    fn dyn_block_advance(&self, reason: String);
57
    fn dyn_release_advance(&self, _reason: String);
58
    fn dyn_allow_one_advance(&self, duration: Duration);
59

            
60
    // CoarseTimeProvider
61
    fn dyn_now_coarse(&self) -> CoarseInstant;
62
}
63

            
64
dyn_clone::clone_trait_object!(DynProvider);
65

            
66
/// Type-erased `SleepProvider` and `CoarseTimeProvider`
67
///
68
/// Useful where time is needed, but we don't want a runtime type parameter.
69
#[derive(Clone, Debug)]
70
pub struct DynTimeProvider(Impl);
71

            
72
/// Actual contents of a `DynTimeProvider`
73
///
74
/// We optimise the `PreferredRuntime` case.
75
/// We *could*, instead, just use `Box<dyn DynProvider>` here.
76
///
77
/// The reason for doing it this way is that we expect this to be on many hot paths.
78
/// Putting a message in a queue is extremely common, and we'd like to save a dyn dispatch,
79
/// and reference to a further heap entry (which might be distant in the cache).
80
///
81
/// (Also, it's nice to avoid boxing when we crate new types that use this,
82
/// including our memory-quota-tracked mpsc streams, see `tor-memquota::mq_queue`.
83
///
84
/// The downside is that this means:
85
///  * This enum instead of a simple type
86
///  * The `unsafe` inside `downcast_value`.
87
///  * `match` statements in method shims
88
#[derive(Clone, Educe)]
89
#[educe(Debug)]
90
enum Impl {
91
    /// Just (a handle to) the preferred runtime
92
    Preferred(PreferredRuntime),
93
    /// Some other runtime
94
    Dyn(#[educe(Debug(ignore))] Box<dyn DynProvider>),
95
}
96

            
97
impl DynTimeProvider {
98
    /// Create a new `DynTimeProvider` from a concrete runtime type
99
2228
    pub fn new<R: SleepProvider + CoarseTimeProvider>(runtime: R) -> Self {
100
        // Try casting to a `DynTimeProvider` directly first, to avoid possibly creating a
101
        // `DynTimeProvider` containing another `DynTimeProvider`.
102
2228
        let runtime = match downcast_value(runtime) {
103
86
            Ok(x) => return x,
104
2142
            Err(x) => x,
105
        };
106
        // Try casting to a `PreferredRuntime`.
107
2142
        let imp = match downcast_value(runtime) {
108
4
            Ok(preferred) => Impl::Preferred(preferred),
109
2138
            Err(other) => Impl::Dyn(Box::new(other) as _),
110
        };
111
2142
        DynTimeProvider(imp)
112
2228
    }
113
}
114

            
115
//---------- impl DynProvider for any SleepProvider + CoarseTimeProvider ----------
116

            
117
/// Define ordinary methods in `impl DynProvider`
118
///
119
/// This macro exists mostly to avoid copypaste mistakes where we (for example)
120
/// implement `block_advance` by calling `release_advance`.
121
macro_rules! dyn_impl_methods { { $(
122
    fn $name:ident(
123
        ,
124
        $( $param:ident: $ptype:ty ),*
125
    ) -> $ret:ty;
126
)* } => { paste! { $(
127
17182
    fn [<dyn_ $name>](
128
17182
        &self,
129
17182
        $( $param: $ptype, )*
130
17182
    )-> $ret {
131
17182
        self.$name( $($param,)* )
132
17182
    }
133
)* } } }
134

            
135
impl<R: SleepProvider + CoarseTimeProvider> DynProvider for R {
136
    dyn_impl_methods! {
137
        fn now(,) -> Instant;
138
        fn wallclock(,) -> SystemTime;
139

            
140
        fn block_advance(, reason: String) -> ();
141
        fn release_advance(, reason: String) -> ();
142
        fn allow_one_advance(, duration: Duration) -> ();
143

            
144
        fn now_coarse(,) -> CoarseInstant;
145
    }
146

            
147
220
    fn dyn_sleep(&self, duration: Duration) -> DynSleepFuture {
148
220
        Box::pin(self.sleep(duration))
149
220
    }
150
}
151

            
152
//---------- impl SleepProvider and CoarseTimeProvider for DynTimeProvider ----------
153

            
154
/// Define ordinary methods in `impl .. for DynTimeProvider`
155
///
156
/// This macro exists mostly to avoid copypaste mistakes where we (for example)
157
/// implement `block_advance` by calling `release_advance`.
158
macro_rules! pub_impl_methods { { $(
159
    fn $name:ident $( [ $($generics:tt)* ] )? (
160
        ,
161
        $( $param:ident: $ptype:ty ),*
162
    ) -> $ret:ty;
163
)* } => { paste! { $(
164
416598
    fn $name $( < $($generics)* > )?(
165
416598
        &self,
166
416598
        $( $param: $ptype, )*
167
416598
    )-> $ret {
168
416598
        match &self.0 {
169
49
            Impl::Preferred(p) => with_preferred_runtime!(p; p.$name( $($param,)* )),
170
416549
            Impl::Dyn(p) => p.[<dyn_ $name>]( $($param .into() ,)? ),
171
        }
172
416598
    }
173
)* } } }
174

            
175
impl SleepProvider for DynTimeProvider {
176
    pub_impl_methods! {
177
        fn now(,) -> Instant;
178
        fn wallclock(,) -> SystemTime;
179

            
180
        fn block_advance[R: Into<String>](, reason: R) -> ();
181
        fn release_advance[R: Into<String>](, reason: R) -> ();
182
        fn allow_one_advance(, duration: Duration) -> ();
183
    }
184

            
185
    type SleepFuture = DynSleepFuture;
186

            
187
5390
    fn sleep(&self, duration: Duration) -> DynSleepFuture {
188
5390
        match &self.0 {
189
            Impl::Preferred(p) => with_preferred_runtime!(p; Box::pin(p.sleep(duration))),
190
5390
            Impl::Dyn(p) => p.dyn_sleep(duration),
191
        }
192
5390
    }
193
}
194

            
195
impl CoarseTimeProvider for DynTimeProvider {
196
    pub_impl_methods! {
197
        fn now_coarse(,) -> CoarseInstant;
198
    }
199
}
200

            
201
//---------- downcast_value ----------
202

            
203
// TODO expose this, maybe in tor-basic-utils ?
204

            
205
/// Try to cast `I` (which is presumably a TAIT) to `O` (presumably a concrete type)
206
///
207
/// We use runtime casting, but typically the answer is known at compile time.
208
///
209
/// Astonishingly, this isn't in any of the following:
210
///  * `std`
211
///  * `match-downcast`
212
///  * `better_any` (`downcast:move` comes close but doesn't give you your `self` back)
213
///  * `castaway`
214
///  * `mopa`
215
///  * `as_any`
216
4378
fn downcast_value<I: std::any::Any, O: Sized + 'static>(input: I) -> Result<O, I> {
217
4378
    // `MaybeUninit` makes it possible to to use `downcast_mut`
218
4378
    // and, if it's successful, *move* out of the reference.
219
4378
    //
220
4378
    // It might be possible to write this function using `mme::transmute` instead.
221
4378
    // That might be simpler on the surface, but `mem:transmute` is a very big hammer,
222
4378
    // and doing it that way would make it quite easy to accidentally
223
4378
    // use the wrong type for the dynamic type check, or mess up lifetimes in I or O.
224
4378
    // (Also if we try to transmute the *value*, it might not be possible to
225
4378
    // persuade the compiler that the two layouts were necessarily the same.)
226
4378
    //
227
4378
    // The technique we use is:
228
4378
    //    * Put the input into `MaybeUninit`, giving us manual control of `I`'s ownership.
229
4378
    //    * Try to downcast `&mut I` (from the `MaybeUninit`) to `&mut O`.
230
4378
    //    * If the downcast is successful, move out of the `&mut O`;
231
4378
    //      this invalidates the `MaybeUninit` (making it uninitialised).
232
4378
    //    * If the downcast is unsuccessful, reocver the original `I`,
233
4378
    //      which hasn't in fact have invalidated.
234
4378

            
235
4378
    let mut input = MaybeUninit::new(input);
236
4378
    // SAFETY: the MaybeUninit is initialised just above
237
4378
    let mut_ref: &mut I = unsafe { input.assume_init_mut() };
238
4378
    match <dyn std::any::Any>::downcast_mut(mut_ref) {
239
94
        Some::<&mut O>(output) => {
240
94
            let output = output as *mut O;
241
94
            // SAFETY:
242
94
            //  output is properly aligned and points to a properly initialised
243
94
            //    O, because it came from a mut reference
244
94
            //  Reading this *invalidates* the MaybeUninit, since the value isn't Copy.
245
94
            //  It also invalidates mut_ref, which we therefore mustn't use again.
246
94
            let output: O = unsafe { output.read() };
247
94
            // Prove that the MaybeUninit is live up to here, and then isn't used any more
248
94
            #[allow(clippy::drop_non_drop)] // Yes, we know
249
94
            mem::drop::<MaybeUninit<I>>(input);
250
94
            Ok(output)
251
        }
252
4284
        None => Err(
253
4284
            // SAFETY: Indeed, it was just initialised, and downcast_mut didn't change that
254
4284
            unsafe { input.assume_init() },
255
4284
        ),
256
    }
257
4378
}
258

            
259
#[cfg(test)]
260
mod test {
261
    // @@ begin test lint list maintained by maint/add_warning @@
262
    #![allow(clippy::bool_assert_comparison)]
263
    #![allow(clippy::clone_on_copy)]
264
    #![allow(clippy::dbg_macro)]
265
    #![allow(clippy::mixed_attributes_style)]
266
    #![allow(clippy::print_stderr)]
267
    #![allow(clippy::print_stdout)]
268
    #![allow(clippy::single_char_pattern)]
269
    #![allow(clippy::unwrap_used)]
270
    #![allow(clippy::unchecked_duration_subtraction)]
271
    #![allow(clippy::useless_vec)]
272
    #![allow(clippy::needless_pass_by_value)]
273
    //! <!-- @@ end test lint list maintained by maint/add_warning @@ -->
274
    #![allow(clippy::useless_format)]
275
    use super::*;
276

            
277
    use std::fmt::{Debug, Display};
278
    use std::hint::black_box;
279

            
280
    fn try_downcast_string<S: Display + Debug + 'static>(x: S) -> Result<String, S> {
281
        black_box(downcast_value(black_box(x)))
282
    }
283

            
284
    #[test]
285
    fn check_downcast_value() {
286
        // This and the one in check_downcast_dropcount are not combined, with generics,
287
        // so that the types of everything are as clear as they can be.
288
        assert_eq!(try_downcast_string(format!("hi")).unwrap(), format!("hi"));
289
        assert_eq!(try_downcast_string("hi").unwrap_err().to_string(), "hi");
290
    }
291

            
292
    #[test]
293
    fn check_downcast_dropcount() {
294
        #[derive(Debug, derive_more::Display)]
295
        #[display("{self:?}")]
296
        struct DropCounter(u32);
297

            
298
        fn try_downcast_dc(x: impl Debug + 'static) -> Result<DropCounter, impl Debug + 'static> {
299
            black_box(downcast_value(black_box(x)))
300
        }
301

            
302
        impl Drop for DropCounter {
303
            fn drop(&mut self) {
304
                let _: u32 = self.0.checked_sub(1).unwrap();
305
            }
306
        }
307

            
308
        let dc = DropCounter(0);
309
        let mut dc: DropCounter = try_downcast_dc(dc).unwrap();
310
        assert_eq!(dc.0, 0);
311
        dc.0 = 1;
312

            
313
        let dc = DropCounter(0);
314
        let mut dc: DropCounter = try_downcast_string(dc).unwrap_err();
315
        assert_eq!(dc.0, 0);
316
        dc.0 = 1;
317
    }
318

            
319
    if_preferred_runtime! {[
320
        #[test]
321
        fn dyn_time_provider_from_dyn_time_provider() {
322
            // A new `DynTimeProvider(Impl::PreferredRuntime(_))`.
323
            let x = DynTimeProvider::new(PreferredRuntime::create().unwrap());
324

            
325
            // Cast `x` as a generic `SleepProvider + CoarseTimeProvider` and wrap in a new
326
            // `DynTimeProvider`.
327
            fn new_provider<R: SleepProvider + CoarseTimeProvider>(runtime: R) -> DynTimeProvider {
328
                DynTimeProvider::new(runtime)
329
            }
330
            let x = new_provider(x);
331

            
332
            // Ensure that `x` didn't end up as a `DynTimeProvider(Impl::Dyn(_))`.
333
            assert!(matches!(x, DynTimeProvider(Impl::Preferred(_))));
334
        }
335
    ] [
336
        // no test if there is no preferred runtime
337
    ]}
338
}