async_winit/
timer.rs

1/*
2
3`async-winit` is free software: you can redistribute it and/or modify it under the terms of one of
4the following licenses:
5
6* GNU Lesser General Public License as published by the Free Software Foundation, either
7  version 3 of the License, or (at your option) any later version.
8* Mozilla Public License as published by the Mozilla Foundation, version 2.
9
10`async-winit` is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even
11the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General
12Public License and the Patron License for more details.
13
14You should have received a copy of the GNU Lesser General Public License and the Mozilla
15Public License along with `async-winit`. If not, see <https://www.gnu.org/licenses/>.
16
17*/
18
19// This file is partially derived from `async-io`, which was originally created by Stjepan Glavina
20// and contributors. It was originally released under the MIT license and Apache 2.0 license.
21
22//! Asynchronous timers.
23
24use crate::reactor::Reactor;
25use crate::sync::ThreadSafety;
26
27use std::fmt;
28use std::future::Future;
29use std::pin::Pin;
30use std::task::{Context, Poll, Waker};
31use std::time::{Duration, Instant};
32
33use futures_lite::stream::Stream;
34
35/// A future or stream that emits timer events.
36///
37/// This timer waits for a specific duration or interval to elapse before returning `Poll::Ready`.
38/// It uses the [`ControlFlow::WaitUntil`] mechanism to wait for the timer to fire.
39///
40/// This type is similar to the [`Timer`] type in the `async-io` crate. The main practical difference
41/// is that, on certain platforms, this `Timer` type may have marginally higher precision.
42///
43/// [`ControlFlow::WaitUntil`]: crate::event_loop::ControlFlow::WaitUntil
44/// [`Timer`]: https://docs.rs/async-io/latest/async_io/timer/struct.Timer.html
45pub struct Timer<TS: ThreadSafety = crate::DefaultThreadSafety> {
46    /// Static reference to the reactor.
47    reactor: TS::Rc<Reactor<TS>>,
48
49    /// This timer's ID and the last waker that polled it.
50    id_and_waker: Option<(usize, Waker)>,
51
52    /// The time at which this timer will fire.
53    deadline: Option<Instant>,
54
55    /// The period.
56    period: Duration,
57}
58
59impl<TS: ThreadSafety> fmt::Debug for Timer<TS> {
60    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
61        f.debug_struct("Timer")
62            .field("deadline", &self.deadline)
63            .field("period", &self.period)
64            .field("registered", &self.id_and_waker.is_some())
65            .finish()
66    }
67}
68
69impl<TS: ThreadSafety> Unpin for Timer<TS> {}
70
71impl<TS: ThreadSafety> Timer<TS> {
72    /// Create a new timer that will never fire.
73    pub fn never() -> Self {
74        Self {
75            reactor: Reactor::<TS>::get(),
76            id_and_waker: None,
77            deadline: None,
78            period: Duration::MAX,
79        }
80    }
81
82    /// Returns `true` if this timer will eventually return `Poll::Ready`.
83    pub fn will_fire(&self) -> bool {
84        self.deadline.is_some()
85    }
86
87    /// Create a timer that fires after the given duration.
88    pub fn after(duration: Duration) -> Self {
89        Instant::now()
90            .checked_add(duration)
91            .map_or_else(Self::never, Self::at)
92    }
93
94    /// Create a timer that fires at the given time.
95    pub fn at(deadline: Instant) -> Self {
96        Self::interval_at(deadline, Duration::MAX)
97    }
98
99    /// Create a timer that fires on an interval.
100    pub fn interval(period: Duration) -> Self {
101        Instant::now()
102            .checked_add(period)
103            .map_or_else(Self::never, |deadline| Self::interval_at(deadline, period))
104    }
105
106    /// Create a timer that fires on an interval starting at the given time.
107    pub fn interval_at(start: Instant, period: Duration) -> Self {
108        Self {
109            reactor: Reactor::<TS>::get(),
110            id_and_waker: None,
111            deadline: Some(start),
112            period,
113        }
114    }
115
116    /// Set this timer to never fire.
117    pub fn set_never(&mut self) {
118        self.clear();
119        self.deadline = None;
120    }
121
122    /// Set this timer to fire after the given duration.
123    pub fn set_after(&mut self, duration: Duration) {
124        match Instant::now().checked_add(duration) {
125            Some(deadline) => self.set_at(deadline),
126            None => self.set_never(),
127        }
128    }
129
130    /// Set this timer to fire at the given deadline.
131    pub fn set_at(&mut self, deadline: Instant) {
132        self.set_interval_at(deadline, Duration::MAX)
133    }
134
135    /// Set this timer to run at an interval.
136    pub fn set_interval(&mut self, period: Duration) {
137        match Instant::now().checked_add(period) {
138            Some(deadline) => self.set_interval_at(deadline, period),
139            None => self.set_never(),
140        }
141    }
142
143    /// Set this timer to run on an interval starting at the given time.
144    pub fn set_interval_at(&mut self, start: Instant, period: Duration) {
145        self.clear();
146
147        self.deadline = Some(start);
148        self.period = period;
149
150        if let Some((id, waker)) = self.id_and_waker.as_mut() {
151            // Re-register the timer into the reactor.
152            *id = self.reactor.insert_timer(start, waker);
153        }
154    }
155
156    fn clear(&mut self) {
157        if let (Some(deadline), Some((id, _))) = (self.deadline.take(), self.id_and_waker.take()) {
158            self.reactor.remove_timer(deadline, id);
159        }
160    }
161}
162
163impl<TS: ThreadSafety> Drop for Timer<TS> {
164    fn drop(&mut self) {
165        self.clear();
166    }
167}
168
169impl<TS: ThreadSafety> Future for Timer<TS> {
170    type Output = Instant;
171
172    fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
173        self.poll_next(cx).map(Option::unwrap)
174    }
175}
176
177impl<TS: ThreadSafety> Stream for Timer<TS> {
178    type Item = Instant;
179
180    fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
181        let this = self.get_mut();
182
183        if let Some(ref mut deadline) = this.deadline {
184            // Check if the timer is ready.
185            if *deadline < Instant::now() {
186                if let Some((id, _)) = this.id_and_waker.take() {
187                    this.reactor.remove_timer(*deadline, id);
188                }
189
190                let result_time = *deadline;
191
192                if let Some(next) = deadline.checked_add(this.period) {
193                    *deadline = next;
194
195                    // Register the timer into the reactor.
196                    let id = this.reactor.insert_timer(next, cx.waker());
197                    this.id_and_waker = Some((id, cx.waker().clone()));
198                } else {
199                    this.deadline = None;
200                }
201
202                // Return the time that we fired at.
203                return Poll::Ready(Some(result_time));
204            } else {
205                match &this.id_and_waker {
206                    None => {
207                        // This timer needs to be registered.
208                        let id = this.reactor.insert_timer(*deadline, cx.waker());
209                        this.id_and_waker = Some((id, cx.waker().clone()));
210                    }
211
212                    Some((id, w)) if !w.will_wake(cx.waker()) => {
213                        // Deregister timer and remove the old waker.
214                        this.reactor.remove_timer(*deadline, *id);
215
216                        // Register the timer into the reactor.
217                        let id = this.reactor.insert_timer(*deadline, cx.waker());
218                        this.id_and_waker = Some((id, cx.waker().clone()));
219                    }
220
221                    _ => {}
222                }
223            }
224        }
225
226        Poll::Pending
227    }
228}