summaryrefslogtreecommitdiff
path: root/crates/tor-rtmock/src
diff options
context:
space:
mode:
Diffstat (limited to 'crates/tor-rtmock/src')
-rw-r--r--crates/tor-rtmock/src/coarse_time.rs41
-rw-r--r--crates/tor-rtmock/src/lib.rs2
-rw-r--r--crates/tor-rtmock/src/simple_time.rs35
-rw-r--r--crates/tor-rtmock/src/time.rs49
-rw-r--r--crates/tor-rtmock/src/time_core.rs109
5 files changed, 147 insertions, 89 deletions
diff --git a/crates/tor-rtmock/src/coarse_time.rs b/crates/tor-rtmock/src/coarse_time.rs
deleted file mode 100644
index b96cc9f0f..000000000
--- a/crates/tor-rtmock/src/coarse_time.rs
+++ /dev/null
@@ -1,41 +0,0 @@
-//! [`MockCoarseTimeProvider`]
-
-use std::time::Duration;
-use tor_rtcompat::{CoarseDuration, CoarseInstant};
-use tor_rtcompat::{CoarseTimeProvider, RealCoarseTimeProvider};
-
-/// A mockable [`CoarseTimeProvider`]
-#[derive(Clone, Debug)]
-pub(crate) struct MockCoarseTimeProvider {
- /// Starting point
- started: CoarseInstant,
-
- /// How much we have advanced
- ///
- /// We track this as a `Duration`, not a [`CoarseDuration`] (or [`CoarseInstant`])
- /// to avoid accumulating rounding errors,
- /// which might otherwise cause the mocked `Instant` and `CoarseInstant`
- /// clocks to run at noticeably different *rates*.
- elapsed: Duration,
-}
-
-impl MockCoarseTimeProvider {
- /// Start a new [`MockCoarseTimeProvider`]
- pub(crate) fn new() -> Self {
- MockCoarseTimeProvider {
- started: RealCoarseTimeProvider::new().now_coarse(),
- elapsed: Duration::ZERO,
- }
- }
-
- /// Advance the mocked coarse time by `dur`
- pub(crate) fn advance(&mut self, dur: Duration) {
- self.elapsed += dur;
- }
-}
-
-impl CoarseTimeProvider for MockCoarseTimeProvider {
- fn now_coarse(&self) -> CoarseInstant {
- self.started + CoarseDuration::from(self.elapsed)
- }
-}
diff --git a/crates/tor-rtmock/src/lib.rs b/crates/tor-rtmock/src/lib.rs
index 56685f691..e59d88ba2 100644
--- a/crates/tor-rtmock/src/lib.rs
+++ b/crates/tor-rtmock/src/lib.rs
@@ -52,10 +52,10 @@ pub mod simple_time;
pub mod task;
pub mod time;
-mod coarse_time;
mod net_runtime;
mod runtime;
mod sleep_runtime;
+mod time_core;
pub use net_runtime::MockNetRuntime;
pub use runtime::MockRuntime;
diff --git a/crates/tor-rtmock/src/simple_time.rs b/crates/tor-rtmock/src/simple_time.rs
index afff87811..1b1c488de 100644
--- a/crates/tor-rtmock/src/simple_time.rs
+++ b/crates/tor-rtmock/src/simple_time.rs
@@ -17,7 +17,7 @@ use tor_rtcompat::CoarseInstant;
use tor_rtcompat::CoarseTimeProvider;
use tor_rtcompat::SleepProvider;
-use crate::coarse_time::MockCoarseTimeProvider;
+use crate::time_core::MockTimeCore;
/// Simple provider of simulated time
///
@@ -71,14 +71,8 @@ pub struct SleepFuture {
/// | DROPPED | dropped | absent | absent |
#[derive(Debug, AsMut)]
struct State {
- /// Current time
- now: Instant,
-
/// Current time (coarse)
- coarse: MockCoarseTimeProvider,
-
- /// Current wallclock time
- wallclock: SystemTime,
+ core: MockTimeCore,
/// Futures; record of every existing [`SleepFuture`], including any `Waker`
///
@@ -113,9 +107,7 @@ impl Provider {
/// Return a new mock time provider starting at a specified point in time
pub fn new(now: Instant, wallclock: SystemTime) -> Self {
let state = State {
- now,
- coarse: MockCoarseTimeProvider::new(),
- wallclock,
+ core: MockTimeCore::new(now, wallclock),
futures: Default::default(),
unready: Default::default(),
};
@@ -161,9 +153,7 @@ impl Provider {
/// use [`MockExecutor::advance_*()`](crate::MockRuntime).
pub fn advance(&self, d: Duration) {
let mut state = self.lock();
- state.now += d;
- state.coarse.advance(d);
- state.wallclock += d;
+ state.core.advance(d);
state.wake_any();
}
@@ -172,8 +162,7 @@ impl Provider {
/// This has no effect on any sleeping futures.
/// It only affects the return value from [`.wallclock()`](Provider::wallclock).
pub fn jump_wallclock(&self, new_wallclock: SystemTime) {
- let mut state = self.lock();
- state.wallclock = new_wallclock;
+ self.lock().core.jump_wallclock(new_wallclock);
// Really we ought to wake people up, here.
// But absolutely every Rust API is wrong: none offer a way to sleep until a SystemTime.
// (There might be some less-portable non-Rust APIs for that.)
@@ -194,7 +183,7 @@ impl Provider {
let Reverse(until) = state.unready.peek()?.1;
// The invariant (see `State`) guarantees that entries in `unready` are always `> now`,
// so we don't whether duration_since would panic or saturate.
- let d = until.duration_since(state.now);
+ let d = until.duration_since(state.core.instant());
Some(d)
}
@@ -209,7 +198,7 @@ impl SleepProvider for Provider {
fn sleep(&self, d: Duration) -> SleepFuture {
let mut state = self.lock();
- let until = state.now + d;
+ let until = state.core.instant() + d;
let id = state.futures.insert(None);
state.unready.push(id, Reverse(until));
@@ -231,16 +220,16 @@ impl SleepProvider for Provider {
}
fn now(&self) -> Instant {
- self.lock().now
+ self.lock().core.instant()
}
fn wallclock(&self) -> SystemTime {
- self.lock().wallclock
+ self.lock().core.wallclock()
}
}
impl CoarseTimeProvider for Provider {
fn now_coarse(&self) -> CoarseInstant {
- self.lock().coarse.now_coarse()
+ self.lock().core.coarse().now_coarse()
}
}
@@ -251,7 +240,7 @@ impl Future for SleepFuture {
let mut state = self.prov.lock();
if let Some((_, Reverse(scheduled))) = state.unready.get(&self.id) {
// Presence of this entry implies scheduled > now: we are UNPOLLED or WAITING
- assert!(*scheduled > state.now);
+ assert!(*scheduled > state.core.instant());
let waker = Some(cx.waker().clone());
// Make this be WAITING. (If we're re-polled, we simply drop any previous waker.)
*state
@@ -276,7 +265,7 @@ impl State {
loop {
match self.unready.peek() {
// Keep picking off entries with scheduled <= now
- Some((_, Reverse(scheduled))) if *scheduled <= self.now => {
+ Some((_, Reverse(scheduled))) if *scheduled <= self.core.instant() => {
let (id, _) = self.unready.pop().expect("vanished");
// We can .take() the waker since this can only ever run once
// per sleep future (since it happens when we pop it from unready).
diff --git a/crates/tor-rtmock/src/time.rs b/crates/tor-rtmock/src/time.rs
index 1f3a9623f..58272fac6 100644
--- a/crates/tor-rtmock/src/time.rs
+++ b/crates/tor-rtmock/src/time.rs
@@ -29,7 +29,7 @@ use std::collections::HashSet;
use std::fmt::Formatter;
use tor_rtcompat::{CoarseInstant, CoarseTimeProvider, SleepProvider};
-use crate::coarse_time::MockCoarseTimeProvider;
+use crate::time_core::MockTimeCore;
/// A dummy [`SleepProvider`] instance for testing.
///
@@ -161,14 +161,8 @@ impl fmt::Debug for MockSleepProvider {
/// Shared backend for sleep provider and Sleeping futures.
struct SleepSchedule {
- /// What time do we pretend it is (monotonic)? This value only
- /// moves forward.
- instant: Instant,
- /// Coarse time tracker
- coarse: MockCoarseTimeProvider,
- /// What time do we pretend it is (wall clock)? This value can move
- /// in any way, but usually moves in step with `instant`.
- wallclock: SystemTime,
+ /// What time do we pretend it is?
+ core: MockTimeCore,
/// Priority queue of events, in the order that we should wake them.
sleepers: BinaryHeap<SleepEntry>,
/// If the mock time system is being driven by a `WaitFor`, holds a `Waker` to wake up that
@@ -216,10 +210,9 @@ impl MockSleepProvider {
pub fn new(wallclock: SystemTime) -> Self {
let instant = Instant::now();
let sleepers = BinaryHeap::new();
+ let core = MockTimeCore::new(instant, wallclock);
let state = SleepSchedule {
- instant,
- coarse: MockCoarseTimeProvider::new(),
- wallclock,
+ core,
sleepers,
waitfor_waker: None,
sleepers_made: 0,
@@ -258,9 +251,7 @@ impl MockSleepProvider {
// It's not so great to unwrap here in general, but since this is
// only testing code we don't really care.
let mut state = self.state.lock().expect("Poisoned lock for state");
- state.wallclock += dur;
- state.instant += dur;
- state.coarse.advance(dur);
+ state.core.advance(dur);
state.fire();
}
@@ -273,7 +264,7 @@ impl MockSleepProvider {
/// the internal timer state, and the lock is poisoned.
pub fn jump_to(&self, new_wallclock: SystemTime) {
let mut state = self.state.lock().expect("Poisoned lock for state");
- state.wallclock = new_wallclock;
+ state.core.jump_wallclock(new_wallclock);
}
/// Return the amount of virtual time until the next timeout
@@ -283,7 +274,7 @@ impl MockSleepProvider {
/// timeout should elapse right now, return Some(0).
pub(crate) fn time_until_next_timeout(&self) -> Option<Duration> {
let state = self.state.lock().expect("Poisoned lock for state");
- let now = state.instant;
+ let now = state.core.instant();
state
.sleepers
.peek()
@@ -327,7 +318,7 @@ impl MockSleepProvider {
// we have quota to advance up to a certain time while advances are blocked.
// Let's see when the next timeout is, and whether it falls within that quota.
let next_timeout = {
- let now = state.instant;
+ let now = state.core.instant();
state
.sleepers
.peek()
@@ -391,7 +382,7 @@ impl SleepSchedule {
fn fire(&mut self) {
use std::collections::binary_heap::PeekMut;
- let now = self.instant;
+ let now = self.core.instant();
while let Some(top) = self.sleepers.peek_mut() {
if now < top.when {
return;
@@ -436,7 +427,7 @@ impl SleepProvider for MockSleepProvider {
type SleepFuture = Sleeping;
fn sleep(&self, duration: Duration) -> Self::SleepFuture {
let mut provider = self.state.lock().expect("Poisoned lock for state");
- let when = provider.instant + duration;
+ let when = provider.core.instant() + duration;
// We're making a new sleeper, so register this in the state.
provider.sleepers_made += 1;
trace!(
@@ -481,20 +472,30 @@ impl SleepProvider for MockSleepProvider {
}
fn now(&self) -> Instant {
- self.state.lock().expect("Poisoned lock for state").instant
+ self.state
+ .lock()
+ .expect("Poisoned lock for state")
+ .core
+ .instant()
}
fn wallclock(&self) -> SystemTime {
self.state
.lock()
.expect("Poisoned lock for state")
- .wallclock
+ .core
+ .wallclock()
}
}
impl CoarseTimeProvider for MockSleepProvider {
fn now_coarse(&self) -> CoarseInstant {
- self.state.lock().expect("poisoned").coarse.now_coarse()
+ self.state
+ .lock()
+ .expect("poisoned")
+ .core
+ .coarse()
+ .now_coarse()
}
}
@@ -535,7 +536,7 @@ impl Future for Sleeping {
fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<()> {
if let Some(provider) = Weak::upgrade(&self.provider) {
let mut provider = provider.lock().expect("Poisoned lock for provider");
- let now = provider.instant;
+ let now = provider.core.instant();
if now >= self.when {
// The sleep time's elapsed.
diff --git a/crates/tor-rtmock/src/time_core.rs b/crates/tor-rtmock/src/time_core.rs
new file mode 100644
index 000000000..e4cabe30d
--- /dev/null
+++ b/crates/tor-rtmock/src/time_core.rs
@@ -0,0 +1,109 @@
+//! [`MockTimeCore`] and [`MockCoarseTimeProvider`]
+
+use derive_adhoc::{define_derive_adhoc, Adhoc};
+use std::time::{Duration, Instant, SystemTime};
+use tor_rtcompat::{CoarseDuration, CoarseInstant};
+use tor_rtcompat::{CoarseTimeProvider, RealCoarseTimeProvider};
+
+define_derive_adhoc! {
+ /// Derive getters for struct fields.
+ ///
+ /// Like `amplify::Getters` but `pub(crate)`.
+ ///
+ /// TODO add this feature to `amplify`.
+ CrateGetters =
+ ${define REF ${if not(fmeta(getter_copy)) { & }}}
+ $(
+ impl $ttype {
+ ${fattrs doc}
+ pub(crate) fn $fname(&self) -> $REF $ftype {
+ $REF self.$fname
+ }
+ }
+ )
+}
+
+/// Mock time, as a value
+///
+/// Contains an `Instant`, `SystemTime` and `CoarseInstant`.
+///
+/// Arranges that they are all moved in step,
+/// unless explicitly requested otherwise.
+#[derive(Clone, Debug, Adhoc)]
+#[derive_adhoc(CrateGetters)]
+pub(crate) struct MockTimeCore {
+ /// Current time (monotonic clock)
+ #[adhoc(getter_copy)]
+ instant: Instant,
+
+ /// Current wallclock time
+ #[adhoc(getter_copy)]
+ wallclock: SystemTime,
+
+ /// Coarse time tracking
+ coarse: MockCoarseTimeProvider,
+}
+
+impl MockTimeCore {
+ /// Create a new `MockTimeCore`
+ pub(crate) fn new(instant: Instant, wallclock: SystemTime) -> Self {
+ MockTimeCore {
+ instant,
+ coarse: MockCoarseTimeProvider::new(),
+ wallclock,
+ }
+ }
+
+ /// Advance by a duration
+ ///
+ /// All three time values are advanced in step.
+ pub(crate) fn advance(&mut self, d: Duration) {
+ self.instant += d;
+ self.wallclock += d;
+ self.coarse.advance(d);
+ }
+
+ /// Warp the wallclock (only)
+ //
+ // We *could* just expose the field for mutable access,
+ // but this way seems more regular.
+ pub(crate) fn jump_wallclock(&mut self, new_wallclock: SystemTime) {
+ self.wallclock = new_wallclock;
+ }
+}
+
+/// A mockable [`CoarseTimeProvider`]
+#[derive(Clone, Debug)]
+pub(crate) struct MockCoarseTimeProvider {
+ /// Starting point
+ started: CoarseInstant,
+
+ /// How much we have advanced
+ ///
+ /// We track this as a `Duration`, not a [`CoarseDuration`] (or [`CoarseInstant`])
+ /// to avoid accumulating rounding errors,
+ /// which might otherwise cause the mocked `Instant` and `CoarseInstant`
+ /// clocks to run at noticeably different *rates*.
+ elapsed: Duration,
+}
+
+impl MockCoarseTimeProvider {
+ /// Start a new [`MockCoarseTimeProvider`]
+ pub(crate) fn new() -> Self {
+ MockCoarseTimeProvider {
+ started: RealCoarseTimeProvider::new().now_coarse(),
+ elapsed: Duration::ZERO,
+ }
+ }
+
+ /// Advance the mocked coarse time by `dur`
+ pub(crate) fn advance(&mut self, dur: Duration) {
+ self.elapsed += dur;
+ }
+}
+
+impl CoarseTimeProvider for MockCoarseTimeProvider {
+ fn now_coarse(&self) -> CoarseInstant {
+ self.started + CoarseDuration::from(self.elapsed)
+ }
+}