1#![allow(
3 clippy::cast_possible_truncation,
4 clippy::cast_sign_loss,
5 clippy::cast_possible_wrap
6)]
7use std::{cmp, future::Future, future::poll_fn, pin::Pin, task, task::Poll};
8
9mod types;
10mod wheel;
11
12pub use self::types::{Millis, Seconds};
13pub use self::wheel::{TimerHandle, now, query_system_time, system_time};
14
15#[inline]
22pub fn sleep<T: Into<Millis>>(dur: T) -> Sleep {
23 Sleep::new(dur.into())
24}
25
26#[inline]
30pub fn deadline<T: Into<Millis>>(dur: T) -> Deadline {
31 Deadline::new(dur.into())
32}
33
34#[inline]
39pub fn interval<T: Into<Millis>>(period: T) -> Interval {
40 Interval::new(period.into())
41}
42
43#[inline]
50pub fn timeout<T, U>(dur: U, future: T) -> Timeout<T>
51where
52 T: Future,
53 U: Into<Millis>,
54{
55 Timeout::new_with_delay(future, Sleep::new(dur.into()))
56}
57
58#[inline]
64pub fn timeout_checked<T, U>(dur: U, future: T) -> TimeoutChecked<T>
65where
66 T: Future,
67 U: Into<Millis>,
68{
69 TimeoutChecked::new_with_delay(future, dur.into())
70}
71
72#[derive(Debug)]
88#[must_use = "futures do nothing unless you `.await` or poll them"]
89pub struct Sleep {
90 hnd: TimerHandle,
92}
93
94impl Sleep {
95 #[inline]
97 pub fn new(duration: Millis) -> Sleep {
98 Sleep {
99 hnd: TimerHandle::new(u64::from(cmp::max(duration.0, 1))),
100 }
101 }
102
103 #[inline]
105 pub fn is_elapsed(&self) -> bool {
106 self.hnd.is_elapsed()
107 }
108
109 #[inline]
111 pub fn elapse(&self) {
112 self.hnd.elapse();
113 }
114
115 pub fn reset<T: Into<Millis>>(&self, millis: T) {
123 self.hnd.reset(u64::from(millis.into().0));
124 }
125
126 #[inline]
127 pub async fn wait(&self) {
129 poll_fn(|cx| self.hnd.poll_elapsed(cx)).await;
130 }
131
132 #[inline]
133 pub fn poll_elapsed(&self, cx: &mut task::Context<'_>) -> Poll<()> {
134 self.hnd.poll_elapsed(cx)
135 }
136}
137
138impl Future for Sleep {
139 type Output = ();
140
141 fn poll(self: Pin<&mut Self>, cx: &mut task::Context<'_>) -> Poll<Self::Output> {
142 self.hnd.poll_elapsed(cx)
143 }
144}
145
146#[derive(Debug)]
162#[must_use = "futures do nothing unless you `.await` or poll them"]
163pub struct Deadline {
164 hnd: Option<TimerHandle>,
165}
166
167impl Deadline {
168 #[inline]
172 pub fn new(duration: Millis) -> Deadline {
173 if duration.0 != 0 {
174 Deadline {
175 hnd: Some(TimerHandle::new(u64::from(duration.0))),
176 }
177 } else {
178 Deadline { hnd: None }
179 }
180 }
181
182 #[inline]
183 pub async fn wait(&self) {
185 poll_fn(|cx| self.poll_elapsed(cx)).await;
186 }
187
188 pub fn reset<T: Into<Millis>>(&mut self, millis: T) {
196 let millis = millis.into();
197 if millis.0 != 0 {
198 if let Some(ref mut hnd) = self.hnd {
199 hnd.reset(u64::from(millis.0));
200 } else {
201 self.hnd = Some(TimerHandle::new(u64::from(millis.0)));
202 }
203 } else {
204 let _ = self.hnd.take();
205 }
206 }
207
208 #[inline]
210 pub fn is_elapsed(&self) -> bool {
211 self.hnd.as_ref().is_none_or(TimerHandle::is_elapsed)
212 }
213
214 #[inline]
215 pub fn poll_elapsed(&self, cx: &mut task::Context<'_>) -> Poll<()> {
216 self.hnd
217 .as_ref()
218 .map_or(Poll::Pending, |t| t.poll_elapsed(cx))
219 }
220}
221
222impl Future for Deadline {
223 type Output = ();
224
225 fn poll(self: Pin<&mut Self>, cx: &mut task::Context<'_>) -> Poll<Self::Output> {
226 self.poll_elapsed(cx)
227 }
228}
229
230pin_project_lite::pin_project! {
231 #[must_use = "futures do nothing unless you `.await` or poll them"]
233 #[derive(Debug)]
234 pub struct Timeout<T> {
235 #[pin]
236 value: T,
237 delay: Sleep,
238 }
239}
240
241impl<T> Timeout<T> {
242 pub(crate) fn new_with_delay(value: T, delay: Sleep) -> Timeout<T> {
243 Timeout { value, delay }
244 }
245}
246
247impl<T> Future for Timeout<T>
248where
249 T: Future,
250{
251 type Output = Result<T::Output, ()>;
252
253 fn poll(self: Pin<&mut Self>, cx: &mut task::Context<'_>) -> Poll<Self::Output> {
254 let this = self.project();
255
256 if let Poll::Ready(v) = this.value.poll(cx) {
258 return Poll::Ready(Ok(v));
259 }
260
261 match this.delay.poll_elapsed(cx) {
263 Poll::Ready(()) => Poll::Ready(Err(())),
264 Poll::Pending => Poll::Pending,
265 }
266 }
267}
268
269pin_project_lite::pin_project! {
270 #[must_use = "futures do nothing unless you `.await` or poll them"]
272 pub struct TimeoutChecked<T> {
273 #[pin]
274 state: TimeoutCheckedState<T>,
275 }
276}
277
278pin_project_lite::pin_project! {
279 #[project = TimeoutCheckedStateProject]
280 enum TimeoutCheckedState<T> {
281 Timeout{ #[pin] fut: Timeout<T> },
282 NoTimeout{ #[pin] fut: T },
283 }
284}
285
286impl<T> TimeoutChecked<T> {
287 pub(crate) fn new_with_delay(value: T, delay: Millis) -> TimeoutChecked<T> {
288 if delay.is_zero() {
289 TimeoutChecked {
290 state: TimeoutCheckedState::NoTimeout { fut: value },
291 }
292 } else {
293 TimeoutChecked {
294 state: TimeoutCheckedState::Timeout {
295 fut: Timeout::new_with_delay(value, sleep(delay)),
296 },
297 }
298 }
299 }
300}
301
302impl<T> Future for TimeoutChecked<T>
303where
304 T: Future,
305{
306 type Output = Result<T::Output, ()>;
307
308 fn poll(self: Pin<&mut Self>, cx: &mut task::Context<'_>) -> Poll<Self::Output> {
309 match self.project().state.as_mut().project() {
310 TimeoutCheckedStateProject::Timeout { fut } => fut.poll(cx),
311 TimeoutCheckedStateProject::NoTimeout { fut } => fut.poll(cx).map(Result::Ok),
312 }
313 }
314}
315
316#[must_use = "futures do nothing unless you `.await` or poll them"]
321#[derive(Debug)]
322pub struct Interval {
323 hnd: TimerHandle,
324 period: u32,
325}
326
327impl Interval {
328 #[inline]
330 pub fn new(period: Millis) -> Interval {
331 Interval {
332 hnd: TimerHandle::new(u64::from(period.0)),
333 period: period.0,
334 }
335 }
336
337 #[inline]
338 pub async fn tick(&self) {
340 poll_fn(|cx| self.poll_tick(cx)).await;
341 }
342
343 #[inline]
344 pub fn poll_tick(&self, cx: &mut task::Context<'_>) -> Poll<()> {
346 if self.hnd.poll_elapsed(cx).is_ready() {
347 self.hnd.reset(u64::from(self.period));
348 Poll::Ready(())
349 } else {
350 Poll::Pending
351 }
352 }
353}
354
355impl crate::Stream for Interval {
356 type Item = ();
357
358 #[inline]
359 fn poll_next(self: Pin<&mut Self>, cx: &mut task::Context<'_>) -> Poll<Option<Self::Item>> {
360 self.poll_tick(cx).map(|()| Some(()))
361 }
362}
363
364#[cfg(test)]
365mod tests {
366 use futures_util::StreamExt;
367 use std::{future::poll_fn, rc::Rc, time};
368
369 use super::*;
370 use crate::future::lazy;
371
372 #[ntex::test]
376 async fn lowres_time_does_not_immediately_change() {
377 sleep(Millis(25)).await;
378
379 assert_eq!(now(), now());
380 }
381
382 #[ntex::test]
387 async fn lowres_time_updates_after_resolution_interval() {
388 sleep(Millis(50)).await;
389
390 let first_time = now();
391
392 sleep(Millis(25)).await;
393
394 let second_time = now();
395 assert!(second_time - first_time >= time::Duration::from_millis(25));
396 }
397
398 #[ntex::test]
402 async fn system_time_service_time_does_not_immediately_change() {
403 sleep(Seconds(1)).await;
404
405 assert_eq!(system_time(), system_time());
406 assert_eq!(system_time(), query_system_time());
407 }
408
409 #[ntex::test]
414 async fn system_time_service_time_updates_after_resolution_interval() {
415 sleep(Millis(100)).await;
416
417 let wait_time = 300;
418
419 let first_time = system_time()
420 .duration_since(time::SystemTime::UNIX_EPOCH)
421 .unwrap();
422
423 sleep(Millis(wait_time)).await;
424
425 let second_time = system_time()
426 .duration_since(time::SystemTime::UNIX_EPOCH)
427 .unwrap();
428
429 assert!(
430 second_time.checked_sub(first_time).unwrap()
431 >= time::Duration::from_millis(u64::from(wait_time))
432 );
433 }
434
435 #[ntex::test]
436 async fn test_sleep_0() {
437 sleep(Seconds(1)).await;
438
439 let first_time = now();
440 sleep(Millis(0)).await;
441 let second_time = now();
442 assert!(second_time - first_time >= time::Duration::from_millis(1));
443
444 let first_time = now();
445 sleep(Millis(1)).await;
446 let second_time = now();
447 assert!(second_time - first_time >= time::Duration::from_millis(1));
448
449 let first_time = now();
450 let fut = sleep(Millis(10000));
451 assert!(!fut.is_elapsed());
452 fut.reset(Millis::ZERO);
453 fut.await;
454 let second_time = now();
455 assert!(second_time - first_time < time::Duration::from_millis(1));
456
457 let first_time = now();
458 let fut = Sleep {
459 hnd: TimerHandle::new(0),
460 };
461 assert!(fut.is_elapsed());
462 fut.await;
463 let second_time = now();
464 assert!(second_time - first_time < time::Duration::from_millis(1));
465
466 let first_time = now();
467 let fut = Rc::new(sleep(Millis(10_0000)));
468 let s = fut.clone();
469 ntex::rt::spawn(async move {
470 s.elapse();
471 });
472 poll_fn(|cx| fut.poll_elapsed(cx)).await;
473 assert!(fut.is_elapsed());
474 let second_time = now();
475 assert!(second_time - first_time < time::Duration::from_millis(1));
476 }
477
478 #[ntex::test]
479 async fn test_deadline() {
480 sleep(Seconds(1)).await;
481
482 let first_time = now();
483 let dl = deadline(Millis(1));
484 dl.await;
485 let second_time = now();
486 assert!(second_time - first_time >= time::Duration::from_millis(1));
487 assert!(timeout(Millis(100), deadline(Millis(0))).await.is_err());
488
489 let mut dl = deadline(Millis(1));
490 dl.reset(Millis::ZERO);
491 assert!(lazy(|cx| dl.poll_elapsed(cx)).await.is_pending());
492
493 let mut dl = deadline(Millis(1));
494 dl.reset(Millis(100));
495 let first_time = now();
496 dl.await;
497 let second_time = now();
498 assert!(second_time - first_time >= time::Duration::from_millis(100));
499
500 let mut dl = deadline(Millis(0));
501 assert!(dl.is_elapsed());
502 dl.reset(Millis(1));
503 assert!(lazy(|cx| dl.poll_elapsed(cx)).await.is_pending());
504
505 assert!(format!("{dl:?}").contains("Deadline"));
506 }
507
508 #[ntex::test]
509 async fn test_interval() {
510 let mut int = interval(Millis(250));
511
512 let time = time::Instant::now();
513 int.tick().await;
514 let elapsed = time.elapsed();
515 assert!(
516 elapsed > time::Duration::from_millis(200)
517 && elapsed < time::Duration::from_millis(450),
518 "elapsed: {elapsed:?}"
519 );
520
521 let time = time::Instant::now();
522 int.next().await;
523 let elapsed = time.elapsed();
524 assert!(
525 elapsed > time::Duration::from_millis(200)
526 && elapsed < time::Duration::from_millis(450),
527 "elapsed: {elapsed:?}"
528 );
529 }
530
531 #[ntex::test]
532 async fn test_interval_one_sec() {
533 let int = interval(Millis::ONE_SEC);
534
535 for _i in 0..3 {
536 let time = time::Instant::now();
537 int.tick().await;
538 let elapsed = time.elapsed();
539 assert!(
540 elapsed > time::Duration::from_secs(1)
541 && elapsed < time::Duration::from_millis(1300),
542 "elapsed: {elapsed:?}"
543 );
544 }
545 }
546
547 #[ntex::test]
548 async fn test_timeout_checked() {
549 let result = timeout_checked(Millis(200), sleep(Millis(100))).await;
550 assert!(result.is_ok());
551
552 let result = timeout_checked(Millis(5), sleep(Millis(100))).await;
553 assert!(result.is_err());
554
555 let result = timeout_checked(Millis(0), sleep(Millis(100))).await;
556 assert!(result.is_ok());
557 }
558}