41
41
//! [^2] `MTLockRef` is a typedef.
42
42
43
43
pub use crate :: marker:: * ;
44
+ use std:: any:: Any ;
44
45
use std:: collections:: HashMap ;
45
46
use std:: hash:: { BuildHasher , Hash } ;
46
47
use std:: ops:: { Deref , DerefMut } ;
@@ -103,6 +104,37 @@ mod mode {
103
104
104
105
pub use mode:: { is_dyn_thread_safe, set_dyn_thread_safe_mode} ;
105
106
107
+ /// A guard used to hold panics that occur during a parallel section to later by unwound.
108
+ /// This is used for the parallel compiler to prevent fatal errors from non-deterministically
109
+ /// hiding errors by ensuring that everything in the section has completed executing before
110
+ /// continuing with unwinding. It's also used for the non-parallel code to ensure error message
111
+ /// output match the parallel compiler for testing purposes.
112
+ pub struct ParallelGuard {
113
+ panic : Lock < Option < Box < dyn Any + std:: marker:: Send + ' static > > > ,
114
+ }
115
+
116
+ impl ParallelGuard {
117
+ #[ inline]
118
+ pub fn new ( ) -> Self {
119
+ ParallelGuard { panic : Lock :: new ( None ) }
120
+ }
121
+
122
+ pub fn run < R > ( & self , f : impl FnOnce ( ) -> R ) -> Option < R > {
123
+ catch_unwind ( AssertUnwindSafe ( f) )
124
+ . map_err ( |err| {
125
+ * self . panic . lock ( ) = Some ( err) ;
126
+ } )
127
+ . ok ( )
128
+ }
129
+
130
+ #[ inline]
131
+ pub fn unwind ( self ) {
132
+ if let Some ( panic) = self . panic . into_inner ( ) {
133
+ resume_unwind ( panic) ;
134
+ }
135
+ }
136
+ }
137
+
106
138
cfg_if ! {
107
139
if #[ cfg( not( parallel_compiler) ) ] {
108
140
use std:: ops:: Add ;
@@ -198,66 +230,37 @@ cfg_if! {
198
230
where A : FnOnce ( ) -> RA ,
199
231
B : FnOnce ( ) -> RB
200
232
{
201
- ( oper_a( ) , oper_b( ) )
233
+ let guard = ParallelGuard :: new( ) ;
234
+ let a = guard. run( oper_a) ;
235
+ let b = guard. run( oper_b) ;
236
+ guard. unwind( ) ;
237
+ ( a. unwrap( ) , b. unwrap( ) )
202
238
}
203
239
204
240
#[ macro_export]
205
241
macro_rules! parallel {
206
242
( $( $blocks: block) , * ) => { {
207
- // We catch panics here ensuring that all the blocks execute.
208
- // This makes behavior consistent with the parallel compiler.
209
- let mut panic = None ;
210
- $(
211
- if let Err ( p) = :: std:: panic:: catch_unwind(
212
- :: std:: panic:: AssertUnwindSafe ( || $blocks)
213
- ) {
214
- if panic. is_none( ) {
215
- panic = Some ( p) ;
216
- }
217
- }
218
- ) *
219
- if let Some ( panic) = panic {
220
- :: std:: panic:: resume_unwind( panic) ;
221
- }
243
+ let mut guard = $crate :: sync:: ParallelGuard :: new( ) ;
244
+ $( guard. run( || $blocks) ; ) *
245
+ guard. unwind( ) ;
222
246
} }
223
247
}
224
248
225
249
pub fn par_for_each_in<T : IntoIterator >( t: T , mut for_each: impl FnMut ( T :: Item ) + Sync + Send ) {
226
- // We catch panics here ensuring that all the loop iterations execute.
227
- // This makes behavior consistent with the parallel compiler.
228
- let mut panic = None ;
250
+ let guard = ParallelGuard :: new( ) ;
229
251
t. into_iter( ) . for_each( |i| {
230
- if let Err ( p) = catch_unwind( AssertUnwindSafe ( || for_each( i) ) ) {
231
- if panic. is_none( ) {
232
- panic = Some ( p) ;
233
- }
234
- }
252
+ guard. run( || for_each( i) ) ;
235
253
} ) ;
236
- if let Some ( panic) = panic {
237
- resume_unwind( panic) ;
238
- }
254
+ guard. unwind( ) ;
239
255
}
240
256
241
257
pub fn par_map<T : IntoIterator , R , C : FromIterator <R >>(
242
258
t: T ,
243
259
mut map: impl FnMut ( <<T as IntoIterator >:: IntoIter as Iterator >:: Item ) -> R ,
244
260
) -> C {
245
- // We catch panics here ensuring that all the loop iterations execute.
246
- let mut panic = None ;
247
- let r = t. into_iter( ) . filter_map( |i| {
248
- match catch_unwind( AssertUnwindSafe ( || map( i) ) ) {
249
- Ok ( r) => Some ( r) ,
250
- Err ( p) => {
251
- if panic. is_none( ) {
252
- panic = Some ( p) ;
253
- }
254
- None
255
- }
256
- }
257
- } ) . collect( ) ;
258
- if let Some ( panic) = panic {
259
- resume_unwind( panic) ;
260
- }
261
+ let guard = ParallelGuard :: new( ) ;
262
+ let r = t. into_iter( ) . filter_map( |i| guard. run( || map( i) ) ) . collect( ) ;
263
+ guard. unwind( ) ;
261
264
r
262
265
}
263
266
@@ -380,7 +383,11 @@ cfg_if! {
380
383
let ( a, b) = rayon:: join( move || FromDyn :: from( oper_a. into_inner( ) ( ) ) , move || FromDyn :: from( oper_b. into_inner( ) ( ) ) ) ;
381
384
( a. into_inner( ) , b. into_inner( ) )
382
385
} else {
383
- ( oper_a( ) , oper_b( ) )
386
+ let guard = ParallelGuard :: new( ) ;
387
+ let a = guard. run( oper_a) ;
388
+ let b = guard. run( oper_b) ;
389
+ guard. unwind( ) ;
390
+ ( a. unwrap( ) , b. unwrap( ) )
384
391
}
385
392
}
386
393
@@ -415,28 +422,10 @@ cfg_if! {
415
422
// of a single threaded rustc.
416
423
parallel!( impl $fblock [ ] [ $( $blocks) , * ] ) ;
417
424
} else {
418
- // We catch panics here ensuring that all the blocks execute.
419
- // This makes behavior consistent with the parallel compiler.
420
- let mut panic = None ;
421
- if let Err ( p) = :: std:: panic:: catch_unwind(
422
- :: std:: panic:: AssertUnwindSafe ( || $fblock)
423
- ) {
424
- if panic. is_none( ) {
425
- panic = Some ( p) ;
426
- }
427
- }
428
- $(
429
- if let Err ( p) = :: std:: panic:: catch_unwind(
430
- :: std:: panic:: AssertUnwindSafe ( || $blocks)
431
- ) {
432
- if panic. is_none( ) {
433
- panic = Some ( p) ;
434
- }
435
- }
436
- ) *
437
- if let Some ( panic) = panic {
438
- :: std:: panic:: resume_unwind( panic) ;
439
- }
425
+ let guard = $crate :: sync:: ParallelGuard :: new( ) ;
426
+ guard. run( || $fblock) ;
427
+ $( guard. run( || $blocks) ; ) *
428
+ guard. unwind( ) ;
440
429
}
441
430
} ;
442
431
}
@@ -449,31 +438,17 @@ cfg_if! {
449
438
) {
450
439
if mode:: is_dyn_thread_safe( ) {
451
440
let for_each = FromDyn :: from( for_each) ;
452
- let panic: Mutex <Option <_>> = Mutex :: new( None ) ;
453
- t. into_par_iter( ) . for_each( |i| if let Err ( p) = catch_unwind( AssertUnwindSafe ( || for_each( i) ) ) {
454
- let mut l = panic. lock( ) ;
455
- if l. is_none( ) {
456
- * l = Some ( p)
457
- }
441
+ let guard = ParallelGuard :: new( ) ;
442
+ t. into_par_iter( ) . for_each( |i| {
443
+ guard. run( || for_each( i) ) ;
458
444
} ) ;
459
-
460
- if let Some ( panic) = panic. into_inner( ) {
461
- resume_unwind( panic) ;
462
- }
445
+ guard. unwind( ) ;
463
446
} else {
464
- // We catch panics here ensuring that all the loop iterations execute.
465
- // This makes behavior consistent with the parallel compiler.
466
- let mut panic = None ;
447
+ let guard = ParallelGuard :: new( ) ;
467
448
t. into_iter( ) . for_each( |i| {
468
- if let Err ( p) = catch_unwind( AssertUnwindSafe ( || for_each( i) ) ) {
469
- if panic. is_none( ) {
470
- panic = Some ( p) ;
471
- }
472
- }
449
+ guard. run( || for_each( i) ) ;
473
450
} ) ;
474
- if let Some ( panic) = panic {
475
- resume_unwind( panic) ;
476
- }
451
+ guard. unwind( ) ;
477
452
}
478
453
}
479
454
@@ -487,43 +462,15 @@ cfg_if! {
487
462
map: impl Fn ( I ) -> R + DynSync + DynSend
488
463
) -> C {
489
464
if mode:: is_dyn_thread_safe( ) {
490
- let panic: Mutex <Option <_>> = Mutex :: new( None ) ;
491
465
let map = FromDyn :: from( map) ;
492
- // We catch panics here ensuring that all the loop iterations execute.
493
- let r = t. into_par_iter( ) . filter_map( |i| {
494
- match catch_unwind( AssertUnwindSafe ( || map( i) ) ) {
495
- Ok ( r) => Some ( r) ,
496
- Err ( p) => {
497
- let mut l = panic. lock( ) ;
498
- if l. is_none( ) {
499
- * l = Some ( p) ;
500
- }
501
- None
502
- } ,
503
- }
504
- } ) . collect( ) ;
505
-
506
- if let Some ( panic) = panic. into_inner( ) {
507
- resume_unwind( panic) ;
508
- }
466
+ let guard = ParallelGuard :: new( ) ;
467
+ let r = t. into_par_iter( ) . filter_map( |i| guard. run( || map( i) ) ) . collect( ) ;
468
+ guard. unwind( ) ;
509
469
r
510
470
} else {
511
- // We catch panics here ensuring that all the loop iterations execute.
512
- let mut panic = None ;
513
- let r = t. into_iter( ) . filter_map( |i| {
514
- match catch_unwind( AssertUnwindSafe ( || map( i) ) ) {
515
- Ok ( r) => Some ( r) ,
516
- Err ( p) => {
517
- if panic. is_none( ) {
518
- panic = Some ( p) ;
519
- }
520
- None
521
- }
522
- }
523
- } ) . collect( ) ;
524
- if let Some ( panic) = panic {
525
- resume_unwind( panic) ;
526
- }
471
+ let guard = ParallelGuard :: new( ) ;
472
+ let r = t. into_iter( ) . filter_map( |i| guard. run( || map( i) ) ) . collect( ) ;
473
+ guard. unwind( ) ;
527
474
r
528
475
}
529
476
}
0 commit comments