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]
398 async fn now_is_current() {
399 sleep(Millis(25)).await;
400
401 let now = now();
402 let cur = time::Instant::now();
403 assert!(cur >= now);
404 assert!(cur - now < time::Duration::from_millis(50));
405 }
406
407 #[ntex::test]
412 async fn now_updates_after_sleep() {
413 sleep(Millis(50)).await;
414
415 let first_time = now();
416
417 sleep(Millis(25)).await;
418
419 let second_time = now();
420 assert!(second_time - first_time >= time::Duration::from_millis(25));
421 }
422
423 #[ntex::test]
428 async fn system_time_service_time_does_not_immediately_change() {
429 sleep(Seconds(1)).await;
430
431 assert_eq!(system_time(), system_time());
432 }
433
434 #[ntex::test]
439 async fn system_time_service_time_updates_after_resolution_interval() {
440 sleep(Millis(100)).await;
441
442 let wait_time = 600;
443
444 let first_time = system_time()
445 .duration_since(time::SystemTime::UNIX_EPOCH)
446 .unwrap();
447
448 sleep(Millis(wait_time)).await;
449
450 let second_time = system_time()
451 .duration_since(time::SystemTime::UNIX_EPOCH)
452 .unwrap();
453
454 assert!(
455 second_time.checked_sub(first_time).unwrap()
456 >= time::Duration::from_millis(u64::from(wait_time))
457 );
458 }
459
460 #[ntex::test]
461 async fn test_sleep_0() {
462 sleep(Seconds(1)).await;
463
464 let first_time = time::Instant::now();
465 sleep(Millis(0)).await;
466 let second_time = time::Instant::now();
467 assert!(second_time - first_time >= time::Duration::from_millis(1));
468
469 let first_time = time::Instant::now();
470 sleep(Millis(1)).await;
471 let second_time = time::Instant::now();
472 assert!(second_time - first_time >= time::Duration::from_millis(1));
473
474 let first_time = time::Instant::now();
475 let fut = sleep(Millis(10000));
476 assert!(!fut.is_elapsed());
477 fut.reset(Millis::ZERO);
479 assert!(!fut.is_elapsed());
480 fut.await;
481 let second_time = time::Instant::now();
482 assert!(second_time - first_time >= time::Duration::from_millis(1));
483
484 let fut = sleep(Millis::ZERO);
485 assert!(!fut.is_elapsed());
486 fut.await;
487
488 let first_time = time::Instant::now();
492 let fut = Sleep {
493 hnd: TimerHandle::new(0),
494 };
495 assert!(fut.is_elapsed());
496 fut.await;
497 let second_time = time::Instant::now();
498 assert!(second_time - first_time < time::Duration::from_millis(50));
499
500 let first_time = time::Instant::now();
501 let fut = Rc::new(sleep(Millis(10_0000)));
502 let s = fut.clone();
503 ntex::rt::spawn(async move {
504 s.elapse();
505 });
506 poll_fn(|cx| fut.poll_elapsed(cx)).await;
507 assert!(fut.is_elapsed());
508 let second_time = time::Instant::now();
509 assert!(second_time - first_time < time::Duration::from_millis(50));
510 }
511
512 #[ntex::test]
513 async fn test_deadline() {
514 sleep(Seconds(1)).await;
515
516 let first_time = now();
517 let dl = deadline(Millis(1));
518 dl.await;
519 let second_time = now();
520 assert!(second_time - first_time >= time::Duration::from_millis(1));
521 assert!(timeout(Millis(100), deadline(Millis(0))).await.is_err());
522
523 let mut dl = deadline(Millis(1));
524 dl.reset(Millis::ZERO);
525 assert!(lazy(|cx| dl.poll_elapsed(cx)).await.is_pending());
526
527 let mut dl = deadline(Millis(1));
528 dl.reset(Millis(100));
529 let first_time = now();
530 dl.await;
531 let second_time = now();
532 assert!(second_time - first_time >= time::Duration::from_millis(100));
533
534 let mut dl = deadline(Millis(0));
535 assert!(dl.is_elapsed());
536 dl.reset(Millis(1));
537 assert!(lazy(|cx| dl.poll_elapsed(cx)).await.is_pending());
538
539 assert!(format!("{dl:?}").contains("Deadline"));
540 }
541
542 #[ntex::test]
543 async fn test_interval_zero_period() {
544 let int = interval(Millis::ZERO);
545 int.tick().await;
546
547 assert!(lazy(|cx| int.poll_tick(cx)).await.is_pending());
549 int.tick().await;
550 }
551
552 #[ntex::test]
553 async fn test_interval() {
554 let mut int = interval(Millis(250));
555
556 let time = time::Instant::now();
558 int.tick().await;
559 assert!(time.elapsed() < time::Duration::from_millis(50));
560
561 let time = time::Instant::now();
562 int.tick().await;
563 let elapsed = time.elapsed();
564 assert!(
565 elapsed > time::Duration::from_millis(200)
566 && elapsed < time::Duration::from_millis(450),
567 "elapsed: {elapsed:?}"
568 );
569
570 let time = time::Instant::now();
571 int.next().await;
572 let elapsed = time.elapsed();
573 assert!(
574 elapsed > time::Duration::from_millis(200)
575 && elapsed < time::Duration::from_millis(450),
576 "elapsed: {elapsed:?}"
577 );
578 }
579
580 #[ntex::test]
581 async fn test_interval_one_sec() {
582 let int = interval(Millis::ONE_SEC);
583 int.tick().await;
584
585 for _i in 0..3 {
586 let time = time::Instant::now();
587 int.tick().await;
588 let elapsed = time.elapsed();
589 assert!(
590 elapsed > time::Duration::from_secs(1)
591 && elapsed < time::Duration::from_millis(1300),
592 "elapsed: {elapsed:?}"
593 );
594 }
595 }
596
597 #[ntex::test]
598 async fn test_timeout_checked() {
599 let result = timeout_checked(Millis(200), sleep(Millis(100))).await;
600 assert!(result.is_ok());
601
602 let result = timeout_checked(Millis(5), std::future::pending::<()>()).await;
605 assert!(result.is_err());
606
607 let result = timeout_checked(Millis(0), sleep(Millis(100))).await;
608 assert!(result.is_ok());
609 }
610
611 #[ntex::test]
612 async fn sleep_and_deadline_wait() {
613 let start = now();
614 sleep(Millis(5)).wait().await;
615 deadline(Millis(5)).wait().await;
616 assert!(now() >= start);
617
618 let hnd = Deadline::new(Millis::ZERO);
619 assert!(
620 crate::time::timeout(Millis(20), hnd.wait()).await.is_err(),
621 "zero deadline never completes"
622 );
623 }
624}