Skip to content

Commit b56acac

Browse files
committed
Add ParallelGuard type to handle unwinding in parallel sections
1 parent 51eb842 commit b56acac

File tree

1 file changed

+68
-121
lines changed
  • compiler/rustc_data_structures/src

1 file changed

+68
-121
lines changed

compiler/rustc_data_structures/src/sync.rs

Lines changed: 68 additions & 121 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@
4141
//! [^2] `MTLockRef` is a typedef.
4242
4343
pub use crate::marker::*;
44+
use std::any::Any;
4445
use std::collections::HashMap;
4546
use std::hash::{BuildHasher, Hash};
4647
use std::ops::{Deref, DerefMut};
@@ -103,6 +104,37 @@ mod mode {
103104

104105
pub 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+
106138
cfg_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

Comments
 (0)