1use futures::{stream::Stream, task::Context};
4use futures_timer::Delay;
5use std::{future::Future, pin::Pin, task::Poll, time::Duration};
6
7#[cfg(target_family = "wasm")]
8use instant::Instant;
9#[cfg(not(target_family = "wasm"))]
10use std::time::Instant;
11
12#[derive(Debug)]
19pub struct Ticker {
20 interval: Duration,
21 next: Instant,
22 schedule: Delay,
23}
24
25impl Ticker {
26 pub fn new(interval: Duration) -> Ticker {
29 Ticker::new_with_next(interval, interval)
30 }
31
32 pub fn new_with_next(interval: Duration, first_in: Duration) -> Ticker {
35 let first = Instant::now() + first_in;
36 Ticker {
37 interval,
38 next: first,
39 schedule: Delay::new(first_in),
40 }
41 }
42
43 pub fn next_tick(&self) -> Instant {
45 self.next_tick_from(Instant::now())
46 }
47
48 pub fn next_tick_from(&self, now: Instant) -> Instant {
54 if self.next > now {
55 return self.next;
56 }
57 let raw_next = self.next + self.interval;
58 if raw_next > now {
59 return raw_next;
60 }
61 if self.interval.as_nanos() == 0 {
62 return now;
65 }
66 let missed_times = 1 + ((now - raw_next).as_nanos() / self.interval.as_nanos()) as u32;
70 self.next + self.interval * (missed_times + 1)
71 }
72}
73
74impl Stream for Ticker {
75 type Item = Instant;
76
77 fn poll_next(mut self: Pin<&mut Self>, cx: &mut Context) -> Poll<Option<Self::Item>> {
78 let schedule = Pin::new(&mut self.schedule);
79 match schedule.poll(cx) {
80 Poll::Pending => Poll::Pending,
81
82 Poll::Ready(_) => {
83 let now = Instant::now();
84 let next = self.next_tick_from(now);
85 self.next = next;
86 self.schedule.reset(
87 next.checked_duration_since(now)
88 .unwrap_or_else(|| Duration::from_nanos(0)),
89 );
90 Poll::Ready(Some(now))
91 }
92 }
93 }
94
95 fn size_hint(&self) -> (usize, Option<usize>) {
96 (1, None)
97 }
98}