Skip to main content

moq_net/
runtime.rs

1//! Private deadlines driven by the owning driver's supplied clock.
2
3use std::task::Poll;
4
5/// The instant type used for deadlines and annotations.
6///
7/// [`std::time::Instant`] on native. The browser has no monotonic std clock, so
8/// wasm substitutes an equivalent backed by `performance.now()`.
9#[cfg(not(target_family = "wasm"))]
10pub type Instant = std::time::Instant;
11/// The instant type used for deadlines and annotations (wasm shim).
12#[cfg(target_family = "wasm")]
13pub type Instant = web_async::time::Instant;
14
15/// A single re-armable timer registration, produced by [`Timers::timer`].
16///
17/// This is the primitive [`Deadline`] wraps; implement it, use `Deadline`.
18/// Arming is synchronous and in-memory: no I/O submission, no async
19/// cancellation. Implementations typically keep a slot in the runtime's timer
20/// wheel (or wrap a tokio `Sleep`).
21pub trait Timer {
22	/// Arm, re-arm, or disarm (`None`) the timer.
23	///
24	/// Re-arming an elapsed timer for a later instant makes it pend again;
25	/// re-arming for an instant already in the past leaves it elapsed.
26	fn set(&mut self, at: Option<Instant>);
27
28	/// Ready once the armed instant has passed, registering `waiter` otherwise.
29	///
30	/// Fused: an elapsed timer keeps reporting `Ready` until re-armed. A
31	/// disarmed timer never fires.
32	fn poll(&mut self, waiter: &kio::Waiter) -> Poll<()>;
33}
34
35/// The timer half of a runtime: mint [`Timer`]s and read the clock they follow.
36///
37/// Shared by session and origin drivers, independently of their transport or
38/// executor. Clones must be cheap (a ZST or a reference count).
39pub trait Timers: Clone {
40	/// The timer registration this runtime hands out.
41	type Timer: Timer;
42
43	/// A new, disarmed timer.
44	fn timer(&self) -> Self::Timer;
45
46	/// The latest instant supplied by the owner.
47	fn now(&self) -> Instant;
48}
49
50/// A wall-clock deadline: the ergonomic layer over [`Timer`].
51///
52/// Arm it with an [`Instant`], poll it from a `poll_*` function, re-arm or
53/// disarm as the deadline moves. Re-setting the instant it already holds does
54/// nothing, so a poll loop can recompute its deadline every turn without
55/// restarting the countdown.
56pub struct Deadline<R: Timers> {
57	at: Option<Instant>,
58	timer: R::Timer,
59}
60
61impl<R: Timers> Deadline<R> {
62	/// A disarmed deadline, which never fires until [`set`](Self::set) arms it.
63	pub fn new(runtime: &R) -> Self {
64		Self {
65			at: None,
66			timer: runtime.timer(),
67		}
68	}
69
70	/// A deadline armed for `duration` past the runtime's [`now`](Timers::now).
71	///
72	/// A duration the clock cannot represent (e.g. [`std::time::Duration::MAX`])
73	/// leaves the deadline disarmed, so it never fires rather than panicking on
74	/// the overflow.
75	pub fn after(runtime: &R, duration: std::time::Duration) -> Self {
76		let mut deadline = Self::new(runtime);
77		deadline.set(runtime.now().checked_add(duration));
78		deadline
79	}
80
81	/// Arm, re-arm, or disarm (`None`) the deadline.
82	pub fn set(&mut self, at: Option<Instant>) {
83		if self.at == at {
84			return;
85		}
86		self.at = at;
87		self.timer.set(at);
88	}
89
90	/// The instant this fires at, or `None` while disarmed.
91	#[cfg(test)]
92	pub fn deadline(&self) -> Option<Instant> {
93		self.at
94	}
95
96	/// Poll the deadline, registering `waiter` so the poll re-fires once it
97	/// elapses. `Ready` once the instant has passed, `Pending` before then and
98	/// while disarmed.
99	pub fn poll(&mut self, waiter: &kio::Waiter) -> Poll<()> {
100		if self.at.is_none() {
101			return Poll::Pending;
102		}
103		self.timer.poll(waiter)
104	}
105}
106
107impl<R: Timers> std::fmt::Debug for Deadline<R> {
108	fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
109		f.debug_struct("Deadline").field("at", &self.at).finish()
110	}
111}
112
113#[cfg(test)]
114mod test;
115#[cfg(test)]
116pub use test::Test;
117
118/// A tokio-backed runtime for this crate's own unit tests, so the existing
119/// `tokio::time::pause`/`advance` tests keep their semantics: `now` reads
120/// tokio's (pausable) clock and timers are tokio sleeps, which paused tests
121/// auto-advance. Production adapters live outside this crate; this one is
122/// compiled only into the test harness (integration tests carry their own copy
123/// in `tests/support`).
124#[cfg(all(test, not(target_family = "wasm")))]
125pub(crate) mod tokio_test {
126	use std::{pin::Pin, task::Poll};
127
128	use super::{Instant, Timer};
129
130	#[derive(Clone, Default)]
131	pub(crate) struct Tokio;
132
133	impl Tokio {
134		pub fn new() -> Self {
135			Self
136		}
137	}
138
139	impl super::Timers for Tokio {
140		type Timer = TokioTimer;
141
142		fn timer(&self) -> Self::Timer {
143			TokioTimer { at: None, sleep: None }
144		}
145
146		fn now(&self) -> Instant {
147			tokio::time::Instant::now().into_std()
148		}
149	}
150
151	pub(crate) struct TokioTimer {
152		at: Option<Instant>,
153		// Allocated on the first poll after arming, then re-armed in place via
154		// `Sleep::reset`. Construction is deferred because it panics without a
155		// live tokio time driver, and only the poll is guaranteed to run inside
156		// the runtime.
157		sleep: Option<Pin<Box<tokio::time::Sleep>>>,
158	}
159
160	impl Timer for TokioTimer {
161		fn set(&mut self, at: Option<Instant>) {
162			self.at = at;
163			// Reuse the allocation when there is one; `reset` also clears
164			// `is_elapsed`.
165			if let (Some(at), Some(sleep)) = (at, &mut self.sleep) {
166				sleep.as_mut().reset(tokio::time::Instant::from_std(at));
167			}
168		}
169
170		fn poll(&mut self, waiter: &kio::Waiter) -> Poll<()> {
171			let Some(at) = self.at else { return Poll::Pending };
172			let sleep = self
173				.sleep
174				.get_or_insert_with(|| Box::pin(tokio::time::sleep_until(tokio::time::Instant::from_std(at))));
175			if sleep.is_elapsed() {
176				return Poll::Ready(());
177			}
178			waiter.poll_future(sleep.as_mut())
179		}
180	}
181}