moduvex_runtime/time/
sleep.rs1use std::future::Future;
8use std::pin::Pin;
9use std::task::{Context, Poll};
10use std::time::{Duration, Instant};
11
12use super::{with_timer_wheel, TimerId};
13
14pub struct Sleep {
18 deadline: Instant,
20 timer_id: Option<TimerId>,
22}
23
24impl Sleep {
25 pub(crate) fn new(deadline: Instant) -> Self {
27 Self {
28 deadline,
29 timer_id: None,
30 }
31 }
32}
33
34impl Future for Sleep {
35 type Output = ();
36
37 fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
38 let now = Instant::now();
39
40 if now >= self.deadline {
42 if let Some(id) = self.timer_id.take() {
44 with_timer_wheel(|w| {
45 w.cancel(id);
46 });
47 }
48 return Poll::Ready(());
49 }
50
51 if let Some(old_id) = self.timer_id.take() {
55 with_timer_wheel(|w| {
56 w.cancel(old_id);
57 });
58 }
59 let id = with_timer_wheel(|w| w.insert(self.deadline, cx.waker().clone()));
60 self.timer_id = Some(id);
61
62 Poll::Pending
63 }
64}
65
66impl Drop for Sleep {
67 fn drop(&mut self) {
68 if let Some(id) = self.timer_id.take() {
70 with_timer_wheel(|w| {
71 w.cancel(id);
72 });
73 }
74 }
75}
76
77pub fn sleep(duration: Duration) -> Sleep {
90 Sleep::new(Instant::now() + duration)
91}
92
93pub fn sleep_until(deadline: Instant) -> Sleep {
95 Sleep::new(deadline)
96}
97
98#[cfg(test)]
101mod tests {
102 use super::*;
103 use crate::executor::block_on_with_spawn;
104 use std::time::Duration;
105
106 #[test]
107 fn sleep_zero_completes_immediately() {
108 block_on_with_spawn(async {
109 let before = Instant::now();
110 sleep(Duration::ZERO).await;
111 assert!(before.elapsed() < Duration::from_millis(50));
113 });
114 }
115
116 #[test]
117 fn sleep_100ms_completes_within_bounds() {
118 block_on_with_spawn(async {
119 let before = Instant::now();
120 sleep(Duration::from_millis(100)).await;
121 let elapsed = before.elapsed();
122 assert!(
123 elapsed >= Duration::from_millis(95),
124 "sleep resolved too early: {:?}",
125 elapsed
126 );
127 assert!(
128 elapsed < Duration::from_millis(500),
129 "sleep took too long: {:?}",
130 elapsed
131 );
132 });
133 }
134
135 #[test]
136 fn sleep_drop_before_completion_does_not_panic() {
137 block_on_with_spawn(async {
138 let s = sleep(Duration::from_millis(1000));
140 drop(s); });
142 }
143
144 #[test]
147 fn sleep_past_deadline_returns_immediately() {
148 block_on_with_spawn(async {
149 let deadline = Instant::now() - Duration::from_millis(100);
150 let before = Instant::now();
151 sleep_until(deadline).await;
152 assert!(before.elapsed() < Duration::from_millis(50));
153 });
154 }
155
156 #[test]
157 fn sleep_1ms_completes() {
158 block_on_with_spawn(async {
159 sleep(Duration::from_millis(1)).await;
160 });
161 }
162
163 #[test]
164 fn sleep_concurrent_multiple() {
165 use crate::executor::spawn;
166 block_on_with_spawn(async {
167 let before = Instant::now();
168 let h1 = spawn(async { sleep(Duration::from_millis(50)).await });
169 let h2 = spawn(async { sleep(Duration::from_millis(50)).await });
170 h1.await.unwrap();
171 h2.await.unwrap();
172 assert!(before.elapsed() < Duration::from_millis(500));
174 });
175 }
176
177 #[test]
178 fn sleep_until_future_instant() {
179 block_on_with_spawn(async {
180 let deadline = Instant::now() + Duration::from_millis(20);
181 sleep_until(deadline).await;
182 assert!(Instant::now() >= deadline);
183 });
184 }
185
186 #[test]
187 fn sleep_drop_does_not_leak_timer() {
188 block_on_with_spawn(async {
191 for _ in 0..50 {
192 let s = sleep(Duration::from_secs(10));
193 drop(s);
194 }
195 });
196 }
197
198 #[test]
199 fn sleep_10ms_completes() {
200 block_on_with_spawn(async {
201 let before = Instant::now();
202 sleep(Duration::from_millis(10)).await;
203 assert!(before.elapsed() >= Duration::from_millis(5));
204 });
205 }
206
207 #[test]
208 fn sleep_two_sequential_sleeps() {
209 block_on_with_spawn(async {
210 sleep(Duration::from_millis(5)).await;
211 sleep(Duration::from_millis(5)).await;
212 });
214 }
215
216 #[test]
217 fn sleep_until_already_past_instant() {
218 block_on_with_spawn(async {
219 let past = Instant::now() - Duration::from_secs(1);
221 let before = Instant::now();
222 sleep_until(past).await;
223 assert!(before.elapsed() < Duration::from_millis(100));
225 });
226 }
227
228 #[test]
229 fn sleep_duration_zero_uses_zero_const() {
230 block_on_with_spawn(async {
231 let before = Instant::now();
233 sleep(Duration::ZERO).await;
234 assert!(before.elapsed() < Duration::from_millis(100));
235 });
236 }
237
238 #[test]
239 fn sleep_3_sequential_1ms_each() {
240 block_on_with_spawn(async {
241 for _ in 0..3 {
242 sleep(Duration::from_millis(1)).await;
243 }
244 });
245 }
246
247 #[test]
248 fn sleep_until_now_returns_immediately() {
249 block_on_with_spawn(async {
250 let now = Instant::now();
251 let before = Instant::now();
252 sleep_until(now).await;
253 assert!(before.elapsed() < Duration::from_millis(100));
254 });
255 }
256
257 #[test]
258 fn sleep_multiple_concurrent_different_durations() {
259 use crate::executor::spawn;
260 block_on_with_spawn(async {
261 let before = Instant::now();
262 let h1 = spawn(async { sleep(Duration::from_millis(10)).await });
263 let h2 = spawn(async { sleep(Duration::from_millis(20)).await });
264 let h3 = spawn(async { sleep(Duration::from_millis(30)).await });
265 h1.await.unwrap();
266 h2.await.unwrap();
267 h3.await.unwrap();
268 assert!(before.elapsed() < Duration::from_millis(500));
270 });
271 }
272}