async_executors/iface/
timer.rs

1use
2{
3	std          :: { time::Duration, future::Future, task::{ Poll, Context }, pin::Pin } ,
4	futures_core :: { future::BoxFuture                                                 } ,
5
6	pin_project::pin_project,
7};
8
9/// Represents the fact that an executor has timer functionality.
10///
11//  Implementation:
12//  - for tokio: use tokio when tokio_time feature is enabled, futures-timer otherwise.
13//  - for async-global-executor: use futures-timer.
14//  - for glommio: has own timer that can't be turned off. But we don't use it because
15//    it's not Send.
16//  - for bindgen: use futures-timer
17//  - for async-std: has a timer that cannot be turned off. Isn't Send on Wasm.
18//
19//  The trait needs to be available inconditionally, as a library must be able
20//  to depend on it without specifying a backend.
21//
22#[ blanket::blanket( derive( Ref, Mut, Rc, Arc, Box ) ) ]
23//
24pub trait Timer
25{
26	/// Future that resolves after a given duration.
27	//
28	#[ must_use = "sleep() returns a future, which does nothing unless awaited" ]
29	//
30	fn sleep( &self, dur: Duration ) -> BoxFuture<'static, ()>;
31}
32
33
34
35
36// The following code was taken from tor-rtcompat https://gitlab.torproject.org/tpo/core/arti/-/blob/main/tor-rtcompat/src/timer.rs
37// This is licenced: "MIT OR Apache-2.0".
38//
39
40
41
42/// An extension trait on [`Timer`] for timeouts and clock delays.
43//
44pub trait TimerExt: Timer
45{
46	/// Wrap a [`Future`] with a timeout.
47	///
48	/// The output of the new future will be the returned value of
49	/// `future` if it completes within `duration`.  Otherwise, it
50	/// will be `Err(TimeoutError)`.
51	///
52	/// # Limitations
53	///
54	/// This uses [`Timer::sleep`] for its timer, and is
55	/// subject to the same limitations.
56	//
57	#[ must_use = "timeout() returns a future, which does nothing unless awaited." ]
58	//
59	fn timeout<F: Future>( &self, duration: Duration, future: F ) -> Timeout<F>
60	{
61		let sleep_future = self.sleep( duration );
62
63		Timeout { future, sleep_future }
64	}
65}
66
67
68impl<T: Timer> TimerExt for T {}
69
70
71/// An error value given when a function times out.
72///
73/// This value is generated when the timeout from
74/// [`TimerExt::timeout`] expires before the provided future
75/// is ready.
76//
77#[ derive( Copy, Clone, Debug, Eq, PartialEq ) ]
78//
79#[allow(clippy::exhaustive_structs)]
80//
81pub struct TimeoutError;
82
83
84impl std::error::Error for TimeoutError {}
85
86
87impl std::fmt::Display for TimeoutError
88{
89	fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result
90	{
91		write!( f, "Timeout expired" )
92	}
93}
94
95
96impl From<TimeoutError> for std::io::Error
97{
98	fn from( err: TimeoutError ) -> std::io::Error
99	{
100		std::io::Error::new( std::io::ErrorKind::TimedOut, err )
101	}
102}
103
104
105/// A timeout returned by [`TimerExt::timeout`].
106//
107#[pin_project]
108//
109pub struct Timeout<T>
110{
111	/// The future we want to execute.
112	//
113	#[pin] future: T,
114
115	/// The future implementing the timeout.
116	//
117	sleep_future: BoxFuture<'static, ()>,
118}
119
120
121
122impl<T> Future for Timeout<T>
123
124	where T: Future,
125
126{
127	type Output = Result< T::Output, TimeoutError >;
128
129
130	fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output>
131	{
132		let this = self.project();
133
134
135		if let Poll::Ready(x) = this.future.poll(cx)
136		{
137			return Poll::Ready(Ok(x));
138		}
139
140
141		match this.sleep_future.as_mut().poll(cx)
142		{
143			Poll::Pending   => Poll::Pending                    ,
144			Poll::Ready(()) => Poll::Ready( Err(TimeoutError) ) ,
145		}
146	}
147}
148
149
150impl<T> std::fmt::Debug for Timeout<T>
151{
152	fn fmt( &self, f: &mut std::fmt::Formatter<'_> ) -> std::fmt::Result
153	{
154		write!( f, "Timeout future" )
155	}
156}
157