time_core.rs
1 //! [`MockTimeCore`] and [`MockCoarseTimeProvider`] 2 3 use derive_deftly::{define_derive_deftly, Deftly}; 4 use std::time::{Duration, Instant, SystemTime}; 5 use tor_rtcompat::{CoarseDuration, CoarseInstant}; 6 use tor_rtcompat::{CoarseTimeProvider, RealCoarseTimeProvider}; 7 8 define_derive_deftly! { 9 /// Derive getters for struct fields. 10 /// 11 /// Like `amplify::Getters` but `pub(crate)`. 12 /// 13 /// TODO add this feature to `amplify`. 14 CrateGetters: 15 ${define REF ${if not(fmeta(getter_copy)) { & }}} 16 $( 17 impl $ttype { 18 ${fattrs doc} 19 pub(crate) fn $fname(&self) -> $REF $ftype { 20 $REF self.$fname 21 } 22 } 23 ) 24 } 25 26 /// Mock time, as a value 27 /// 28 /// Contains an `Instant`, `SystemTime` and `CoarseInstant`. 29 /// 30 /// Arranges that they are all moved in step, 31 /// unless explicitly requested otherwise. 32 #[derive(Clone, Debug, Deftly)] 33 #[derive_deftly(CrateGetters)] 34 pub(crate) struct MockTimeCore { 35 /// Current time (monotonic clock) 36 #[deftly(getter_copy)] 37 instant: Instant, 38 39 /// Current wallclock time 40 #[deftly(getter_copy)] 41 wallclock: SystemTime, 42 43 /// Coarse time tracking 44 coarse: MockCoarseTimeProvider, 45 } 46 47 impl MockTimeCore { 48 /// Create a new `MockTimeCore` 49 pub(crate) fn new(instant: Instant, wallclock: SystemTime) -> Self { 50 MockTimeCore { 51 instant, 52 coarse: MockCoarseTimeProvider::new(), 53 wallclock, 54 } 55 } 56 57 /// Advance by a duration 58 /// 59 /// All three time values are advanced in step. 60 pub(crate) fn advance(&mut self, d: Duration) { 61 self.instant += d; 62 self.wallclock += d; 63 self.coarse.advance(d); 64 } 65 66 /// Warp the wallclock (only) 67 // 68 // We *could* just expose the field for mutable access, 69 // but this way seems more regular. 70 pub(crate) fn jump_wallclock(&mut self, new_wallclock: SystemTime) { 71 self.wallclock = new_wallclock; 72 } 73 } 74 75 /// A mockable [`CoarseTimeProvider`] 76 #[derive(Clone, Debug)] 77 pub(crate) struct MockCoarseTimeProvider { 78 /// Starting point 79 started: CoarseInstant, 80 81 /// How much we have advanced 82 /// 83 /// We track this as a `Duration`, not a [`CoarseDuration`] (or [`CoarseInstant`]) 84 /// to avoid accumulating rounding errors, 85 /// which might otherwise cause the mocked `Instant` and `CoarseInstant` 86 /// clocks to run at noticeably different *rates*. 87 elapsed: Duration, 88 } 89 90 impl MockCoarseTimeProvider { 91 /// Start a new [`MockCoarseTimeProvider`] 92 pub(crate) fn new() -> Self { 93 MockCoarseTimeProvider { 94 started: RealCoarseTimeProvider::new().now_coarse(), 95 elapsed: Duration::ZERO, 96 } 97 } 98 99 /// Advance the mocked coarse time by `dur` 100 pub(crate) fn advance(&mut self, dur: Duration) { 101 self.elapsed += dur; 102 } 103 } 104 105 impl CoarseTimeProvider for MockCoarseTimeProvider { 106 fn now_coarse(&self) -> CoarseInstant { 107 self.started + CoarseDuration::from(self.elapsed) 108 } 109 }