Skip to content

Commit 64452aa

Browse files
committed
glib: catch panics in ThreadPool push methods
1 parent f57819c commit 64452aa

File tree

2 files changed

+46
-12
lines changed

2 files changed

+46
-12
lines changed

glib/src/lib.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -205,7 +205,7 @@ mod future_with_timeout;
205205
pub use self::future_with_timeout::*;
206206

207207
mod thread_pool;
208-
pub use self::thread_pool::ThreadPool;
208+
pub use self::thread_pool::{ThreadHandle, ThreadPool};
209209

210210
pub mod thread_guard;
211211

glib/src/thread_pool.rs

Lines changed: 45 additions & 11 deletions
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::{future::Future, ptr};
3+
use std::{future::Future, panic, ptr};
44

55
use futures_channel::oneshot;
66

@@ -13,6 +13,30 @@ pub struct ThreadPool(ptr::NonNull<ffi::GThreadPool>);
1313
unsafe impl Send for ThreadPool {}
1414
unsafe impl Sync for ThreadPool {}
1515

16+
// rustdoc-stripper-ignore-next
17+
/// A handle to a thread running on a [`ThreadPool`].
18+
///
19+
/// Like [`std::thread::JoinHandle`] for a GLib thread. The return value from the task can be
20+
/// retrieved by calling [`ThreadHandle::join`]. Dropping the handle "detaches" the thread,
21+
/// allowing it to complete but discarding the return value.
22+
#[derive(Debug)]
23+
pub struct ThreadHandle<T> {
24+
rx: std::sync::mpsc::Receiver<std::thread::Result<T>>,
25+
}
26+
27+
impl<T> ThreadHandle<T> {
28+
// rustdoc-stripper-ignore-next
29+
/// Waits for the associated thread to finish.
30+
///
31+
/// Blocks until the associated thread returns. Returns `Ok` with the value returned from the
32+
/// thread, or `Err` if the thread panicked. This function will return immediately if the
33+
/// associated thread has already finished.
34+
#[inline]
35+
pub fn join(self) -> std::thread::Result<T> {
36+
self.rx.recv().unwrap()
37+
}
38+
}
39+
1640
impl ThreadPool {
1741
#[doc(alias = "g_thread_pool_new")]
1842
pub fn shared(max_threads: Option<u32>) -> Result<Self, crate::Error> {
@@ -53,9 +77,15 @@ impl ThreadPool {
5377
}
5478

5579
#[doc(alias = "g_thread_pool_push")]
56-
pub fn push<F: FnOnce() + Send + 'static>(&self, func: F) -> Result<(), crate::Error> {
80+
pub fn push<T: Send + 'static, F: FnOnce() -> T + Send + 'static>(
81+
&self,
82+
func: F,
83+
) -> Result<ThreadHandle<T>, crate::Error> {
84+
let (tx, rx) = std::sync::mpsc::sync_channel(1);
5785
unsafe {
58-
let func: Box<dyn FnOnce() + Send + 'static> = Box::new(func);
86+
let func: Box<dyn FnOnce() + Send + 'static> = Box::new(move || {
87+
let _ = tx.send(panic::catch_unwind(panic::AssertUnwindSafe(func)));
88+
});
5989
let func = Box::new(func);
6090
let mut err = ptr::null_mut();
6191

@@ -66,7 +96,7 @@ impl ThreadPool {
6696
&mut err,
6797
));
6898
if ret {
69-
Ok(())
99+
Ok(ThreadHandle { rx })
70100
} else {
71101
let _ = Box::from_raw(func);
72102
Err(from_glib_full(err))
@@ -77,11 +107,12 @@ impl ThreadPool {
77107
pub fn push_future<T: Send + 'static, F: FnOnce() -> T + Send + 'static>(
78108
&self,
79109
func: F,
80-
) -> Result<impl Future<Output = T> + Send + Sync + 'static, crate::Error> {
110+
) -> Result<impl Future<Output = std::thread::Result<T>> + Send + Sync + 'static, crate::Error>
111+
{
81112
let (sender, receiver) = oneshot::channel();
82113

83114
self.push(move || {
84-
let _ = sender.send(func());
115+
let _ = sender.send(panic::catch_unwind(panic::AssertUnwindSafe(func)));
85116
})?;
86117

87118
Ok(async move { receiver.await.expect("Dropped before executing") })
@@ -198,11 +229,14 @@ mod tests {
198229
let p = ThreadPool::exclusive(1).unwrap();
199230
let (sender, receiver) = mpsc::channel();
200231

201-
p.push(move || {
202-
sender.send(true).unwrap();
203-
})
204-
.unwrap();
232+
let handle = p
233+
.push(move || {
234+
sender.send(true).unwrap();
235+
123
236+
})
237+
.unwrap();
205238

239+
assert_eq!(handle.join().unwrap(), 123);
206240
assert_eq!(receiver.recv(), Ok(true));
207241
}
208242

@@ -214,6 +248,6 @@ mod tests {
214248
let fut = p.push_future(|| true).unwrap();
215249

216250
let res = c.block_on(fut);
217-
assert!(res);
251+
assert!(res.unwrap());
218252
}
219253
}

0 commit comments

Comments
 (0)