Skip to content

Commit db5f305

Browse files
authored
Merge pull request #818 from jf2048/task-spawn-blocking
gio: add spawn_blocking()
2 parents 96e89df + 2f2dbd4 commit db5f305

File tree

5 files changed

+404
-56
lines changed

5 files changed

+404
-56
lines changed

gio/src/task.rs

Lines changed: 67 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
// Take a look at the license at the top of the repository in the LICENSE file.
22

3-
use std::{boxed::Box as Box_, mem::transmute, ptr};
3+
use std::{boxed::Box as Box_, future::Future, mem::transmute, panic, ptr};
44

55
use glib::{
66
prelude::*,
@@ -9,6 +9,8 @@ use glib::{
99
value::ValueType,
1010
};
1111

12+
use futures_channel::oneshot;
13+
1214
use crate::{AsyncResult, Cancellable};
1315

1416
glib::wrapper! {
@@ -380,6 +382,70 @@ impl<V: ValueType + Send> Task<V> {
380382
unsafe impl<V: ValueType + Send> Send for Task<V> {}
381383
unsafe impl<V: ValueType + Send> Sync for Task<V> {}
382384

385+
// rustdoc-stripper-ignore-next
386+
/// A handle to a task running on the I/O thread pool.
387+
///
388+
/// Like [`std::thread::JoinHandle`] for a blocking I/O task rather than a thread. The return value
389+
/// from the task can be retrieved by awaiting on this handle. Dropping the handle "detaches" the
390+
/// task, allowing it to complete but discarding the return value.
391+
#[derive(Debug)]
392+
pub struct JoinHandle<T> {
393+
rx: oneshot::Receiver<std::thread::Result<T>>,
394+
}
395+
396+
impl<T> JoinHandle<T> {
397+
#[inline]
398+
fn new() -> (Self, oneshot::Sender<std::thread::Result<T>>) {
399+
let (tx, rx) = oneshot::channel();
400+
(Self { rx }, tx)
401+
}
402+
}
403+
404+
impl<T> Future for JoinHandle<T> {
405+
type Output = std::thread::Result<T>;
406+
#[inline]
407+
fn poll(
408+
mut self: std::pin::Pin<&mut Self>,
409+
cx: &mut std::task::Context<'_>,
410+
) -> std::task::Poll<Self::Output> {
411+
std::pin::Pin::new(&mut self.rx)
412+
.poll(cx)
413+
.map(|r| r.unwrap())
414+
}
415+
}
416+
417+
impl<T> futures_core::FusedFuture for JoinHandle<T> {
418+
#[inline]
419+
fn is_terminated(&self) -> bool {
420+
self.rx.is_terminated()
421+
}
422+
}
423+
424+
// rustdoc-stripper-ignore-next
425+
/// Runs a blocking I/O task on the I/O thread pool.
426+
///
427+
/// Calls `func` on the internal Gio thread pool for blocking I/O operations. The thread pool is
428+
/// shared with other Gio async I/O operations, and may rate-limit the tasks it receives. Callers
429+
/// may want to avoid blocking indefinitely by making sure blocking calls eventually time out.
430+
///
431+
/// This function should not be used to spawn async tasks. Instead, use
432+
/// [`glib::MainContext::spawn`] or [`glib::MainContext::spawn_local`] to run a future.
433+
pub fn spawn_blocking<T, F>(func: F) -> JoinHandle<T>
434+
where
435+
T: Send + 'static,
436+
F: FnOnce() -> T + Send + 'static,
437+
{
438+
// use Cancellable::NONE as source obj to fulfill `Send` requirement
439+
let task = unsafe { Task::<bool>::new(Cancellable::NONE, Cancellable::NONE, |_, _| {}) };
440+
let (join, tx) = JoinHandle::new();
441+
task.run_in_thread(move |_, _: Option<&Cancellable>, _| {
442+
let res = panic::catch_unwind(panic::AssertUnwindSafe(func));
443+
let _ = tx.send(res);
444+
});
445+
446+
join
447+
}
448+
383449
#[cfg(test)]
384450
mod test {
385451
use super::*;

glib/src/lib.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -197,14 +197,15 @@ pub use self::bridged_logging::{rust_log_handler, GlibLogger, GlibLoggerDomain,
197197
pub mod subclass;
198198

199199
mod main_context_futures;
200+
pub use main_context_futures::{JoinError, JoinHandle};
200201
mod source_futures;
201202
pub use self::source_futures::*;
202203

203204
mod future_with_timeout;
204205
pub use self::future_with_timeout::*;
205206

206207
mod thread_pool;
207-
pub use self::thread_pool::ThreadPool;
208+
pub use self::thread_pool::{ThreadHandle, ThreadPool};
208209

209210
pub mod thread_guard;
210211

0 commit comments

Comments
 (0)