glib/
future_with_timeout.rs

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("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 = crate::timeout_future_with_priority(priority, timeout);
33    pin_mut!(fut);
34
35    match future::select(fut, timeout).await {
36        Either::Left((x, _)) => 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}