1use pin_project::{pin_project, pinned_drop};
2use tokio::time::Sleep;
3
4use std::{
5 future::Future,
6 pin::Pin,
7 sync::{
8 Arc,
9 atomic::{AtomicU64, Ordering},
10 },
11 task::{Context, Poll},
12 time::Duration,
13};
14
15use super::{inner::ClockInner, manual::ManualClock};
16
17static NEXT_SLEEP_ID: AtomicU64 = AtomicU64::new(0);
19
20fn next_sleep_id() -> u64 {
22 NEXT_SLEEP_ID.fetch_add(1, Ordering::Relaxed)
23}
24
25#[pin_project(PinnedDrop)]
29pub struct ClockSleep {
30 #[pin]
31 inner: ClockSleepInner,
32}
33
34#[pin_project(project = ClockSleepInnerProj)]
35enum ClockSleepInner {
36 Realtime {
37 #[pin]
38 sleep: Sleep,
39 },
40 Manual {
41 wake_at_ms: i64,
42 sleep_id: u64,
43 clock: Arc<ManualClock>,
44 registered: bool,
45 coalesceable: bool,
47 },
48}
49
50impl ClockSleep {
51 pub(crate) fn new(clock_inner: &ClockInner, duration: Duration) -> Self {
52 Self::new_inner(clock_inner, duration, false)
53 }
54
55 pub(crate) fn new_coalesceable(clock_inner: &ClockInner, duration: Duration) -> Self {
56 Self::new_inner(clock_inner, duration, true)
57 }
58
59 fn new_inner(clock_inner: &ClockInner, duration: Duration, coalesceable: bool) -> Self {
60 let inner = match clock_inner {
61 ClockInner::Realtime(rt) => ClockSleepInner::Realtime {
62 sleep: rt.sleep(duration),
63 },
64 ClockInner::Manual(manual) => {
65 let added = i64::try_from(duration.as_millis()).unwrap_or(i64::MAX);
66 let wake_at_ms = manual.now_ms().saturating_add(added);
67
68 ClockSleepInner::Manual {
69 wake_at_ms,
70 sleep_id: next_sleep_id(),
71 clock: Arc::clone(manual),
72 registered: false,
73 coalesceable,
74 }
75 }
76 };
77
78 Self { inner }
79 }
80}
81
82impl Future for ClockSleep {
83 type Output = ();
84
85 fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<()> {
86 let this = self.project();
87
88 match this.inner.project() {
89 ClockSleepInnerProj::Realtime { sleep } => sleep.poll(cx),
90
91 ClockSleepInnerProj::Manual {
92 wake_at_ms,
93 sleep_id,
94 clock,
95 registered,
96 coalesceable,
97 } => {
98 if clock.now_ms() >= *wake_at_ms {
100 return Poll::Ready(());
101 }
102
103 if !*registered {
105 if *coalesceable {
106 clock.register_coalesce_wake(*wake_at_ms, *sleep_id, cx.waker().clone());
107 } else {
108 clock.register_wake(*wake_at_ms, *sleep_id, cx.waker().clone());
109 }
110 *registered = true;
111 }
112
113 Poll::Pending
114 }
115 }
116 }
117}
118
119#[pinned_drop]
120impl PinnedDrop for ClockSleep {
121 fn drop(self: Pin<&mut Self>) {
122 if let ClockSleepInner::Manual {
124 sleep_id,
125 clock,
126 registered: true,
127 ..
128 } = &self.inner
129 {
130 clock.cancel_wake(*sleep_id);
131 }
132 }
133}
134
135#[pin_project]
139pub struct ClockTimeout<F> {
140 #[pin]
141 future: F,
142 #[pin]
143 sleep: ClockSleep,
144 completed: bool,
145}
146
147impl<F> ClockTimeout<F> {
148 pub(crate) fn new(clock_inner: &ClockInner, duration: Duration, future: F) -> Self {
149 Self {
150 future,
151 sleep: ClockSleep::new(clock_inner, duration),
152 completed: false,
153 }
154 }
155}
156
157impl<F: Future> Future for ClockTimeout<F> {
158 type Output = Result<F::Output, Elapsed>;
159
160 fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
161 let this = self.project();
162
163 if *this.completed {
164 panic!("ClockTimeout polled after completion");
165 }
166
167 if let Poll::Ready(output) = this.future.poll(cx) {
169 *this.completed = true;
170 return Poll::Ready(Ok(output));
171 }
172
173 if let Poll::Ready(()) = this.sleep.poll(cx) {
175 *this.completed = true;
176 return Poll::Ready(Err(Elapsed));
177 }
178
179 Poll::Pending
180 }
181}
182
183#[derive(Debug, Clone, Copy, PartialEq, Eq)]
185pub struct Elapsed;
186
187impl std::fmt::Display for Elapsed {
188 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
189 write!(f, "deadline has elapsed")
190 }
191}
192
193impl std::error::Error for Elapsed {}