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
55use glib:: {
66 prelude:: * ,
@@ -9,6 +9,8 @@ use glib::{
99 value:: ValueType ,
1010} ;
1111
12+ use futures_channel:: oneshot;
13+
1214use crate :: { AsyncResult , Cancellable } ;
1315
1416glib:: wrapper! {
@@ -380,6 +382,70 @@ impl<V: ValueType + Send> Task<V> {
380382unsafe impl < V : ValueType + Send > Send for Task < V > { }
381383unsafe 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) ]
384450mod test {
385451 use super :: * ;
0 commit comments