@@ -52,6 +52,11 @@ pub trait Runtime: Send + 'static {
52
52
fn spawn < F > ( fut : F ) -> Self :: JoinHandle
53
53
where
54
54
F : Future < Output = ( ) > + Send + ' static ;
55
+
56
+ /// Spawn a function onto this runtime's blocking event loop
57
+ fn spawn_blocking < F > ( f : F ) -> Self :: JoinHandle
58
+ where
59
+ F : FnOnce ( ) + Send + ' static ;
55
60
}
56
61
57
62
/// Extension trait for async/await runtimes that support spawning local tasks
@@ -161,6 +166,10 @@ where
161
166
/// # {
162
167
/// # unreachable!()
163
168
/// # }
169
+ /// #
170
+ /// # fn spawn_blocking<F>(f: F) -> Self::JoinHandle where F: FnOnce() + Send + 'static {
171
+ /// # unreachable!()
172
+ /// # }
164
173
/// # }
165
174
/// #
166
175
/// # impl ContextExt for MyCustomRuntime {
@@ -265,6 +274,10 @@ where
265
274
/// # {
266
275
/// # unreachable!()
267
276
/// # }
277
+ /// #
278
+ /// # fn spawn_blocking<F>(f: F) -> Self::JoinHandle where F: FnOnce() + Send + 'static {
279
+ /// # unreachable!()
280
+ /// # }
268
281
/// # }
269
282
/// #
270
283
/// # impl ContextExt for MyCustomRuntime {
@@ -415,6 +428,10 @@ fn set_result(
415
428
/// # {
416
429
/// # unreachable!()
417
430
/// # }
431
+ /// #
432
+ /// # fn spawn_blocking<F>(f: F) -> Self::JoinHandle where F: FnOnce() + Send + 'static {
433
+ /// # unreachable!()
434
+ /// # }
418
435
/// # }
419
436
/// #
420
437
/// # impl ContextExt for MyCustomRuntime {
@@ -540,6 +557,10 @@ where
540
557
/// # {
541
558
/// # unreachable!()
542
559
/// # }
560
+ /// #
561
+ /// # fn spawn_blocking<F>(f: F) -> Self::JoinHandle where F: FnOnce() + Send + 'static {
562
+ /// # unreachable!()
563
+ /// # }
543
564
/// # }
544
565
/// #
545
566
/// # impl ContextExt for MyCustomRuntime {
@@ -581,7 +602,7 @@ pub fn future_into_py_with_locals<R, F, T>(
581
602
where
582
603
R : Runtime + ContextExt ,
583
604
F : Future < Output = PyResult < T > > + Send + ' static ,
584
- T : for < ' py > IntoPyObject < ' py > ,
605
+ T : for < ' py > IntoPyObject < ' py > + Send + ' static ,
585
606
{
586
607
let ( cancel_tx, cancel_rx) = oneshot:: channel ( ) ;
587
608
@@ -606,44 +627,50 @@ where
606
627
)
607
628
. await ;
608
629
609
- Python :: attach ( move |py| {
610
- if cancelled ( future_tx1. bind ( py) )
611
- . map_err ( dump_err ( py) )
612
- . unwrap_or ( false )
613
- {
614
- return ;
615
- }
616
-
617
- let _ = set_result (
618
- & locals2. event_loop ( py) ,
619
- future_tx1. bind ( py) ,
620
- result. and_then ( |val| val. into_py_any ( py) ) ,
621
- )
622
- . map_err ( dump_err ( py) ) ;
623
- } ) ;
624
- } )
625
- . await
626
- {
627
- if e. is_panic ( ) {
630
+ // We should not hold GIL inside async-std/tokio event loop,
631
+ // because a blocked task may prevent other tasks from progressing.
632
+ R :: spawn_blocking ( || {
628
633
Python :: attach ( move |py| {
629
- if cancelled ( future_tx2 . bind ( py) )
634
+ if cancelled ( future_tx1 . bind ( py) )
630
635
. map_err ( dump_err ( py) )
631
636
. unwrap_or ( false )
632
637
{
633
638
return ;
634
639
}
635
640
636
- let panic_message = format ! (
637
- "rust future panicked: {}" ,
638
- get_panic_message( & e. into_panic( ) )
639
- ) ;
640
641
let _ = set_result (
641
- locals . 0 . event_loop . bind ( py) ,
642
- future_tx2 . bind ( py) ,
643
- Err ( RustPanic :: new_err ( panic_message ) ) ,
642
+ & locals2 . event_loop ( py) ,
643
+ future_tx1 . bind ( py) ,
644
+ result . and_then ( |val| val . into_py_any ( py ) ) ,
644
645
)
645
646
. map_err ( dump_err ( py) ) ;
646
647
} ) ;
648
+ } ) ;
649
+ } )
650
+ . await
651
+ {
652
+ if e. is_panic ( ) {
653
+ R :: spawn_blocking ( || {
654
+ Python :: attach ( move |py| {
655
+ if cancelled ( future_tx2. bind ( py) )
656
+ . map_err ( dump_err ( py) )
657
+ . unwrap_or ( false )
658
+ {
659
+ return ;
660
+ }
661
+
662
+ let panic_message = format ! (
663
+ "rust future panicked: {}" ,
664
+ get_panic_message( & e. into_panic( ) )
665
+ ) ;
666
+ let _ = set_result (
667
+ locals. 0 . event_loop . bind ( py) ,
668
+ future_tx2. bind ( py) ,
669
+ Err ( RustPanic :: new_err ( panic_message) ) ,
670
+ )
671
+ . map_err ( dump_err ( py) ) ;
672
+ } ) ;
673
+ } ) ;
647
674
}
648
675
}
649
676
} ) ;
@@ -812,6 +839,10 @@ impl PyDoneCallback {
812
839
/// # {
813
840
/// # unreachable!()
814
841
/// # }
842
+ /// #
843
+ /// # fn spawn_blocking<F>(f: F) -> Self::JoinHandle where F: FnOnce() + Send + 'static {
844
+ /// # unreachable!()
845
+ /// # }
815
846
/// # }
816
847
/// #
817
848
/// # impl ContextExt for MyCustomRuntime {
@@ -844,7 +875,7 @@ pub fn future_into_py<R, F, T>(py: Python, fut: F) -> PyResult<Bound<PyAny>>
844
875
where
845
876
R : Runtime + ContextExt ,
846
877
F : Future < Output = PyResult < T > > + Send + ' static ,
847
- T : for < ' py > IntoPyObject < ' py > ,
878
+ T : for < ' py > IntoPyObject < ' py > + Send + ' static ,
848
879
{
849
880
future_into_py_with_locals :: < R , F , T > ( py, get_current_locals :: < R > ( py) ?, fut)
850
881
}
@@ -921,6 +952,10 @@ where
921
952
/// # {
922
953
/// # unreachable!()
923
954
/// # }
955
+ /// #
956
+ /// # fn spawn_blocking<F>(f: F) -> Self::JoinHandle where F: FnOnce() + Send + 'static {
957
+ /// # unreachable!()
958
+ /// # }
924
959
/// # }
925
960
/// #
926
961
/// # impl ContextExt for MyCustomRuntime {
@@ -1126,6 +1161,10 @@ where
1126
1161
/// # {
1127
1162
/// # unreachable!()
1128
1163
/// # }
1164
+ /// #
1165
+ /// # fn spawn_blocking<F>(f: F) -> Self::JoinHandle where F: FnOnce() + Send + 'static {
1166
+ /// # unreachable!()
1167
+ /// # }
1129
1168
/// # }
1130
1169
/// #
1131
1170
/// # impl ContextExt for MyCustomRuntime {
@@ -1240,6 +1279,10 @@ where
1240
1279
/// # {
1241
1280
/// # unreachable!()
1242
1281
/// # }
1282
+ /// #
1283
+ /// # fn spawn_blocking<F>(f: F) -> Self::JoinHandle where F: FnOnce() + Send + 'static {
1284
+ /// # unreachable!()
1285
+ /// # }
1243
1286
/// # }
1244
1287
/// #
1245
1288
/// # impl ContextExt for MyCustomRuntime {
@@ -1389,6 +1432,10 @@ where
1389
1432
/// # {
1390
1433
/// # unreachable!()
1391
1434
/// # }
1435
+ /// #
1436
+ /// # fn spawn_blocking<F>(f: F) -> Self::JoinHandle where F: FnOnce() + Send + 'static {
1437
+ /// # unreachable!()
1438
+ /// # }
1392
1439
/// # }
1393
1440
/// #
1394
1441
/// # impl ContextExt for MyCustomRuntime {
@@ -1584,6 +1631,10 @@ async def forward(gen, sender):
1584
1631
/// # {
1585
1632
/// # unreachable!()
1586
1633
/// # }
1634
+ /// #
1635
+ /// # fn spawn_blocking<F>(f: F) -> Self::JoinHandle where F: FnOnce() + Send + 'static {
1636
+ /// # unreachable!()
1637
+ /// # }
1587
1638
/// # }
1588
1639
/// #
1589
1640
/// # impl ContextExt for MyCustomRuntime {
@@ -1737,6 +1788,10 @@ where
1737
1788
/// # {
1738
1789
/// # unreachable!()
1739
1790
/// # }
1791
+ /// #
1792
+ /// # fn spawn_blocking<F>(f: F) -> Self::JoinHandle where F: FnOnce() + Send + 'static {
1793
+ /// # unreachable!()
1794
+ /// # }
1740
1795
/// # }
1741
1796
/// #
1742
1797
/// # impl ContextExt for MyCustomRuntime {
0 commit comments