1
1
// Take a look at the license at the top of the repository in the LICENSE file.
2
2
3
- use std:: { boxed:: Box as Box_ , mem:: transmute, ptr} ;
3
+ use std:: { boxed:: Box as Box_ , future :: Future , mem:: transmute, panic , ptr} ;
4
4
5
5
use glib:: {
6
6
prelude:: * ,
@@ -9,6 +9,8 @@ use glib::{
9
9
value:: ValueType ,
10
10
} ;
11
11
12
+ use futures_channel:: oneshot;
13
+
12
14
use crate :: { AsyncResult , Cancellable } ;
13
15
14
16
glib:: wrapper! {
@@ -380,6 +382,70 @@ impl<V: ValueType + Send> Task<V> {
380
382
unsafe impl < V : ValueType + Send > Send for Task < V > { }
381
383
unsafe impl < V : ValueType + Send > Sync for Task < V > { }
382
384
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
+
383
449
#[ cfg( test) ]
384
450
mod test {
385
451
use super :: * ;
0 commit comments