1 | // Take a look at the license at the top of the repository in the LICENSE file. |
2 | |
3 | use std::{error, fmt}; |
4 | |
5 | use 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)] |
13 | pub struct FutureWithTimeoutError; |
14 | |
15 | impl 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 | |
21 | impl 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`. |
27 | pub 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`. |
45 | pub 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)] |
53 | mod 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 | |