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
55use futures_channel:: oneshot;
66
@@ -13,6 +13,30 @@ pub struct ThreadPool(ptr::NonNull<ffi::GThreadPool>);
1313unsafe impl Send for ThreadPool { }
1414unsafe 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+
1640impl 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