1#![allow(
7 clippy::cast_possible_truncation,
8 clippy::cast_sign_loss,
9 clippy::cast_possible_wrap
10)]
11use std::{cmp, future::Future, future::poll_fn, pin::Pin, task, task::Poll};
12
13mod callbacks;
14mod types;
15mod wheel;
16
17pub use self::callbacks::CallbackTimer;
18pub use self::types::{Millis, Seconds};
19pub use self::wheel::{TimerHandle, now, system_time};
20
21#[inline]
28pub fn sleep<T: Into<Millis>>(dur: T) -> Sleep {
29 Sleep::new(dur.into())
30}
31
32#[inline]
36pub fn deadline<T: Into<Millis>>(dur: T) -> Deadline {
37 Deadline::new(dur.into())
38}
39
40#[inline]
47pub fn interval<T: Into<Millis>>(period: T) -> Interval {
48 Interval::new(period.into())
49}
50
51#[inline]
59pub fn timeout<T, U>(dur: U, future: T) -> Timeout<T>
60where
61 T: Future,
62 U: Into<Millis>,
63{
64 Timeout::new_with_delay(future, Sleep::new(dur.into()))
65}
66
67#[inline]
73pub fn timeout_checked<T, U>(dur: U, future: T) -> TimeoutChecked<T>
74where
75 T: Future,
76 U: Into<Millis>,
77{
78 TimeoutChecked::new_with_delay(future, dur.into())
79}
80
81#[derive(Debug)]
97#[must_use = "futures do nothing unless you `.await` or poll them"]
98pub struct Sleep {
99 hnd: TimerHandle,
101}
102
103impl Sleep {
104 #[inline]
108 pub fn new(duration: Millis) -> Sleep {
109 Sleep {
110 hnd: TimerHandle::new(u64::from(cmp::max(duration.0, 1))),
111 }
112 }
113
114 #[inline]
116 pub fn is_elapsed(&self) -> bool {
117 self.hnd.is_elapsed()
118 }
119
120 #[inline]
122 pub fn elapse(&self) {
123 self.hnd.elapse();
124 }
125
126 pub fn reset<T: Into<Millis>>(&self, millis: T) {
134 self.hnd.reset(u64::from(cmp::max(millis.into().0, 1)));
135 }
136
137 #[inline]
138 pub async fn wait(&self) {
140 poll_fn(|cx| self.hnd.poll_elapsed(cx)).await;
141 }
142
143 #[inline]
144 pub fn poll_elapsed(&self, cx: &mut task::Context<'_>) -> Poll<()> {
146 self.hnd.poll_elapsed(cx)
147 }
148}
149
150impl Future for Sleep {
151 type Output = ();
152
153 fn poll(self: Pin<&mut Self>, cx: &mut task::Context<'_>) -> Poll<Self::Output> {
154 self.hnd.poll_elapsed(cx)
155 }
156}
157
158#[derive(Debug)]
174#[must_use = "futures do nothing unless you `.await` or poll them"]
175pub struct Deadline {
176 hnd: Option<TimerHandle>,
177}
178
179impl Deadline {
180 #[inline]
184 pub fn new(duration: Millis) -> Deadline {
185 if duration.0 != 0 {
186 Deadline {
187 hnd: Some(TimerHandle::new(u64::from(duration.0))),
188 }
189 } else {
190 Deadline { hnd: None }
191 }
192 }
193
194 #[inline]
195 pub async fn wait(&self) {
197 poll_fn(|cx| self.poll_elapsed(cx)).await;
198 }
199
200 pub fn reset<T: Into<Millis>>(&mut self, millis: T) {
208 let millis = millis.into();
209 if millis.0 != 0 {
210 if let Some(ref mut hnd) = self.hnd {
211 hnd.reset(u64::from(millis.0));
212 } else {
213 self.hnd = Some(TimerHandle::new(u64::from(millis.0)));
214 }
215 } else {
216 let _ = self.hnd.take();
217 }
218 }
219
220 #[inline]
226 pub fn is_elapsed(&self) -> bool {
227 self.hnd.as_ref().is_none_or(TimerHandle::is_elapsed)
228 }
229
230 #[inline]
231 pub fn poll_elapsed(&self, cx: &mut task::Context<'_>) -> Poll<()> {
235 self.hnd
236 .as_ref()
237 .map_or(Poll::Pending, |t| t.poll_elapsed(cx))
238 }
239}
240
241impl Future for Deadline {
242 type Output = ();
243
244 fn poll(self: Pin<&mut Self>, cx: &mut task::Context<'_>) -> Poll<Self::Output> {
245 self.poll_elapsed(cx)
246 }
247}
248
249pin_project_lite::pin_project! {
250 #[must_use = "futures do nothing unless you `.await` or poll them"]
252 #[derive(Debug)]
253 pub struct Timeout<T> {
254 #[pin]
255 value: T,
256 delay: Sleep,
257 }
258}
259
260impl<T> Timeout<T> {
261 pub(crate) fn new_with_delay(value: T, delay: Sleep) -> Timeout<T> {
262 Timeout { value, delay }
263 }
264}
265
266impl<T> Future for Timeout<T>
267where
268 T: Future,
269{
270 type Output = Result<T::Output, ()>;
271
272 fn poll(self: Pin<&mut Self>, cx: &mut task::Context<'_>) -> Poll<Self::Output> {
273 let this = self.project();
274
275 if let Poll::Ready(v) = this.value.poll(cx) {
277 return Poll::Ready(Ok(v));
278 }
279
280 match this.delay.poll_elapsed(cx) {
282 Poll::Ready(()) => Poll::Ready(Err(())),
283 Poll::Pending => Poll::Pending,
284 }
285 }
286}
287
288pin_project_lite::pin_project! {
289 #[must_use = "futures do nothing unless you `.await` or poll them"]
291 pub struct TimeoutChecked<T> {
292 #[pin]
293 state: TimeoutCheckedState<T>,
294 }
295}
296
297pin_project_lite::pin_project! {
298 #[project = TimeoutCheckedStateProject]
299 enum TimeoutCheckedState<T> {
300 Timeout{ #[pin] fut: Timeout<T> },
301 NoTimeout{ #[pin] fut: T },
302 }
303}
304
305impl<T> TimeoutChecked<T> {
306 pub(crate) fn new_with_delay(value: T, delay: Millis) -> TimeoutChecked<T> {
307 if delay.is_zero() {
308 TimeoutChecked {
309 state: TimeoutCheckedState::NoTimeout { fut: value },
310 }
311 } else {
312 TimeoutChecked {
313 state: TimeoutCheckedState::Timeout {
314 fut: Timeout::new_with_delay(value, sleep(delay)),
315 },
316 }
317 }
318 }
319}
320
321impl<T> Future for TimeoutChecked<T>
322where
323 T: Future,
324{
325 type Output = Result<T::Output, ()>;
326
327 fn poll(self: Pin<&mut Self>, cx: &mut task::Context<'_>) -> Poll<Self::Output> {
328 match self.project().state.as_mut().project() {
329 TimeoutCheckedStateProject::Timeout { fut } => fut.poll(cx),
330 TimeoutCheckedStateProject::NoTimeout { fut } => fut.poll(cx).map(Result::Ok),
331 }
332 }
333}
334
335#[must_use = "futures do nothing unless you `.await` or poll them"]
340#[derive(Debug)]
341pub struct Interval {
342 hnd: TimerHandle,
343 period: u32,
344}
345
346impl Interval {
347 #[inline]
352 pub fn new(period: Millis) -> Interval {
353 Interval {
354 hnd: TimerHandle::new(0),
355 period: cmp::max(period.0, 1),
356 }
357 }
358
359 #[inline]
360 pub async fn tick(&self) {
362 poll_fn(|cx| self.poll_tick(cx)).await;
363 }
364
365 #[inline]
366 pub fn poll_tick(&self, cx: &mut task::Context<'_>) -> Poll<()> {
368 if self.hnd.poll_elapsed(cx).is_ready() {
369 self.hnd.reset(u64::from(self.period));
370 Poll::Ready(())
371 } else {
372 Poll::Pending
373 }
374 }
375}
376
377impl crate::Stream for Interval {
378 type Item = ();
379
380 #[inline]
381 fn poll_next(self: Pin<&mut Self>, cx: &mut task::Context<'_>) -> Poll<Option<Self::Item>> {
382 self.poll_tick(cx).map(|()| Some(()))
383 }
384}
385
386#[cfg(test)]
387mod tests {
388 use futures_util::StreamExt;
389 use std::{future::poll_fn, rc::Rc, time};
390
391 use super::*;
392 use crate::future::lazy;
393
394 #[ntex::test]
400 async fn now_is_current() {
401 sleep(Millis(25)).await;
402
403 let now = now();
404 let cur = time::Instant::now();
405 assert!(cur >= now);
406 assert!(cur - now < time::Duration::from_millis(50));
407 }
408
409 #[ntex::test]
415 async fn now_updates_after_sleep() {
416 sleep(Millis(50)).await;
417
418 let first_time = now();
419
420 sleep(Millis(25)).await;
421
422 let second_time = now();
423 assert!(second_time - first_time >= time::Duration::from_millis(25));
424 }
425
426 #[ntex::test]
431 async fn system_time_service_time_does_not_immediately_change() {
432 sleep(Seconds(1)).await;
433
434 assert_eq!(system_time(), system_time());
435 }
436
437 #[ntex::test]
443 async fn system_time_service_time_updates_after_resolution_interval() {
444 sleep(Millis(100)).await;
445
446 let wait_time = 600;
447
448 let first_time = system_time()
449 .duration_since(time::SystemTime::UNIX_EPOCH)
450 .unwrap();
451
452 sleep(Millis(wait_time)).await;
453
454 let second_time = system_time()
455 .duration_since(time::SystemTime::UNIX_EPOCH)
456 .unwrap();
457
458 assert!(
459 second_time.checked_sub(first_time).unwrap()
460 >= time::Duration::from_millis(u64::from(wait_time))
461 );
462 }
463
464 #[ntex::test]
470 async fn test_sleep_0() {
471 sleep(Seconds(1)).await;
472
473 let first_time = time::Instant::now();
474 sleep(Millis(0)).await;
475 let second_time = time::Instant::now();
476 assert!(second_time - first_time >= time::Duration::from_millis(1));
477
478 let first_time = time::Instant::now();
479 sleep(Millis(1)).await;
480 let second_time = time::Instant::now();
481 assert!(second_time - first_time >= time::Duration::from_millis(1));
482
483 let first_time = time::Instant::now();
484 let fut = sleep(Millis(10000));
485 assert!(!fut.is_elapsed());
486 fut.reset(Millis::ZERO);
488 assert!(!fut.is_elapsed());
489 fut.await;
490 let second_time = time::Instant::now();
491 assert!(second_time - first_time >= time::Duration::from_millis(1));
492
493 let fut = sleep(Millis::ZERO);
494 assert!(!fut.is_elapsed());
495 fut.await;
496
497 let first_time = time::Instant::now();
501 let fut = Sleep {
502 hnd: TimerHandle::new(0),
503 };
504 assert!(fut.is_elapsed());
505 fut.await;
506 let second_time = time::Instant::now();
507 assert!(second_time - first_time < time::Duration::from_millis(50));
508
509 let first_time = time::Instant::now();
510 let fut = Rc::new(sleep(Millis(10_0000)));
511 let s = fut.clone();
512 ntex::rt::spawn(async move {
513 s.elapse();
514 });
515 poll_fn(|cx| fut.poll_elapsed(cx)).await;
516 assert!(fut.is_elapsed());
517 let second_time = time::Instant::now();
518 assert!(second_time - first_time < time::Duration::from_millis(50));
519 }
520
521 #[ntex::test]
527 async fn test_deadline() {
528 sleep(Seconds(1)).await;
529
530 let first_time = now();
531 let dl = deadline(Millis(1));
532 dl.await;
533 let second_time = now();
534 assert!(second_time - first_time >= time::Duration::from_millis(1));
535 assert!(timeout(Millis(100), deadline(Millis(0))).await.is_err());
536
537 let mut dl = deadline(Millis(1));
538 dl.reset(Millis::ZERO);
539 assert!(lazy(|cx| dl.poll_elapsed(cx)).await.is_pending());
540
541 let mut dl = deadline(Millis(1));
542 dl.reset(Millis(100));
543 let first_time = now();
544 dl.await;
545 let second_time = now();
546 assert!(second_time - first_time >= time::Duration::from_millis(100));
547
548 let mut dl = deadline(Millis(0));
549 assert!(dl.is_elapsed());
550 dl.reset(Millis(1));
551 assert!(lazy(|cx| dl.poll_elapsed(cx)).await.is_pending());
552
553 assert!(format!("{dl:?}").contains("Deadline"));
554 }
555
556 #[ntex::test]
561 async fn test_interval_zero_period() {
562 let int = interval(Millis::ZERO);
563 int.tick().await;
564
565 assert!(lazy(|cx| int.poll_tick(cx)).await.is_pending());
567 int.tick().await;
568 }
569
570 #[ntex::test]
576 async fn test_interval() {
577 let mut int = interval(Millis(250));
578
579 let time = time::Instant::now();
581 int.tick().await;
582 assert!(time.elapsed() < time::Duration::from_millis(50));
583
584 let time = time::Instant::now();
585 int.tick().await;
586 let elapsed = time.elapsed();
587 assert!(
588 elapsed > time::Duration::from_millis(200)
589 && elapsed < time::Duration::from_millis(450),
590 "elapsed: {elapsed:?}"
591 );
592
593 let time = time::Instant::now();
594 int.next().await;
595 let elapsed = time.elapsed();
596 assert!(
597 elapsed > time::Duration::from_millis(200)
598 && elapsed < time::Duration::from_millis(450),
599 "elapsed: {elapsed:?}"
600 );
601 }
602
603 #[ntex::test]
608 async fn test_interval_one_sec() {
609 let int = interval(Millis::ONE_SEC);
610 int.tick().await;
611
612 for _i in 0..3 {
613 let time = time::Instant::now();
614 int.tick().await;
615 let elapsed = time.elapsed();
616 assert!(
617 elapsed > time::Duration::from_secs(1)
618 && elapsed < time::Duration::from_millis(1300),
619 "elapsed: {elapsed:?}"
620 );
621 }
622 }
623
624 #[ntex::test]
630 async fn test_timeout_checked() {
631 let result = timeout_checked(Millis(200), sleep(Millis(100))).await;
632 assert!(result.is_ok());
633
634 let result = timeout_checked(Millis(5), std::future::pending::<()>()).await;
637 assert!(result.is_err());
638
639 let result = timeout_checked(Millis(0), sleep(Millis(100))).await;
640 assert!(result.is_ok());
641 }
642
643 #[ntex::test]
649 async fn sleep_and_deadline_wait() {
650 let start = now();
651 sleep(Millis(5)).wait().await;
652 deadline(Millis(5)).wait().await;
653 assert!(now() >= start);
654
655 let hnd = Deadline::new(Millis::ZERO);
656 assert!(
657 crate::time::timeout(Millis(20), hnd.wait()).await.is_err(),
658 "zero deadline never completes"
659 );
660 }
661}