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