relay_server/utils/sleep_handle.rs
1use std::future::Future;
2use std::pin::Pin;
3use std::task::Poll;
4use std::time::Duration;
5
6use futures::FutureExt;
7use futures::future::FusedFuture;
8
9/// A future wrapper around [`tokio::time::Sleep`].
10///
11/// When initialized with [`SleepHandle::idle`], this future is pending indefinitely every time it
12/// is polled. To initiate a delay, use [`set`](Self::set). After the delay has passed, the future
13/// resolves with `()` **exactly once** and resets to idle. To reset the future while it is
14/// sleeping, use [`reset`](Self::reset).
15#[derive(Debug)]
16pub struct SleepHandle(Option<Pin<Box<tokio::time::Sleep>>>);
17
18impl SleepHandle {
19 /// Creates [`SleepHandle`] and sets its internal state to an indefinitely pending future.
20 pub fn idle() -> Self {
21 Self(None)
22 }
23
24 /// Resets the internal state to an indefinitely pending future.
25 pub fn reset(&mut self) {
26 self.0 = None;
27 }
28
29 /// Sets the internal state to a future that will yield after `duration` time has elapsed.
30 pub fn set(&mut self, duration: Duration) {
31 self.0 = Some(Box::pin(tokio::time::sleep(duration)));
32 }
33
34 /// Primes the internal state like [`Self::set`], only if currently [`Self::idle`].
35 pub fn set_if_idle(&mut self, duration: Duration) {
36 if self.is_idle() {
37 self.set(duration)
38 }
39 }
40
41 /// Checks whether the internal state is currently pending indefinite.
42 pub fn is_idle(&self) -> bool {
43 self.0.is_none()
44 }
45}
46
47impl Future for SleepHandle {
48 type Output = ();
49
50 fn poll(mut self: Pin<&mut Self>, cx: &mut std::task::Context<'_>) -> Poll<Self::Output> {
51 let poll = match &mut self.0 {
52 Some(sleep) => sleep.poll_unpin(cx),
53 None => Poll::Pending,
54 };
55
56 if poll.is_ready() {
57 self.reset();
58 }
59
60 poll
61 }
62}
63
64impl FusedFuture for SleepHandle {
65 fn is_terminated(&self) -> bool {
66 // The handle never terminates, it may be reset or primed at any time.
67 false
68 }
69}