Skip to content

Commit cd5eedd

Browse files
authored
Merge pull request #528 from jsparber/future_timeout
glib: Add function to wrap a future with a timeout
2 parents 41d0dab + 855e63f commit cd5eedd

File tree

3 files changed

+105
-1
lines changed

3 files changed

+105
-1
lines changed

glib/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ futures-core = { version = "0.3", default-features = false }
2626
futures-task = { version = "0.3", default-features = false }
2727
futures-executor = "0.3"
2828
futures-channel = "0.3"
29+
futures-util = "0.3"
2930
ffi = { package = "glib-sys", path = "sys" }
3031
gobject_ffi = { package = "gobject-sys", path = "gobject-sys" }
3132
glib-macros = { path = "../glib-macros" }
@@ -34,7 +35,6 @@ smallvec = "1.0"
3435
thiserror = "1"
3536

3637
[dev-dependencies]
37-
futures-util = "0.3"
3838
tempfile = "3"
3939
gir-format-check = "^0.1"
4040

glib/src/future_with_timeout.rs

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

glib/src/lib.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -155,6 +155,9 @@ mod main_context_futures;
155155
mod source_futures;
156156
pub use self::source_futures::*;
157157

158+
mod future_with_timeout;
159+
pub use self::future_with_timeout::*;
160+
158161
mod thread_pool;
159162
pub use self::thread_pool::ThreadPool;
160163

0 commit comments

Comments
 (0)