referrerpolicy=no-referrer-when-downgrade

sc_mixnet/
maybe_inf_delay.rs

1// This file is part of Substrate.
2
3// Copyright (C) Parity Technologies (UK) Ltd.
4// SPDX-License-Identifier: GPL-3.0-or-later WITH Classpath-exception-2.0
5
6// This program is free software: you can redistribute it and/or modify
7// it under the terms of the GNU General Public License as published by
8// the Free Software Foundation, either version 3 of the License, or
9// (at your option) any later version.
10
11// This program is distributed in the hope that it will be useful,
12// but WITHOUT ANY WARRANTY; without even the implied warranty of
13// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14// GNU General Public License for more details.
15
16// You should have received a copy of the GNU General Public License
17// along with this program. If not, see <https://www.gnu.org/licenses/>.
18
19use futures::{future::FusedFuture, FutureExt};
20use futures_timer::Delay;
21use std::{
22	future::Future,
23	pin::Pin,
24	task::{Context, Poll, Waker},
25	time::Duration,
26};
27
28enum Inner {
29	Infinite {
30		/// Waker from the most recent `poll` call. If `None`, either `poll` has not been called
31		/// yet, we returned `Poll::Ready` from the last call, or the waker is attached to `delay`.
32		waker: Option<Waker>,
33		delay: Option<Delay>,
34	},
35	Finite(Delay),
36}
37
38/// Like [`Delay`] but the duration can be infinite (in which case the future will never fire).
39/// Unlike [`Delay`], implements [`FusedFuture`], with [`is_terminated`](Self::is_terminated)
40/// returning `true` when the delay is infinite. As with [`Delay`], once [`poll`](Self::poll)
41/// returns [`Poll::Ready`], it will continue to do so until [`reset`](Self::reset) is called.
42pub struct MaybeInfDelay(Inner);
43
44impl MaybeInfDelay {
45	/// Create a new `MaybeInfDelay` future. If `duration` is [`Some`], the future will fire after
46	/// the given duration has elapsed. If `duration` is [`None`], the future will "never" fire
47	/// (although see [`reset`](Self::reset)).
48	pub fn new(duration: Option<Duration>) -> Self {
49		match duration {
50			Some(duration) => Self(Inner::Finite(Delay::new(duration))),
51			None => Self(Inner::Infinite { waker: None, delay: None }),
52		}
53	}
54
55	/// Reset the timer. `duration` is handled just like in [`new`](Self::new). Note that while
56	/// this is similar to `std::mem::replace(&mut self, MaybeInfDelay::new(duration))`, with
57	/// `replace` you would have to manually ensure [`poll`](Self::poll) was called again; with
58	/// `reset` this is not necessary.
59	pub fn reset(&mut self, duration: Option<Duration>) {
60		match duration {
61			Some(duration) => match &mut self.0 {
62				Inner::Infinite { waker, delay } => {
63					let mut delay = match delay.take() {
64						Some(mut delay) => {
65							delay.reset(duration);
66							delay
67						},
68						None => Delay::new(duration),
69					};
70					if let Some(waker) = waker.take() {
71						let mut cx = Context::from_waker(&waker);
72						match delay.poll_unpin(&mut cx) {
73							Poll::Pending => (), // Waker attached to delay
74							Poll::Ready(_) => waker.wake(),
75						}
76					}
77					self.0 = Inner::Finite(delay);
78				},
79				Inner::Finite(delay) => delay.reset(duration),
80			},
81			None =>
82				self.0 = match std::mem::replace(
83					&mut self.0,
84					Inner::Infinite { waker: None, delay: None },
85				) {
86					Inner::Finite(delay) => Inner::Infinite { waker: None, delay: Some(delay) },
87					infinite => infinite,
88				},
89		}
90	}
91}
92
93impl Future for MaybeInfDelay {
94	type Output = ();
95
96	fn poll(mut self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
97		match &mut self.0 {
98			Inner::Infinite { waker, .. } => {
99				*waker = Some(cx.waker().clone());
100				Poll::Pending
101			},
102			Inner::Finite(delay) => delay.poll_unpin(cx),
103		}
104	}
105}
106
107impl FusedFuture for MaybeInfDelay {
108	fn is_terminated(&self) -> bool {
109		matches!(self.0, Inner::Infinite { .. })
110	}
111}