tor_rtmock/time.rs
1//! Functionality for simulating the passage of time in unit tests.
2//!
3//! We do this by providing [`MockSleepProvider`], a "SleepProvider"
4//! instance that can simulate timeouts and retries without requiring
5//! the actual system clock to advance.
6//!
7//! ### Deprecated
8//!
9//! This mock time facility has some limitations.
10//! See [`MockSleepProvider`] for more information.
11//! Use [`MockRuntime`](crate::MockRuntime) for new tests.
12
13#![forbid(unsafe_code)] // if you remove this, enable (or write) miri tests (git grep miri)
14#![allow(clippy::missing_docs_in_private_items)]
15
16use std::{
17 cmp::{Eq, Ordering, PartialEq, PartialOrd},
18 collections::BinaryHeap,
19 fmt,
20 pin::Pin,
21 sync::{Arc, Mutex, Weak},
22 task::{Context, Poll, Waker},
23};
24use web_time_compat::{Duration, Instant, InstantExt, SystemTime};
25
26use futures::Future;
27use tracing::trace;
28
29use std::collections::HashSet;
30use std::fmt::Formatter;
31use tor_rtcompat::{CoarseInstant, CoarseTimeProvider, SleepProvider};
32
33use crate::time_core::MockTimeCore;
34
35/// A dummy [`SleepProvider`] instance for testing.
36///
37/// The MockSleepProvider ignores the current time, and instead keeps
38/// its own view of the current `Instant` and `SystemTime`. You
39/// can advance them in-step by calling `advance()`, and you can simulate
40/// jumps in the system clock by calling `jump()`.
41///
42/// This is *not* for production use.
43///
44/// ### Deprecated
45///
46/// This mock time facility has some limitations, notably lack of support for tasks,
47/// and a confusing API for controlling the mock time.
48///
49/// New test cases should probably use `MockRuntime`
50/// which incorporates `MockSimpletimeProvider`.
51///
52/// Comparison of `MockSleepProvider` with `SimpleMockTimeProvider`:
53///
54/// * `SimpleMockTimeProvider` does not support, or expect the use of,
55/// `block_advance` et al.
56/// Instead, the advancement of simulated time is typically done automatically
57/// in cooperation with the executor,
58/// using `MockRuntime`'s `advance_*` methods.
59///
60/// * Consequently, `SimpleMockTimeProvider` can be used in test cases that
61/// spawn tasks and perform sleeps in them.
62///
63/// * And, consequently, `SimpleMockTimeProvider` does not need non-test code to
64/// contain calls which are solely related to getting the time mocking to work right.
65///
66/// * `SimpleMockTimeProvider` gives correct sleeping locations
67/// with `MockExecutor`'s dump of sleeping tasks' stack traces.
68///
69/// * Conversely, to use `SimpleMockTimeProvider` in all but the most simple test cases,
70/// coordination with the executor is required.
71/// This coordination is provided by the integrated `MockRuntime`;
72/// `SimpleMockTimeProvider` is of limited usefulness by itself.
73///
74/// ### Examples
75///
76/// Suppose you've written a function that relies on making a
77/// connection to the network and possibly timing out:
78///
79/// ```rust
80/// use tor_rtcompat::{Runtime,SleepProviderExt};
81/// use std::{net::SocketAddr, io::Result, time::Duration, io::Error};
82/// use futures::io::AsyncWriteExt;
83///
84/// async fn say_hi(runtime: impl Runtime, addr: &SocketAddr) -> Result<()> {
85/// let delay = Duration::new(5,0);
86/// runtime.timeout(delay, async {
87/// let mut conn = runtime.connect(addr, &Default::default()).await?;
88/// conn.write_all(b"Hello world!\r\n").await?;
89/// conn.close().await?;
90/// Ok::<_,Error>(())
91/// }).await??;
92/// Ok(())
93/// }
94/// ```
95///
96/// But how should you test this function?
97///
98/// You might try connecting to a well-known website to test the
99/// connection case, and to a well-known black hole to test the
100/// timeout case... but that's a bit undesirable. Your tests might be
101/// running in a container with no internet access; and even if they
102/// aren't, it isn't so great for your tests to rely on the actual
103/// state of the internet. Similarly, if you make your timeout too long,
104/// your tests might block for a long time; but if your timeout is too short,
105/// the tests might fail on a slow machine or on a slow network.
106///
107/// Or, you could solve both of these problems by using `tor-rtmock`
108/// to replace the internet _and_ the passage of time. (Here we're only
109/// replacing the internet.)
110///
111/// ```rust,no_run
112/// # async fn say_hi<R,A>(runtime: R, addr: A) -> Result<(), ()> { Ok(()) }
113/// # // TODO this test hangs for some reason? Fix it and remove no_run above
114/// use tor_rtmock::{MockSleepRuntime,MockNetRuntime,net::MockNetwork};
115/// use tor_rtcompat::{NetStreamProvider,NetStreamListener};
116/// use futures::io::AsyncReadExt;
117/// use std::net::SocketAddr;
118/// use futures::StreamExt as _;
119///
120/// tor_rtcompat::test_with_all_runtimes!(|rt| async move {
121///
122/// let addr1 = "198.51.100.7".parse().unwrap();
123/// let addr2 = "198.51.100.99".parse().unwrap();
124/// let sockaddr: SocketAddr = "198.51.100.99:101".parse().unwrap();
125///
126/// // Make a runtime that pretends that we are at the first address...
127/// let fake_internet = MockNetwork::new();
128/// let rt1 = fake_internet.builder().add_address(addr1).runtime(rt.clone());
129/// // ...and one that pretends we're listening at the second address.
130/// let rt2 = fake_internet.builder().add_address(addr2).runtime(rt);
131/// let listener = rt2.listen(&sockaddr, &Default::default()).await.unwrap();
132/// let mut incoming_stream = listener.incoming();
133///
134/// // Now we can test our function!
135/// let (result1,output) = futures::join!(
136/// say_hi(rt1, &sockaddr),
137/// async {
138/// let (mut conn,addr) = incoming_stream.next().await.unwrap().unwrap();
139/// assert_eq!(addr.ip(), addr1);
140/// let mut output = Vec::new();
141/// conn.read_to_end(&mut output).await.unwrap();
142/// output
143/// });
144///
145/// assert!(result1.is_ok());
146/// assert_eq!(&output[..], b"Hello world!\r\n");
147/// });
148/// ```
149#[derive(Clone)]
150// When we're used by external crates, we're always cfg(not(test)), so we seem deprecated
151// from outside this crate. *Within* this crate, this cfg_attr means that if we use things
152// that are deprecated for other reasons, we will notice.
153#[cfg_attr(not(test), deprecated(since = "0.29.0"))]
154pub struct MockSleepProvider {
155 /// The shared backend for this MockSleepProvider and its futures.
156 state: Arc<Mutex<SleepSchedule>>,
157}
158
159impl fmt::Debug for MockSleepProvider {
160 fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
161 f.debug_struct("MockSleepProvider").finish_non_exhaustive()
162 }
163}
164
165/// Shared backend for sleep provider and Sleeping futures.
166struct SleepSchedule {
167 /// What time do we pretend it is?
168 core: MockTimeCore,
169 /// Priority queue of events, in the order that we should wake them.
170 sleepers: BinaryHeap<SleepEntry>,
171 /// If the mock time system is being driven by a `WaitFor`, holds a `Waker` to wake up that
172 /// `WaitFor` in order for it to make more progress.
173 waitfor_waker: Option<Waker>,
174 /// Number of sleepers instantiated.
175 sleepers_made: usize,
176 /// Number of sleepers polled.
177 sleepers_polled: usize,
178 /// Whether an advance is needed.
179 should_advance: bool,
180 /// A set of reasons why advances shouldn't be allowed right now.
181 blocked_advance: HashSet<String>,
182 /// A time up to which advances are allowed, irrespective of them being blocked.
183 allowed_advance: Duration,
184}
185
186/// An entry telling us when to wake which future up.
187struct SleepEntry {
188 /// The time at which this entry should wake
189 when: Instant,
190 /// The Waker to call when the instant has passed.
191 waker: Waker,
192}
193
194/// A future returned by [`MockSleepProvider::sleep()`].
195pub struct Sleeping {
196 /// The instant when we should become ready.
197 when: Instant,
198 /// True if we have pushed this into the queue.
199 inserted: bool,
200 /// The schedule to queue ourselves in if we're polled before we're ready.
201 provider: Weak<Mutex<SleepSchedule>>,
202}
203
204impl Default for MockSleepProvider {
205 fn default() -> Self {
206 let wallclock = humantime::parse_rfc3339("2023-07-05T11:25:56Z").expect("parse");
207 MockSleepProvider::new(wallclock)
208 }
209}
210
211impl MockSleepProvider {
212 /// Create a new MockSleepProvider, starting at a given wall-clock time.
213 pub fn new(wallclock: SystemTime) -> Self {
214 let instant = Instant::get();
215 let sleepers = BinaryHeap::new();
216 let core = MockTimeCore::new(instant, wallclock);
217 let state = SleepSchedule {
218 core,
219 sleepers,
220 waitfor_waker: None,
221 sleepers_made: 0,
222 sleepers_polled: 0,
223 should_advance: false,
224 blocked_advance: HashSet::new(),
225 allowed_advance: Duration::from_nanos(0),
226 };
227 MockSleepProvider {
228 state: Arc::new(Mutex::new(state)),
229 }
230 }
231
232 /// Advance the simulated timeline forward by `dur`.
233 ///
234 /// Calling this function will wake any pending futures as
235 /// appropriate, and yield to the scheduler so they get a chance
236 /// to run.
237 ///
238 /// # Limitations
239 ///
240 /// This function advances time in one big step. We might instead
241 /// want to advance in small steps and make sure that each step's
242 /// futures can get run before the ones scheduled to run after it.
243 pub async fn advance(&self, dur: Duration) {
244 self.advance_noyield(dur);
245 tor_rtcompat::task::yield_now().await;
246 }
247
248 /// Advance the simulated timeline forward by `dur`.
249 ///
250 /// Calling this function will wake any pending futures as
251 /// appropriate, but not yield to the scheduler. Mostly you
252 /// should call [`advance`](Self::advance) instead.
253 pub(crate) fn advance_noyield(&self, dur: Duration) {
254 // It's not so great to unwrap here in general, but since this is
255 // only testing code we don't really care.
256 let mut state = self.state.lock().expect("Poisoned lock for state");
257 state.core.advance(dur);
258 state.fire();
259 }
260
261 /// Simulate a discontinuity in the system clock, by jumping to
262 /// `new_wallclock`.
263 ///
264 /// # Panics
265 ///
266 /// Panics if we have already panicked while holding the lock on
267 /// the internal timer state, and the lock is poisoned.
268 pub fn jump_to(&self, new_wallclock: SystemTime) {
269 let mut state = self.state.lock().expect("Poisoned lock for state");
270 state.core.jump_wallclock(new_wallclock);
271 }
272
273 /// Return the amount of virtual time until the next timeout
274 /// should elapse.
275 ///
276 /// If there are no more timeouts, return None. If the next
277 /// timeout should elapse right now, return Some(0).
278 pub(crate) fn time_until_next_timeout(&self) -> Option<Duration> {
279 let state = self.state.lock().expect("Poisoned lock for state");
280 let now = state.core.instant();
281 state
282 .sleepers
283 .peek()
284 .map(|sleepent| sleepent.when.saturating_duration_since(now))
285 }
286
287 /// Return true if a `WaitFor` driving this sleep provider should advance time in order for
288 /// futures blocked on sleeping to make progress.
289 ///
290 /// NOTE: This function has side-effects; if it returns true, the caller is expected to do an
291 /// advance before calling it again.
292 pub(crate) fn should_advance(&mut self) -> bool {
293 let mut state = self.state.lock().expect("Poisoned lock for state");
294 if !state.blocked_advance.is_empty() && state.allowed_advance == Duration::from_nanos(0) {
295 // We've had advances blocked, and don't have any quota for doing allowances while
296 // blocked left.
297 trace!(
298 "should_advance = false: blocked by {:?}",
299 state.blocked_advance
300 );
301 return false;
302 }
303 if !state.should_advance {
304 // The advance flag wasn't set.
305 trace!("should_advance = false; bit not previously set");
306 return false;
307 }
308 // Clear the advance flag; we'll either return true and cause an advance to happen,
309 // or the reasons to return false below also imply that the advance flag will be set again
310 // later on.
311 state.should_advance = false;
312 if state.sleepers_polled < state.sleepers_made {
313 // Something did set the advance flag before, but it's not valid any more now because
314 // more unpolled sleepers were created.
315 trace!("should_advance = false; advancing no longer valid");
316 return false;
317 }
318 if !state.blocked_advance.is_empty() && state.allowed_advance > Duration::from_nanos(0) {
319 // If we're here, we would've returned earlier due to having advances blocked, but
320 // we have quota to advance up to a certain time while advances are blocked.
321 // Let's see when the next timeout is, and whether it falls within that quota.
322 let next_timeout = {
323 let now = state.core.instant();
324 state
325 .sleepers
326 .peek()
327 .map(|sleepent| sleepent.when.saturating_duration_since(now))
328 };
329 let next_timeout = match next_timeout {
330 Some(x) => x,
331 None => {
332 // There's no timeout set, so we really shouldn't be here anyway.
333 trace!("should_advance = false; allow_one set but no timeout yet");
334 return false;
335 }
336 };
337 if next_timeout <= state.allowed_advance {
338 // We can advance up to the next timeout, since it's in our quota.
339 // Subtract the amount we're going to advance by from said quota.
340 state.allowed_advance -= next_timeout;
341 trace!(
342 "WARNING: allowing advance due to allow_one; new allowed is {:?}",
343 state.allowed_advance
344 );
345 } else {
346 // The next timeout is too far in the future.
347 trace!(
348 "should_advance = false; allow_one set but only up to {:?}, next is {:?}",
349 state.allowed_advance, next_timeout
350 );
351 return false;
352 }
353 }
354 true
355 }
356
357 /// Register a `Waker` to be woken up when an advance in time is required to make progress.
358 ///
359 /// This is used by `WaitFor`.
360 pub(crate) fn register_waitfor_waker(&mut self, waker: Waker) {
361 let mut state = self.state.lock().expect("Poisoned lock for state");
362 state.waitfor_waker = Some(waker);
363 }
364
365 /// Remove a previously registered `Waker` registered with `register_waitfor_waker()`.
366 pub(crate) fn clear_waitfor_waker(&mut self) {
367 let mut state = self.state.lock().expect("Poisoned lock for state");
368 state.waitfor_waker = None;
369 }
370
371 /// Returns true if a `Waker` has been registered with `register_waitfor_waker()`.
372 ///
373 /// This is used to ensure that you don't have two concurrent `WaitFor`s running.
374 pub(crate) fn has_waitfor_waker(&self) -> bool {
375 let state = self.state.lock().expect("Poisoned lock for state");
376 state.waitfor_waker.is_some()
377 }
378}
379
380impl SleepSchedule {
381 /// Wake any pending events that are ready according to the
382 /// current simulated time.
383 fn fire(&mut self) {
384 use std::collections::binary_heap::PeekMut;
385
386 let now = self.core.instant();
387 while let Some(top) = self.sleepers.peek_mut() {
388 if now < top.when {
389 return;
390 }
391
392 PeekMut::pop(top).waker.wake();
393 }
394 }
395
396 /// Add a new SleepEntry to this schedule.
397 fn push(&mut self, ent: SleepEntry) {
398 self.sleepers.push(ent);
399 }
400
401 /// If all sleepers made have been polled, set the advance flag and wake up any `WaitFor` that
402 /// might be waiting.
403 fn maybe_advance(&mut self) {
404 if self.sleepers_polled >= self.sleepers_made {
405 if let Some(ref waker) = self.waitfor_waker {
406 trace!("setting advance flag");
407 self.should_advance = true;
408 waker.wake_by_ref();
409 } else {
410 trace!("would advance, but no waker");
411 }
412 }
413 }
414
415 /// Register a sleeper as having been polled, and advance if necessary.
416 fn increment_poll_count(&mut self) {
417 self.sleepers_polled += 1;
418 trace!(
419 "sleeper polled, {}/{}",
420 self.sleepers_polled, self.sleepers_made
421 );
422 self.maybe_advance();
423 }
424}
425
426impl SleepProvider for MockSleepProvider {
427 type SleepFuture = Sleeping;
428 fn sleep(&self, duration: Duration) -> Self::SleepFuture {
429 let mut provider = self.state.lock().expect("Poisoned lock for state");
430 let when = provider.core.instant() + duration;
431 // We're making a new sleeper, so register this in the state.
432 provider.sleepers_made += 1;
433 trace!(
434 "sleeper made for {:?}, {}/{}",
435 duration, provider.sleepers_polled, provider.sleepers_made
436 );
437
438 Sleeping {
439 when,
440 inserted: false,
441 provider: Arc::downgrade(&self.state),
442 }
443 }
444
445 fn block_advance<T: Into<String>>(&self, reason: T) {
446 let mut provider = self.state.lock().expect("Poisoned lock for state");
447 let reason = reason.into();
448 trace!("advancing blocked: {}", reason);
449 provider.blocked_advance.insert(reason);
450 }
451
452 fn release_advance<T: Into<String>>(&self, reason: T) {
453 let mut provider = self.state.lock().expect("Poisoned lock for state");
454 let reason = reason.into();
455 trace!("advancing released: {}", reason);
456 provider.blocked_advance.remove(&reason);
457 if provider.blocked_advance.is_empty() {
458 provider.maybe_advance();
459 }
460 }
461
462 fn allow_one_advance(&self, dur: Duration) {
463 let mut provider = self.state.lock().expect("Poisoned lock for state");
464 provider.allowed_advance = Duration::max(provider.allowed_advance, dur);
465 trace!(
466 "** allow_one_advance fired; may advance up to {:?} **",
467 provider.allowed_advance
468 );
469 provider.maybe_advance();
470 }
471
472 fn now(&self) -> Instant {
473 self.state
474 .lock()
475 .expect("Poisoned lock for state")
476 .core
477 .instant()
478 }
479
480 fn wallclock(&self) -> SystemTime {
481 self.state
482 .lock()
483 .expect("Poisoned lock for state")
484 .core
485 .wallclock()
486 }
487}
488
489impl CoarseTimeProvider for MockSleepProvider {
490 fn now_coarse(&self) -> CoarseInstant {
491 self.state
492 .lock()
493 .expect("poisoned")
494 .core
495 .coarse()
496 .now_coarse()
497 }
498}
499
500impl PartialEq for SleepEntry {
501 fn eq(&self, other: &Self) -> bool {
502 self.when == other.when
503 }
504}
505impl Eq for SleepEntry {}
506impl PartialOrd for SleepEntry {
507 fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
508 Some(self.cmp(other))
509 }
510}
511impl Ord for SleepEntry {
512 fn cmp(&self, other: &Self) -> Ordering {
513 self.when.cmp(&other.when).reverse()
514 }
515}
516
517impl Drop for Sleeping {
518 fn drop(&mut self) {
519 if let Some(provider) = Weak::upgrade(&self.provider) {
520 let mut provider = provider.lock().expect("Poisoned lock for provider");
521 if !self.inserted {
522 // A sleeper being dropped will never be polled, so there's no point waiting;
523 // act as if it's been polled in order to avoid waiting forever.
524 trace!("sleeper dropped, incrementing count");
525 provider.increment_poll_count();
526 self.inserted = true;
527 }
528 }
529 }
530}
531
532impl Future for Sleeping {
533 type Output = ();
534 fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<()> {
535 if let Some(provider) = Weak::upgrade(&self.provider) {
536 let mut provider = provider.lock().expect("Poisoned lock for provider");
537 let now = provider.core.instant();
538
539 if now >= self.when {
540 // The sleep time's elapsed.
541 if !self.inserted {
542 // If we never registered this sleeper as being polled, do so now.
543 provider.increment_poll_count();
544 self.inserted = true;
545 }
546 if !provider.should_advance {
547 // The first advance during a `WaitFor` gets triggered by all sleepers that
548 // have been created being polled.
549 // However, this only happens once.
550 // What we do to get around this is have sleepers that return Ready kick off
551 // another advance, in order to wake the next waiting sleeper.
552 provider.maybe_advance();
553 }
554 return Poll::Ready(());
555 }
556 // dbg!("sleep check with", self.when-now);
557
558 if !self.inserted {
559 let entry = SleepEntry {
560 when: self.when,
561 waker: cx.waker().clone(),
562 };
563
564 provider.push(entry);
565 self.inserted = true;
566 // Register this sleeper as having been polled.
567 provider.increment_poll_count();
568 }
569 // dbg!(provider.sleepers.len());
570 }
571 Poll::Pending
572 }
573}
574
575#[cfg(all(test, not(miri)))] // miri cannot do CLOCK_REALTIME
576mod test {
577 // @@ begin test lint list maintained by maint/add_warning @@
578 #![allow(clippy::bool_assert_comparison)]
579 #![allow(clippy::clone_on_copy)]
580 #![allow(clippy::dbg_macro)]
581 #![allow(clippy::mixed_attributes_style)]
582 #![allow(clippy::print_stderr)]
583 #![allow(clippy::print_stdout)]
584 #![allow(clippy::single_char_pattern)]
585 #![allow(clippy::unwrap_used)]
586 #![allow(clippy::unchecked_time_subtraction)]
587 #![allow(clippy::useless_vec)]
588 #![allow(clippy::needless_pass_by_value)]
589 #![allow(clippy::string_slice)] // See arti#2571
590 //! <!-- @@ end test lint list maintained by maint/add_warning @@ -->
591 use super::*;
592 use tor_rtcompat::test_with_all_runtimes;
593 use web_time_compat::SystemTimeExt;
594
595 #[test]
596 fn basics_of_time_travel() {
597 let w1 = SystemTime::get();
598 let sp = MockSleepProvider::new(w1);
599 let i1 = sp.now();
600 assert_eq!(sp.wallclock(), w1);
601
602 let interval = Duration::new(4 * 3600 + 13 * 60, 0);
603 sp.advance_noyield(interval);
604 assert_eq!(sp.now(), i1 + interval);
605 assert_eq!(sp.wallclock(), w1 + interval);
606
607 sp.jump_to(w1 + interval * 3);
608 assert_eq!(sp.now(), i1 + interval);
609 assert_eq!(sp.wallclock(), w1 + interval * 3);
610 }
611
612 #[test]
613 fn time_moves_on() {
614 test_with_all_runtimes!(|_| async {
615 use oneshot_fused_workaround as oneshot;
616 use std::sync::atomic::AtomicBool;
617 use std::sync::atomic::Ordering;
618
619 let sp = MockSleepProvider::new(SystemTime::get());
620 let one_hour = Duration::new(3600, 0);
621
622 let (s1, r1) = oneshot::channel();
623 let (s2, r2) = oneshot::channel();
624 let (s3, r3) = oneshot::channel();
625
626 let b1 = AtomicBool::new(false);
627 let b2 = AtomicBool::new(false);
628 let b3 = AtomicBool::new(false);
629
630 let real_start = Instant::get();
631
632 futures::join!(
633 async {
634 sp.sleep(one_hour).await;
635 b1.store(true, Ordering::SeqCst);
636 s1.send(()).unwrap();
637 },
638 async {
639 sp.sleep(one_hour * 3).await;
640 b2.store(true, Ordering::SeqCst);
641 s2.send(()).unwrap();
642 },
643 async {
644 sp.sleep(one_hour * 5).await;
645 b3.store(true, Ordering::SeqCst);
646 s3.send(()).unwrap();
647 },
648 async {
649 sp.advance(one_hour * 2).await;
650 r1.await.unwrap();
651 assert!(b1.load(Ordering::SeqCst));
652 assert!(!b2.load(Ordering::SeqCst));
653 assert!(!b3.load(Ordering::SeqCst));
654
655 sp.advance(one_hour * 2).await;
656 r2.await.unwrap();
657 assert!(b1.load(Ordering::SeqCst));
658 assert!(b2.load(Ordering::SeqCst));
659 assert!(!b3.load(Ordering::SeqCst));
660
661 sp.advance(one_hour * 2).await;
662 r3.await.unwrap();
663 assert!(b1.load(Ordering::SeqCst));
664 assert!(b2.load(Ordering::SeqCst));
665 assert!(b3.load(Ordering::SeqCst));
666 let real_end = Instant::get();
667
668 assert!(real_end - real_start < one_hour);
669 }
670 );
671 std::io::Result::Ok(())
672 });
673 }
674}