1// Take a look at the license at the top of the repository in the LICENSE file.
2
3use std::{error, fmt};
4
5use futures_util::{
6 future::{self, Either, Future},
7 pin_mut,
8};
9
10// rustdoc-stripper-ignore-next
11/// The error returned when a future times out.
12#[derive(Clone, Copy, PartialEq, Eq, Debug)]
13pub struct FutureWithTimeoutError;
14
15impl fmt::Display for FutureWithTimeoutError {
16 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
17 f.write_str(data:"The future timed out")
18 }
19}
20
21impl error::Error for FutureWithTimeoutError {}
22
23// rustdoc-stripper-ignore-next
24/// Add a timeout to a `Future`.
25///
26/// The `Future` must be spawned on an `Executor` backed by a `glib::MainContext`.
27pub async fn future_with_timeout_with_priority<T>(
28 priority: crate::Priority,
29 timeout: std::time::Duration,
30 fut: impl Future<Output = T>,
31) -> Result<T, FutureWithTimeoutError> {
32 let timeout: Pin + Send>> = crate::timeout_future_with_priority(priority, value:timeout);
33 pin_mut!(fut);
34
35 match future::select(future1:fut, future2:timeout).await {
36 Either::Left((x: T, _)) => Ok(x),
37 _ => Err(FutureWithTimeoutError),
38 }
39}
40
41// rustdoc-stripper-ignore-next
42/// Add a timeout to a `Future`.
43///
44/// The `Future` must be spawned on an `Executor` backed by a `glib::MainContext`.
45pub async fn future_with_timeout<T>(
46 timeout: std::time::Duration,
47 fut: impl Future<Output = T>,
48) -> Result<T, FutureWithTimeoutError> {
49 future_with_timeout_with_priority(crate::Priority::default(), timeout, fut).await
50}
51
52#[cfg(test)]
53mod tests {
54 use std::time::Duration;
55
56 use futures_util::FutureExt;
57
58 use super::*;
59 use crate::{MainContext, MainLoop};
60
61 #[test]
62 fn test_future_with_timeout() {
63 let c = MainContext::new();
64
65 let fut = future::pending::<()>();
66 let result = c.block_on(future_with_timeout(Duration::from_millis(20), fut));
67 assert_eq!(result, Err(FutureWithTimeoutError));
68
69 let fut = future::ready(());
70 let result = c.block_on(future_with_timeout(Duration::from_millis(20), fut));
71 assert_eq!(result, Ok(()));
72 }
73
74 #[test]
75 fn test_future_with_timeout_send() {
76 let c = MainContext::new();
77 let l = MainLoop::new(Some(&c), false);
78
79 let l_clone = l.clone();
80 let fut = future::pending::<()>();
81 c.spawn(
82 future_with_timeout(Duration::from_millis(20), fut).then(move |result| {
83 l_clone.quit();
84 assert_eq!(result, Err(FutureWithTimeoutError));
85 futures_util::future::ready(())
86 }),
87 );
88
89 l.run();
90
91 let l_clone = l.clone();
92 let fut = future::ready(());
93 c.spawn(
94 future_with_timeout(Duration::from_millis(20), fut).then(move |result| {
95 l_clone.quit();
96 assert_eq!(result, Ok(()));
97 futures_util::future::ready(())
98 }),
99 );
100
101 l.run();
102 }
103}
104