Skip to content

Commit 0de4a0f

Browse files
committed
gio: Add helper for getting a future from a Cancellable
1 parent 6830cac commit 0de4a0f

File tree

2 files changed

+51
-1
lines changed

2 files changed

+51
-1
lines changed

gio/Gir.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -342,7 +342,7 @@ manual_traits = ["CancellableExtManual"]
342342
ignore = true
343343
[[object.function]]
344344
name = "source_new"
345-
# Needs manual bindings for setting callback, etc
345+
# Only for use with other souce implementations
346346
ignore = true
347347
[[object.signal]]
348348
name = "cancelled"

gio/src/cancellable.rs

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22

33
use crate::prelude::*;
44
use crate::Cancellable;
5+
use futures_channel::oneshot;
6+
use futures_core::Future;
57
use glib::{translate::*, IsA};
68
use std::num::NonZeroU64;
79

@@ -62,6 +64,10 @@ pub trait CancellableExtManual {
6264
/// operation is finished and the signal handler is removed.
6365
#[doc(alias = "g_cancellable_disconnect")]
6466
fn disconnect_cancelled(&self, id: CancelledHandlerId);
67+
// rustdoc-stripper-ignore-next
68+
/// Returns a `Future` that completes when the cancellable becomes cancelled. Completes
69+
/// immediately if the cancellable is already cancelled.
70+
fn future(&self) -> std::pin::Pin<Box<dyn Future<Output = ()> + Send + Sync + 'static>>;
6571
}
6672

6773
impl<O: IsA<Cancellable>> CancellableExtManual for O {
@@ -101,6 +107,19 @@ impl<O: IsA<Cancellable>> CancellableExtManual for O {
101107
fn disconnect_cancelled(&self, id: CancelledHandlerId) {
102108
unsafe { ffi::g_cancellable_disconnect(self.as_ptr() as *mut _, id.as_raw()) };
103109
}
110+
fn future(&self) -> std::pin::Pin<Box<dyn Future<Output = ()> + Send + Sync + 'static>> {
111+
let cancellable = self.as_ref().clone();
112+
let (tx, rx) = oneshot::channel();
113+
let id = cancellable.connect_cancelled(move |_| {
114+
let _ = tx.send(());
115+
});
116+
Box::pin(async move {
117+
rx.await.unwrap();
118+
if let Some(id) = id {
119+
cancellable.disconnect_cancelled(id);
120+
}
121+
})
122+
}
104123
}
105124

106125
#[cfg(test)]
@@ -114,4 +133,35 @@ mod tests {
114133
c.cancel(); // if it doesn't crash at this point, then we're good to go!
115134
c.disconnect_cancelled(id.unwrap());
116135
}
136+
137+
#[test]
138+
fn cancellable_future() {
139+
let c = Cancellable::new();
140+
c.cancel();
141+
glib::MainContext::new().block_on(c.future());
142+
}
143+
144+
#[test]
145+
fn cancellable_future_thread() {
146+
let cancellable = Cancellable::new();
147+
let c = cancellable.clone();
148+
std::thread::spawn(move || c.cancel()).join().unwrap();
149+
glib::MainContext::new().block_on(cancellable.future());
150+
}
151+
152+
#[test]
153+
fn cancellable_future_delayed() {
154+
let ctx = glib::MainContext::new();
155+
let c = Cancellable::new();
156+
let (tx, rx) = oneshot::channel();
157+
{
158+
let c = c.clone();
159+
ctx.spawn_local(async move {
160+
c.future().await;
161+
tx.send(()).unwrap();
162+
});
163+
}
164+
std::thread::spawn(move || c.cancel()).join().unwrap();
165+
ctx.block_on(rx).unwrap();
166+
}
117167
}

0 commit comments

Comments
 (0)