4141//! [^2] `MTLockRef` is a typedef.
4242
4343pub use crate :: marker:: * ;
44+ use std:: any:: Any ;
4445use std:: collections:: HashMap ;
4546use std:: hash:: { BuildHasher , Hash } ;
4647use std:: ops:: { Deref , DerefMut } ;
@@ -103,6 +104,37 @@ mod mode {
103104
104105pub use mode:: { is_dyn_thread_safe, set_dyn_thread_safe_mode} ;
105106
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+
106138cfg_if ! {
107139 if #[ cfg( not( parallel_compiler) ) ] {
108140 use std:: ops:: Add ;
@@ -198,66 +230,37 @@ cfg_if! {
198230 where A : FnOnce ( ) -> RA ,
199231 B : FnOnce ( ) -> RB
200232 {
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( ) )
202238 }
203239
204240 #[ macro_export]
205241 macro_rules! parallel {
206242 ( $( $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( ) ;
222246 } }
223247 }
224248
225249 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( ) ;
229251 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) ) ;
235253 } ) ;
236- if let Some ( panic) = panic {
237- resume_unwind( panic) ;
238- }
254+ guard. unwind( ) ;
239255 }
240256
241257 pub fn par_map<T : IntoIterator , R , C : FromIterator <R >>(
242258 t: T ,
243259 mut map: impl FnMut ( <<T as IntoIterator >:: IntoIter as Iterator >:: Item ) -> R ,
244260 ) -> 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( ) ;
261264 r
262265 }
263266
@@ -380,7 +383,11 @@ cfg_if! {
380383 let ( a, b) = rayon:: join( move || FromDyn :: from( oper_a. into_inner( ) ( ) ) , move || FromDyn :: from( oper_b. into_inner( ) ( ) ) ) ;
381384 ( a. into_inner( ) , b. into_inner( ) )
382385 } 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( ) )
384391 }
385392 }
386393
@@ -415,28 +422,10 @@ cfg_if! {
415422 // of a single threaded rustc.
416423 parallel!( impl $fblock [ ] [ $( $blocks) , * ] ) ;
417424 } 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( ) ;
440429 }
441430 } ;
442431 }
@@ -449,31 +438,17 @@ cfg_if! {
449438 ) {
450439 if mode:: is_dyn_thread_safe( ) {
451440 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) ) ;
458444 } ) ;
459-
460- if let Some ( panic) = panic. into_inner( ) {
461- resume_unwind( panic) ;
462- }
445+ guard. unwind( ) ;
463446 } 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( ) ;
467448 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) ) ;
473450 } ) ;
474- if let Some ( panic) = panic {
475- resume_unwind( panic) ;
476- }
451+ guard. unwind( ) ;
477452 }
478453 }
479454
@@ -487,43 +462,15 @@ cfg_if! {
487462 map: impl Fn ( I ) -> R + DynSync + DynSend
488463 ) -> C {
489464 if mode:: is_dyn_thread_safe( ) {
490- let panic: Mutex <Option <_>> = Mutex :: new( None ) ;
491465 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( ) ;
509469 r
510470 } 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( ) ;
527474 r
528475 }
529476 }
0 commit comments