1
//! Declare MockSleepRuntime.
2

            
3
use pin_project::pin_project;
4
use tracing::trace;
5

            
6
use crate::time::MockSleepProvider;
7

            
8
use 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"))]
22
pub 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

            
32
impl<R: Runtime> MockSleepRuntime<R> {
33
    /// Create a new runtime that wraps `runtime`, but overrides
34
    /// its view of time with a [`MockSleepProvider`].
35
68
    pub fn new(runtime: R) -> Self {
36
68
        let sleep = MockSleepProvider::new(SystemTime::now());
37
68
        MockSleepRuntime { runtime, sleep }
38
68
    }
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
26
    pub async fn advance(&self, dur: Duration) {
52
26
        self.sleep.advance(dur).await;
53
26
    }
54
    /// See [`MockSleepProvider::jump_to()`]
55
2
    pub fn jump_to(&self, new_wallclock: SystemTime) {
56
2
        self.sleep.jump_to(new_wallclock);
57
2
    }
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
216
    pub fn wait_for<F: futures::Future>(&self, fut: F) -> WaitFor<F> {
83
216
        assert!(
84
216
            !self.sleep.has_waitfor_waker(),
85
            "attempted to call MockSleepRuntime::wait_for while another WaitFor is active"
86
        );
87
216
        WaitFor {
88
216
            sleep: self.sleep.clone(),
89
216
            fut,
90
216
        }
91
216
    }
92
}
93

            
94
/// A future that advances time until another future is ready to complete.
95
9154
#[pin_project]
96
pub 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

            
105
use std::pin::Pin;
106
use std::task::{Context, Poll};
107

            
108
impl<F: Future> Future for WaitFor<F> {
109
    type Output = F::Output;
110

            
111
    #[allow(clippy::cognitive_complexity)]
112
9154
    fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
113
9154
        trace!("waitfor poll");
114
9154
        let mut this = self.project();
115
9154
        this.sleep.register_waitfor_waker(cx.waker().clone());
116

            
117
9154
        if let Poll::Ready(r) = this.fut.poll(cx) {
118
216
            trace!("waitfor done!");
119
216
            this.sleep.clear_waitfor_waker();
120
216
            return Poll::Ready(r);
121
8938
        }
122
8938
        trace!("waitfor poll complete");
123

            
124
8938
        if this.sleep.should_advance() {
125
8740
            if let Some(duration) = this.sleep.time_until_next_timeout() {
126
8740
                trace!("Advancing by {:?}", duration);
127
8740
                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
198
            trace!("waiting for sleepers to advance");
137
        }
138
8938
        Poll::Pending
139
9154
    }
140
}