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]
41pub fn interval<T: Into<Millis>>(period: T) -> Interval {
42 Interval::new(period.into())
43}
44
45#[inline]
53pub fn timeout<T, U>(dur: U, future: T) -> Timeout<T>
54where
55 T: Future,
56 U: Into<Millis>,
57{
58 Timeout::new_with_delay(future, Sleep::new(dur.into()))
59}
60
61#[inline]
67pub fn timeout_checked<T, U>(dur: U, future: T) -> TimeoutChecked<T>
68where
69 T: Future,
70 U: Into<Millis>,
71{
72 TimeoutChecked::new_with_delay(future, dur.into())
73}
74
75#[derive(Debug)]
91#[must_use = "futures do nothing unless you `.await` or poll them"]
92pub struct Sleep {
93 hnd: TimerHandle,
95}
96
97impl Sleep {
98 #[inline]
102 pub fn new(duration: Millis) -> Sleep {
103 Sleep {
104 hnd: TimerHandle::new(u64::from(cmp::max(duration.0, 1))),
105 }
106 }
107
108 #[inline]
110 pub fn is_elapsed(&self) -> bool {
111 self.hnd.is_elapsed()
112 }
113
114 #[inline]
116 pub fn elapse(&self) {
117 self.hnd.elapse();
118 }
119
120 pub fn reset<T: Into<Millis>>(&self, millis: T) {
128 self.hnd.reset(u64::from(cmp::max(millis.into().0, 1)));
129 }
130
131 #[inline]
132 pub async fn wait(&self) {
134 poll_fn(|cx| self.hnd.poll_elapsed(cx)).await;
135 }
136
137 #[inline]
138 pub fn poll_elapsed(&self, cx: &mut task::Context<'_>) -> Poll<()> {
140 self.hnd.poll_elapsed(cx)
141 }
142}
143
144impl Future for Sleep {
145 type Output = ();
146
147 fn poll(self: Pin<&mut Self>, cx: &mut task::Context<'_>) -> Poll<Self::Output> {
148 self.hnd.poll_elapsed(cx)
149 }
150}
151
152#[derive(Debug)]
168#[must_use = "futures do nothing unless you `.await` or poll them"]
169pub struct Deadline {
170 hnd: Option<TimerHandle>,
171}
172
173impl Deadline {
174 #[inline]
178 pub fn new(duration: Millis) -> Deadline {
179 if duration.0 != 0 {
180 Deadline {
181 hnd: Some(TimerHandle::new(u64::from(duration.0))),
182 }
183 } else {
184 Deadline { hnd: None }
185 }
186 }
187
188 #[inline]
189 pub async fn wait(&self) {
191 poll_fn(|cx| self.poll_elapsed(cx)).await;
192 }
193
194 pub fn reset<T: Into<Millis>>(&mut self, millis: T) {
202 let millis = millis.into();
203 if millis.0 != 0 {
204 if let Some(ref mut hnd) = self.hnd {
205 hnd.reset(u64::from(millis.0));
206 } else {
207 self.hnd = Some(TimerHandle::new(u64::from(millis.0)));
208 }
209 } else {
210 let _ = self.hnd.take();
211 }
212 }
213
214 #[inline]
220 pub fn is_elapsed(&self) -> bool {
221 self.hnd.as_ref().is_none_or(TimerHandle::is_elapsed)
222 }
223
224 #[inline]
225 pub fn poll_elapsed(&self, cx: &mut task::Context<'_>) -> Poll<()> {
229 self.hnd
230 .as_ref()
231 .map_or(Poll::Pending, |t| t.poll_elapsed(cx))
232 }
233}
234
235impl Future for Deadline {
236 type Output = ();
237
238 fn poll(self: Pin<&mut Self>, cx: &mut task::Context<'_>) -> Poll<Self::Output> {
239 self.poll_elapsed(cx)
240 }
241}
242
243pin_project_lite::pin_project! {
244 #[must_use = "futures do nothing unless you `.await` or poll them"]
246 #[derive(Debug)]
247 pub struct Timeout<T> {
248 #[pin]
249 value: T,
250 delay: Sleep,
251 }
252}
253
254impl<T> Timeout<T> {
255 pub(crate) fn new_with_delay(value: T, delay: Sleep) -> Timeout<T> {
256 Timeout { value, delay }
257 }
258}
259
260impl<T> Future for Timeout<T>
261where
262 T: Future,
263{
264 type Output = Result<T::Output, ()>;
265
266 fn poll(self: Pin<&mut Self>, cx: &mut task::Context<'_>) -> Poll<Self::Output> {
267 let this = self.project();
268
269 if let Poll::Ready(v) = this.value.poll(cx) {
271 return Poll::Ready(Ok(v));
272 }
273
274 match this.delay.poll_elapsed(cx) {
276 Poll::Ready(()) => Poll::Ready(Err(())),
277 Poll::Pending => Poll::Pending,
278 }
279 }
280}
281
282pin_project_lite::pin_project! {
283 #[must_use = "futures do nothing unless you `.await` or poll them"]
285 pub struct TimeoutChecked<T> {
286 #[pin]
287 state: TimeoutCheckedState<T>,
288 }
289}
290
291pin_project_lite::pin_project! {
292 #[project = TimeoutCheckedStateProject]
293 enum TimeoutCheckedState<T> {
294 Timeout{ #[pin] fut: Timeout<T> },
295 NoTimeout{ #[pin] fut: T },
296 }
297}
298
299impl<T> TimeoutChecked<T> {
300 pub(crate) fn new_with_delay(value: T, delay: Millis) -> TimeoutChecked<T> {
301 if delay.is_zero() {
302 TimeoutChecked {
303 state: TimeoutCheckedState::NoTimeout { fut: value },
304 }
305 } else {
306 TimeoutChecked {
307 state: TimeoutCheckedState::Timeout {
308 fut: Timeout::new_with_delay(value, sleep(delay)),
309 },
310 }
311 }
312 }
313}
314
315impl<T> Future for TimeoutChecked<T>
316where
317 T: Future,
318{
319 type Output = Result<T::Output, ()>;
320
321 fn poll(self: Pin<&mut Self>, cx: &mut task::Context<'_>) -> Poll<Self::Output> {
322 match self.project().state.as_mut().project() {
323 TimeoutCheckedStateProject::Timeout { fut } => fut.poll(cx),
324 TimeoutCheckedStateProject::NoTimeout { fut } => fut.poll(cx).map(Result::Ok),
325 }
326 }
327}
328
329#[must_use = "futures do nothing unless you `.await` or poll them"]
334#[derive(Debug)]
335pub struct Interval {
336 hnd: TimerHandle,
337 period: u32,
338}
339
340impl Interval {
341 #[inline]
346 pub fn new(period: Millis) -> Interval {
347 Interval {
348 hnd: TimerHandle::new(0),
349 period: cmp::max(period.0, 1),
350 }
351 }
352
353 #[inline]
354 pub async fn tick(&self) {
356 poll_fn(|cx| self.poll_tick(cx)).await;
357 }
358
359 #[inline]
360 pub fn poll_tick(&self, cx: &mut task::Context<'_>) -> Poll<()> {
362 if self.hnd.poll_elapsed(cx).is_ready() {
363 self.hnd.reset(u64::from(self.period));
364 Poll::Ready(())
365 } else {
366 Poll::Pending
367 }
368 }
369}
370
371impl crate::Stream for Interval {
372 type Item = ();
373
374 #[inline]
375 fn poll_next(self: Pin<&mut Self>, cx: &mut task::Context<'_>) -> Poll<Option<Self::Item>> {
376 self.poll_tick(cx).map(|()| Some(()))
377 }
378}
379
380#[cfg(test)]
381mod tests {
382 use futures_util::StreamExt;
383 use std::{future::poll_fn, rc::Rc, time};
384
385 use super::*;
386 use crate::future::lazy;
387
388 #[ntex::test]
392 async fn lowres_time_does_not_immediately_change() {
393 sleep(Millis(25)).await;
394
395 assert_eq!(now(), now());
396 }
397
398 #[ntex::test]
403 async fn lowres_time_updates_after_resolution_interval() {
404 sleep(Millis(50)).await;
405
406 let first_time = now();
407
408 sleep(Millis(25)).await;
409
410 let second_time = now();
411 assert!(second_time - first_time >= time::Duration::from_millis(25));
412 }
413
414 #[ntex::test]
418 async fn system_time_service_time_does_not_immediately_change() {
419 sleep(Seconds(1)).await;
420
421 assert_eq!(system_time(), system_time());
422 assert_eq!(system_time(), query_system_time());
423 }
424
425 #[ntex::test]
430 async fn system_time_service_time_updates_after_resolution_interval() {
431 sleep(Millis(100)).await;
432
433 let wait_time = 300;
434
435 let first_time = system_time()
436 .duration_since(time::SystemTime::UNIX_EPOCH)
437 .unwrap();
438
439 sleep(Millis(wait_time)).await;
440
441 let second_time = system_time()
442 .duration_since(time::SystemTime::UNIX_EPOCH)
443 .unwrap();
444
445 assert!(
446 second_time.checked_sub(first_time).unwrap()
447 >= time::Duration::from_millis(u64::from(wait_time))
448 );
449 }
450
451 #[ntex::test]
452 async fn test_sleep_0() {
453 sleep(Seconds(1)).await;
454
455 let first_time = now();
456 sleep(Millis(0)).await;
457 let second_time = now();
458 assert!(second_time - first_time >= time::Duration::from_millis(1));
459
460 let first_time = now();
461 sleep(Millis(1)).await;
462 let second_time = now();
463 assert!(second_time - first_time >= time::Duration::from_millis(1));
464
465 let first_time = now();
466 let fut = sleep(Millis(10000));
467 assert!(!fut.is_elapsed());
468 fut.reset(Millis::ZERO);
470 assert!(!fut.is_elapsed());
471 fut.await;
472 let second_time = now();
473 assert!(second_time - first_time >= time::Duration::from_millis(1));
474
475 let fut = sleep(Millis::ZERO);
476 assert!(!fut.is_elapsed());
477 fut.await;
478
479 let first_time = now();
480 let fut = Sleep {
481 hnd: TimerHandle::new(0),
482 };
483 assert!(fut.is_elapsed());
484 fut.await;
485 let second_time = now();
486 assert!(second_time - first_time < time::Duration::from_millis(1));
487
488 let first_time = now();
489 let fut = Rc::new(sleep(Millis(10_0000)));
490 let s = fut.clone();
491 ntex::rt::spawn(async move {
492 s.elapse();
493 });
494 poll_fn(|cx| fut.poll_elapsed(cx)).await;
495 assert!(fut.is_elapsed());
496 let second_time = now();
497 assert!(second_time - first_time < time::Duration::from_millis(1));
498 }
499
500 #[ntex::test]
501 async fn test_deadline() {
502 sleep(Seconds(1)).await;
503
504 let first_time = now();
505 let dl = deadline(Millis(1));
506 dl.await;
507 let second_time = now();
508 assert!(second_time - first_time >= time::Duration::from_millis(1));
509 assert!(timeout(Millis(100), deadline(Millis(0))).await.is_err());
510
511 let mut dl = deadline(Millis(1));
512 dl.reset(Millis::ZERO);
513 assert!(lazy(|cx| dl.poll_elapsed(cx)).await.is_pending());
514
515 let mut dl = deadline(Millis(1));
516 dl.reset(Millis(100));
517 let first_time = now();
518 dl.await;
519 let second_time = now();
520 assert!(second_time - first_time >= time::Duration::from_millis(100));
521
522 let mut dl = deadline(Millis(0));
523 assert!(dl.is_elapsed());
524 dl.reset(Millis(1));
525 assert!(lazy(|cx| dl.poll_elapsed(cx)).await.is_pending());
526
527 assert!(format!("{dl:?}").contains("Deadline"));
528 }
529
530 #[ntex::test]
531 async fn test_interval_zero_period() {
532 let int = interval(Millis::ZERO);
533 int.tick().await;
534
535 assert!(lazy(|cx| int.poll_tick(cx)).await.is_pending());
537 int.tick().await;
538 }
539
540 #[ntex::test]
541 async fn test_interval() {
542 let mut int = interval(Millis(250));
543
544 let time = time::Instant::now();
546 int.tick().await;
547 assert!(time.elapsed() < time::Duration::from_millis(50));
548
549 let time = time::Instant::now();
550 int.tick().await;
551 let elapsed = time.elapsed();
552 assert!(
553 elapsed > time::Duration::from_millis(200)
554 && elapsed < time::Duration::from_millis(450),
555 "elapsed: {elapsed:?}"
556 );
557
558 let time = time::Instant::now();
559 int.next().await;
560 let elapsed = time.elapsed();
561 assert!(
562 elapsed > time::Duration::from_millis(200)
563 && elapsed < time::Duration::from_millis(450),
564 "elapsed: {elapsed:?}"
565 );
566 }
567
568 #[ntex::test]
569 async fn test_interval_one_sec() {
570 let int = interval(Millis::ONE_SEC);
571 int.tick().await;
572
573 for _i in 0..3 {
574 let time = time::Instant::now();
575 int.tick().await;
576 let elapsed = time.elapsed();
577 assert!(
578 elapsed > time::Duration::from_secs(1)
579 && elapsed < time::Duration::from_millis(1300),
580 "elapsed: {elapsed:?}"
581 );
582 }
583 }
584
585 #[ntex::test]
586 async fn test_timeout_checked() {
587 let result = timeout_checked(Millis(200), sleep(Millis(100))).await;
588 assert!(result.is_ok());
589
590 let result = timeout_checked(Millis(5), sleep(Millis(100))).await;
591 assert!(result.is_err());
592
593 let result = timeout_checked(Millis(0), sleep(Millis(100))).await;
594 assert!(result.is_ok());
595 }
596}