tor_rtmock/sleep_runtime.rs
1//! Declare MockSleepRuntime.
2
3use pin_project::pin_project;
4use tracing::trace;
5
6use crate::time::MockSleepProvider;
7
8use crate::util::impl_runtime_prelude::*;
9
10/// A deprecated wrapper Runtime that overrides SleepProvider for the
11/// underlying runtime.
12///
13/// ### Deprecated
14///
15/// The [`MockSleepProvider`] used here has some limitations.
16/// See its documentation for more information.
17/// Use [`MockRuntime`](crate::MockRuntime) for new tests.
18#[derive(Clone, Debug, Deftly)]
19#[derive_deftly(SomeMockRuntime)]
20// TODO #1885, see MockSleepProvider's cfg_attr deprecated
21#[cfg_attr(not(test), deprecated(since = "0.29.0"))]
22pub struct MockSleepRuntime<R: Runtime> {
23 /// The underlying runtime. Most calls get delegated here.
24 #[deftly(mock(task, net))]
25 #[deftly(mock(toplevel_where = "R: ToplevelBlockOn"))]
26 runtime: R,
27 /// A MockSleepProvider. Time-related calls get delegated here.
28 #[deftly(mock(sleep))]
29 sleep: MockSleepProvider,
30}
31
32impl<R: Runtime> MockSleepRuntime<R> {
33 /// Create a new runtime that wraps `runtime`, but overrides
34 /// its view of time with a [`MockSleepProvider`].
35 pub fn new(runtime: R) -> Self {
36 let sleep = MockSleepProvider::new(SystemTime::now());
37 MockSleepRuntime { runtime, sleep }
38 }
39
40 /// Return a reference to the underlying runtime.
41 pub fn inner(&self) -> &R {
42 &self.runtime
43 }
44
45 /// Return a reference to the [`MockSleepProvider`]
46 pub fn mock_sleep(&self) -> &MockSleepProvider {
47 &self.sleep
48 }
49
50 /// See [`MockSleepProvider::advance()`]
51 pub async fn advance(&self, dur: Duration) {
52 self.sleep.advance(dur).await;
53 }
54 /// See [`MockSleepProvider::jump_to()`]
55 pub fn jump_to(&self, new_wallclock: SystemTime) {
56 self.sleep.jump_to(new_wallclock);
57 }
58 /// Run a future under mock time, advancing time forward where necessary until it completes.
59 /// Users of this function should read the whole of this documentation before using!
60 ///
61 /// **NOTE** Instead of using this, consider [`MockRuntime`](crate::MockRuntime),
62 /// which will fully isolate the test case
63 /// (albeit at the cost of demanding manual management of the simulated time).
64 ///
65 /// The returned future will run `fut`, expecting it to create `Sleeping` futures (as returned
66 /// by `MockSleepProvider::sleep()` and similar functions). When all such created futures have
67 /// been polled (indicating the future is waiting on them), time will be advanced in order that
68 /// the first (or only) of said futures returns `Ready`. This process then repeats until `fut`
69 /// returns `Ready` itself (as in, the returned wrapper future will wait for all created
70 /// `Sleeping` futures to be polled, and advance time again).
71 ///
72 /// **Note:** The above described algorithm interacts poorly with futures that spawn
73 /// asynchronous background tasks, or otherwise expect work to complete in the background
74 /// before time is advanced. These futures will need to make use of the
75 /// `SleepProvider::block_advance` (and similar) APIs in order to prevent time advancing while
76 /// said tasks complete; see the documentation for those APIs for more detail.
77 ///
78 /// # Panics
79 ///
80 /// Panics if another `WaitFor` future is already running. (If two ran simultaneously, they
81 /// would both try and advance the same mock time clock, which would be bad.)
82 pub fn wait_for<F: futures::Future>(&self, fut: F) -> WaitFor<F> {
83 assert!(
84 !self.sleep.has_waitfor_waker(),
85 "attempted to call MockSleepRuntime::wait_for while another WaitFor is active"
86 );
87 WaitFor {
88 sleep: self.sleep.clone(),
89 fut,
90 }
91 }
92}
93
94/// A future that advances time until another future is ready to complete.
95#[pin_project]
96pub struct WaitFor<F> {
97 /// A reference to the sleep provider that's simulating time for us.
98 #[pin]
99 sleep: MockSleepProvider,
100 /// The future that we're waiting for.
101 #[pin]
102 fut: F,
103}
104
105use std::pin::Pin;
106use std::task::{Context, Poll};
107
108impl<F: Future> Future for WaitFor<F> {
109 type Output = F::Output;
110
111 #[allow(clippy::cognitive_complexity)]
112 fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
113 trace!("waitfor poll");
114 let mut this = self.project();
115 this.sleep.register_waitfor_waker(cx.waker().clone());
116
117 if let Poll::Ready(r) = this.fut.poll(cx) {
118 trace!("waitfor done!");
119 this.sleep.clear_waitfor_waker();
120 return Poll::Ready(r);
121 }
122 trace!("waitfor poll complete");
123
124 if this.sleep.should_advance() {
125 if let Some(duration) = this.sleep.time_until_next_timeout() {
126 trace!("Advancing by {:?}", duration);
127 this.sleep.advance_noyield(duration);
128 } else {
129 // If we get here, something's probably wedged and the test isn't going to complete
130 // anyway: we were expecting to advance in order to make progress, but we can't.
131 // If we don't panic, the test will just run forever, which is really annoying, so
132 // just panic and fail quickly.
133 panic!("WaitFor told to advance, but didn't have any duration to advance by");
134 }
135 } else {
136 trace!("waiting for sleepers to advance");
137 }
138 Poll::Pending
139 }
140}