From 5739349e96e2a34dc9dd18e58589b1b3afcaa271 Mon Sep 17 00:00:00 2001 From: John Kåre Alsaker Date: Sat, 31 Oct 2020 03:14:32 +0100 Subject: Use conditional synchronization for Lock --- compiler/rustc_data_structures/src/sync.rs | 105 ++++++----------------------- 1 file changed, 19 insertions(+), 86 deletions(-) (limited to 'compiler/rustc_data_structures/src/sync.rs') diff --git a/compiler/rustc_data_structures/src/sync.rs b/compiler/rustc_data_structures/src/sync.rs index 25a08237346..083aa6cf697 100644 --- a/compiler/rustc_data_structures/src/sync.rs +++ b/compiler/rustc_data_structures/src/sync.rs @@ -26,7 +26,8 @@ //! | `AtomicU64` | `Cell` | `atomic::AtomicU64` | //! | `AtomicUsize` | `Cell` | `atomic::AtomicUsize` | //! | | | | -//! | `Lock` | `RefCell` | `parking_lot::Mutex` | +//! | `Lock` | `RefCell` | `RefCell` or | +//! | | | `parking_lot::Mutex` | //! | `RwLock` | `RefCell` | `parking_lot::RwLock` | //! | `MTLock` [^1] | `T` | `Lock` | //! | `MTLockRef<'a, T>` [^2] | `&'a mut MTLock` | `&'a MTLock` | @@ -45,6 +46,9 @@ use std::hash::{BuildHasher, Hash}; use std::ops::{Deref, DerefMut}; use std::panic::{catch_unwind, resume_unwind, AssertUnwindSafe}; +mod lock; +pub use lock::{Lock, LockGuard}; + mod worker_local; pub use worker_local::{Registry, WorkerLocal}; @@ -75,6 +79,13 @@ mod mode { } } + // Whether thread safety might be enabled. + #[inline] + #[cfg(parallel_compiler)] + pub fn might_be_dyn_thread_safe() -> bool { + DYN_THREAD_SAFE_MODE.load(Ordering::Relaxed) != DYN_NOT_THREAD_SAFE + } + // Only set by the `-Z threads` compile option pub fn set_dyn_thread_safe_mode(mode: bool) { let set: u8 = if mode { DYN_THREAD_SAFE } else { DYN_NOT_THREAD_SAFE }; @@ -94,14 +105,15 @@ pub use mode::{is_dyn_thread_safe, set_dyn_thread_safe_mode}; cfg_if! { if #[cfg(not(parallel_compiler))] { + use std::ops::Add; + use std::cell::Cell; + pub unsafe auto trait Send {} pub unsafe auto trait Sync {} unsafe impl Send for T {} unsafe impl Sync for T {} - use std::ops::Add; - /// This is a single threaded variant of `AtomicU64`, `AtomicUsize`, etc. /// It has explicit ordering arguments and is only intended for use with /// the native atomic types. @@ -255,15 +267,11 @@ cfg_if! { pub use std::cell::Ref as MappedReadGuard; pub use std::cell::RefMut as WriteGuard; pub use std::cell::RefMut as MappedWriteGuard; - pub use std::cell::RefMut as LockGuard; pub use std::cell::RefMut as MappedLockGuard; pub use std::cell::OnceCell; use std::cell::RefCell as InnerRwLock; - use std::cell::RefCell as InnerLock; - - use std::cell::Cell; pub type MTLockRef<'a, T> = &'a mut MTLock; @@ -305,6 +313,8 @@ cfg_if! { } } } else { + use parking_lot::Mutex; + pub use std::marker::Send as Send; pub use std::marker::Sync as Sync; @@ -313,7 +323,6 @@ cfg_if! { pub use parking_lot::RwLockWriteGuard as WriteGuard; pub use parking_lot::MappedRwLockWriteGuard as MappedWriteGuard; - pub use parking_lot::MutexGuard as LockGuard; pub use parking_lot::MappedMutexGuard as MappedLockGuard; pub use std::sync::OnceLock as OnceCell; @@ -355,7 +364,6 @@ cfg_if! { } } - use parking_lot::Mutex as InnerLock; use parking_lot::RwLock as InnerRwLock; use std::thread; @@ -441,7 +449,7 @@ cfg_if! { ) { if mode::is_dyn_thread_safe() { let for_each = FromDyn::from(for_each); - let panic: Lock> = Lock::new(None); + let panic: Mutex> = Mutex::new(None); t.into_par_iter().for_each(|i| if let Err(p) = catch_unwind(AssertUnwindSafe(|| for_each(i))) { let mut l = panic.lock(); if l.is_none() { @@ -479,7 +487,7 @@ cfg_if! { map: impl Fn(I) -> R + DynSync + DynSend ) -> C { if mode::is_dyn_thread_safe() { - let panic: Lock> = Lock::new(None); + let panic: Mutex> = Mutex::new(None); let map = FromDyn::from(map); // We catch panics here ensuring that all the loop iterations execute. let r = t.into_par_iter().filter_map(|i| { @@ -542,81 +550,6 @@ impl HashMapExt for HashMap } } -#[derive(Debug)] -pub struct Lock(InnerLock); - -impl Lock { - #[inline(always)] - pub fn new(inner: T) -> Self { - Lock(InnerLock::new(inner)) - } - - #[inline(always)] - pub fn into_inner(self) -> T { - self.0.into_inner() - } - - #[inline(always)] - pub fn get_mut(&mut self) -> &mut T { - self.0.get_mut() - } - - #[cfg(parallel_compiler)] - #[inline(always)] - pub fn try_lock(&self) -> Option> { - self.0.try_lock() - } - - #[cfg(not(parallel_compiler))] - #[inline(always)] - pub fn try_lock(&self) -> Option> { - self.0.try_borrow_mut().ok() - } - - #[cfg(parallel_compiler)] - #[inline(always)] - #[track_caller] - pub fn lock(&self) -> LockGuard<'_, T> { - if ERROR_CHECKING { - self.0.try_lock().expect("lock was already held") - } else { - self.0.lock() - } - } - - #[cfg(not(parallel_compiler))] - #[inline(always)] - #[track_caller] - pub fn lock(&self) -> LockGuard<'_, T> { - self.0.borrow_mut() - } - - #[inline(always)] - #[track_caller] - pub fn with_lock R, R>(&self, f: F) -> R { - f(&mut *self.lock()) - } - - #[inline(always)] - #[track_caller] - pub fn borrow(&self) -> LockGuard<'_, T> { - self.lock() - } - - #[inline(always)] - #[track_caller] - pub fn borrow_mut(&self) -> LockGuard<'_, T> { - self.lock() - } -} - -impl Default for Lock { - #[inline] - fn default() -> Self { - Lock::new(T::default()) - } -} - #[derive(Debug, Default)] pub struct RwLock(InnerRwLock); -- cgit 1.4.1-3-g733a5 From 51eb8427bf3887323a77d4dd56bf4557f80fdabc Mon Sep 17 00:00:00 2001 From: John Kåre Alsaker Date: Sat, 19 Aug 2023 17:26:54 +0200 Subject: Make parallel! an expression --- compiler/rustc_data_structures/src/sync.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'compiler/rustc_data_structures/src/sync.rs') diff --git a/compiler/rustc_data_structures/src/sync.rs b/compiler/rustc_data_structures/src/sync.rs index 083aa6cf697..c46ae9a65da 100644 --- a/compiler/rustc_data_structures/src/sync.rs +++ b/compiler/rustc_data_structures/src/sync.rs @@ -203,7 +203,7 @@ cfg_if! { #[macro_export] macro_rules! parallel { - ($($blocks:block),*) => { + ($($blocks:block),*) => {{ // We catch panics here ensuring that all the blocks execute. // This makes behavior consistent with the parallel compiler. let mut panic = None; @@ -219,7 +219,7 @@ cfg_if! { if let Some(panic) = panic { ::std::panic::resume_unwind(panic); } - } + }} } pub fn par_for_each_in(t: T, mut for_each: impl FnMut(T::Item) + Sync + Send) { -- cgit 1.4.1-3-g733a5 From b56acac41d2abbc9afc91fec370b574be49d5c6e Mon Sep 17 00:00:00 2001 From: John Kåre Alsaker Date: Fri, 3 Jan 2020 01:51:30 +0100 Subject: Add `ParallelGuard` type to handle unwinding in parallel sections --- compiler/rustc_data_structures/src/sync.rs | 189 +++++++++++------------------ 1 file changed, 68 insertions(+), 121 deletions(-) (limited to 'compiler/rustc_data_structures/src/sync.rs') diff --git a/compiler/rustc_data_structures/src/sync.rs b/compiler/rustc_data_structures/src/sync.rs index c46ae9a65da..8defcd39ecd 100644 --- a/compiler/rustc_data_structures/src/sync.rs +++ b/compiler/rustc_data_structures/src/sync.rs @@ -41,6 +41,7 @@ //! [^2] `MTLockRef` is a typedef. pub use crate::marker::*; +use std::any::Any; use std::collections::HashMap; use std::hash::{BuildHasher, Hash}; use std::ops::{Deref, DerefMut}; @@ -103,6 +104,37 @@ mod mode { pub use mode::{is_dyn_thread_safe, set_dyn_thread_safe_mode}; +/// A guard used to hold panics that occur during a parallel section to later by unwound. +/// This is used for the parallel compiler to prevent fatal errors from non-deterministically +/// hiding errors by ensuring that everything in the section has completed executing before +/// continuing with unwinding. It's also used for the non-parallel code to ensure error message +/// output match the parallel compiler for testing purposes. +pub struct ParallelGuard { + panic: Lock>>, +} + +impl ParallelGuard { + #[inline] + pub fn new() -> Self { + ParallelGuard { panic: Lock::new(None) } + } + + pub fn run(&self, f: impl FnOnce() -> R) -> Option { + catch_unwind(AssertUnwindSafe(f)) + .map_err(|err| { + *self.panic.lock() = Some(err); + }) + .ok() + } + + #[inline] + pub fn unwind(self) { + if let Some(panic) = self.panic.into_inner() { + resume_unwind(panic); + } + } +} + cfg_if! { if #[cfg(not(parallel_compiler))] { use std::ops::Add; @@ -198,66 +230,37 @@ cfg_if! { where A: FnOnce() -> RA, B: FnOnce() -> RB { - (oper_a(), oper_b()) + let guard = ParallelGuard::new(); + let a = guard.run(oper_a); + let b = guard.run(oper_b); + guard.unwind(); + (a.unwrap(), b.unwrap()) } #[macro_export] macro_rules! parallel { ($($blocks:block),*) => {{ - // We catch panics here ensuring that all the blocks execute. - // This makes behavior consistent with the parallel compiler. - let mut panic = None; - $( - if let Err(p) = ::std::panic::catch_unwind( - ::std::panic::AssertUnwindSafe(|| $blocks) - ) { - if panic.is_none() { - panic = Some(p); - } - } - )* - if let Some(panic) = panic { - ::std::panic::resume_unwind(panic); - } + let mut guard = $crate::sync::ParallelGuard::new(); + $(guard.run(|| $blocks);)* + guard.unwind(); }} } pub fn par_for_each_in(t: T, mut for_each: impl FnMut(T::Item) + Sync + Send) { - // We catch panics here ensuring that all the loop iterations execute. - // This makes behavior consistent with the parallel compiler. - let mut panic = None; + let guard = ParallelGuard::new(); t.into_iter().for_each(|i| { - if let Err(p) = catch_unwind(AssertUnwindSafe(|| for_each(i))) { - if panic.is_none() { - panic = Some(p); - } - } + guard.run(|| for_each(i)); }); - if let Some(panic) = panic { - resume_unwind(panic); - } + guard.unwind(); } pub fn par_map>( t: T, mut map: impl FnMut(<::IntoIter as Iterator>::Item) -> R, ) -> C { - // We catch panics here ensuring that all the loop iterations execute. - let mut panic = None; - let r = t.into_iter().filter_map(|i| { - match catch_unwind(AssertUnwindSafe(|| map(i))) { - Ok(r) => Some(r), - Err(p) => { - if panic.is_none() { - panic = Some(p); - } - None - } - } - }).collect(); - if let Some(panic) = panic { - resume_unwind(panic); - } + let guard = ParallelGuard::new(); + let r = t.into_iter().filter_map(|i| guard.run(|| map(i))).collect(); + guard.unwind(); r } @@ -380,7 +383,11 @@ cfg_if! { let (a, b) = rayon::join(move || FromDyn::from(oper_a.into_inner()()), move || FromDyn::from(oper_b.into_inner()())); (a.into_inner(), b.into_inner()) } else { - (oper_a(), oper_b()) + let guard = ParallelGuard::new(); + let a = guard.run(oper_a); + let b = guard.run(oper_b); + guard.unwind(); + (a.unwrap(), b.unwrap()) } } @@ -415,28 +422,10 @@ cfg_if! { // of a single threaded rustc. parallel!(impl $fblock [] [$($blocks),*]); } else { - // We catch panics here ensuring that all the blocks execute. - // This makes behavior consistent with the parallel compiler. - let mut panic = None; - if let Err(p) = ::std::panic::catch_unwind( - ::std::panic::AssertUnwindSafe(|| $fblock) - ) { - if panic.is_none() { - panic = Some(p); - } - } - $( - if let Err(p) = ::std::panic::catch_unwind( - ::std::panic::AssertUnwindSafe(|| $blocks) - ) { - if panic.is_none() { - panic = Some(p); - } - } - )* - if let Some(panic) = panic { - ::std::panic::resume_unwind(panic); - } + let guard = $crate::sync::ParallelGuard::new(); + guard.run(|| $fblock); + $(guard.run(|| $blocks);)* + guard.unwind(); } }; } @@ -449,31 +438,17 @@ cfg_if! { ) { if mode::is_dyn_thread_safe() { let for_each = FromDyn::from(for_each); - let panic: Mutex> = Mutex::new(None); - t.into_par_iter().for_each(|i| if let Err(p) = catch_unwind(AssertUnwindSafe(|| for_each(i))) { - let mut l = panic.lock(); - if l.is_none() { - *l = Some(p) - } + let guard = ParallelGuard::new(); + t.into_par_iter().for_each(|i| { + guard.run(|| for_each(i)); }); - - if let Some(panic) = panic.into_inner() { - resume_unwind(panic); - } + guard.unwind(); } else { - // We catch panics here ensuring that all the loop iterations execute. - // This makes behavior consistent with the parallel compiler. - let mut panic = None; + let guard = ParallelGuard::new(); t.into_iter().for_each(|i| { - if let Err(p) = catch_unwind(AssertUnwindSafe(|| for_each(i))) { - if panic.is_none() { - panic = Some(p); - } - } + guard.run(|| for_each(i)); }); - if let Some(panic) = panic { - resume_unwind(panic); - } + guard.unwind(); } } @@ -487,43 +462,15 @@ cfg_if! { map: impl Fn(I) -> R + DynSync + DynSend ) -> C { if mode::is_dyn_thread_safe() { - let panic: Mutex> = Mutex::new(None); let map = FromDyn::from(map); - // We catch panics here ensuring that all the loop iterations execute. - let r = t.into_par_iter().filter_map(|i| { - match catch_unwind(AssertUnwindSafe(|| map(i))) { - Ok(r) => Some(r), - Err(p) => { - let mut l = panic.lock(); - if l.is_none() { - *l = Some(p); - } - None - }, - } - }).collect(); - - if let Some(panic) = panic.into_inner() { - resume_unwind(panic); - } + let guard = ParallelGuard::new(); + let r = t.into_par_iter().filter_map(|i| guard.run(|| map(i))).collect(); + guard.unwind(); r } else { - // We catch panics here ensuring that all the loop iterations execute. - let mut panic = None; - let r = t.into_iter().filter_map(|i| { - match catch_unwind(AssertUnwindSafe(|| map(i))) { - Ok(r) => Some(r), - Err(p) => { - if panic.is_none() { - panic = Some(p); - } - None - } - } - }).collect(); - if let Some(panic) = panic { - resume_unwind(panic); - } + let guard = ParallelGuard::new(); + let r = t.into_iter().filter_map(|i| guard.run(|| map(i))).collect(); + guard.unwind(); r } } -- cgit 1.4.1-3-g733a5 From d36393b839719959e1f2ce90d8805e53b131f759 Mon Sep 17 00:00:00 2001 From: John Kåre Alsaker Date: Thu, 24 Aug 2023 02:52:16 +0200 Subject: Use `Mutex` to avoid issue with conditional locks --- compiler/rustc_data_structures/src/sync.rs | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) (limited to 'compiler/rustc_data_structures/src/sync.rs') diff --git a/compiler/rustc_data_structures/src/sync.rs b/compiler/rustc_data_structures/src/sync.rs index 8defcd39ecd..5fe1e8f8c4f 100644 --- a/compiler/rustc_data_structures/src/sync.rs +++ b/compiler/rustc_data_structures/src/sync.rs @@ -41,6 +41,7 @@ //! [^2] `MTLockRef` is a typedef. pub use crate::marker::*; +use parking_lot::Mutex; use std::any::Any; use std::collections::HashMap; use std::hash::{BuildHasher, Hash}; @@ -110,13 +111,13 @@ pub use mode::{is_dyn_thread_safe, set_dyn_thread_safe_mode}; /// continuing with unwinding. It's also used for the non-parallel code to ensure error message /// output match the parallel compiler for testing purposes. pub struct ParallelGuard { - panic: Lock>>, + panic: Mutex>>, } impl ParallelGuard { #[inline] pub fn new() -> Self { - ParallelGuard { panic: Lock::new(None) } + ParallelGuard { panic: Mutex::new(None) } } pub fn run(&self, f: impl FnOnce() -> R) -> Option { @@ -316,8 +317,6 @@ cfg_if! { } } } else { - use parking_lot::Mutex; - pub use std::marker::Send as Send; pub use std::marker::Sync as Sync; -- cgit 1.4.1-3-g733a5 From c303c8abdddaae5508346f3a31c651744d005dfd Mon Sep 17 00:00:00 2001 From: John Kåre Alsaker Date: Fri, 25 Aug 2023 22:16:21 +0200 Subject: Use a `parallel_guard` function to handle the parallel guard --- compiler/rustc_data_structures/src/sync.rs | 115 ++++++++++++++--------------- 1 file changed, 55 insertions(+), 60 deletions(-) (limited to 'compiler/rustc_data_structures/src/sync.rs') diff --git a/compiler/rustc_data_structures/src/sync.rs b/compiler/rustc_data_structures/src/sync.rs index 5fe1e8f8c4f..2ec509b9118 100644 --- a/compiler/rustc_data_structures/src/sync.rs +++ b/compiler/rustc_data_structures/src/sync.rs @@ -115,11 +115,6 @@ pub struct ParallelGuard { } impl ParallelGuard { - #[inline] - pub fn new() -> Self { - ParallelGuard { panic: Mutex::new(None) } - } - pub fn run(&self, f: impl FnOnce() -> R) -> Option { catch_unwind(AssertUnwindSafe(f)) .map_err(|err| { @@ -127,13 +122,18 @@ impl ParallelGuard { }) .ok() } +} - #[inline] - pub fn unwind(self) { - if let Some(panic) = self.panic.into_inner() { - resume_unwind(panic); - } +/// This gives access to a fresh parallel guard in the closure and will unwind any panics +/// caught in it after the closure returns. +#[inline] +pub fn parallel_guard(f: impl FnOnce(&ParallelGuard) -> R) -> R { + let guard = ParallelGuard { panic: Mutex::new(None) }; + let ret = f(&guard); + if let Some(panic) = guard.panic.into_inner() { + resume_unwind(panic); } + ret } cfg_if! { @@ -231,38 +231,38 @@ cfg_if! { where A: FnOnce() -> RA, B: FnOnce() -> RB { - let guard = ParallelGuard::new(); - let a = guard.run(oper_a); - let b = guard.run(oper_b); - guard.unwind(); + let (a, b) = parallel_guard(|guard| { + let a = guard.run(oper_a); + let b = guard.run(oper_b); + (a, b) + }); (a.unwrap(), b.unwrap()) } #[macro_export] macro_rules! parallel { ($($blocks:block),*) => {{ - let mut guard = $crate::sync::ParallelGuard::new(); - $(guard.run(|| $blocks);)* - guard.unwind(); + $crate::sync::parallel_guard(|guard| { + $(guard.run(|| $blocks);)* + }); }} } pub fn par_for_each_in(t: T, mut for_each: impl FnMut(T::Item) + Sync + Send) { - let guard = ParallelGuard::new(); - t.into_iter().for_each(|i| { - guard.run(|| for_each(i)); - }); - guard.unwind(); + parallel_guard(|guard| { + t.into_iter().for_each(|i| { + guard.run(|| for_each(i)); + }); + }) } pub fn par_map>( t: T, mut map: impl FnMut(<::IntoIter as Iterator>::Item) -> R, ) -> C { - let guard = ParallelGuard::new(); - let r = t.into_iter().filter_map(|i| guard.run(|| map(i))).collect(); - guard.unwind(); - r + parallel_guard(|guard| { + t.into_iter().filter_map(|i| guard.run(|| map(i))).collect() + }) } pub use std::rc::Rc as Lrc; @@ -382,10 +382,11 @@ cfg_if! { let (a, b) = rayon::join(move || FromDyn::from(oper_a.into_inner()()), move || FromDyn::from(oper_b.into_inner()())); (a.into_inner(), b.into_inner()) } else { - let guard = ParallelGuard::new(); - let a = guard.run(oper_a); - let b = guard.run(oper_b); - guard.unwind(); + let (a, b) = parallel_guard(|guard| { + let a = guard.run(oper_a); + let b = guard.run(oper_b); + (a, b) + }); (a.unwrap(), b.unwrap()) } } @@ -421,10 +422,10 @@ cfg_if! { // of a single threaded rustc. parallel!(impl $fblock [] [$($blocks),*]); } else { - let guard = $crate::sync::ParallelGuard::new(); - guard.run(|| $fblock); - $(guard.run(|| $blocks);)* - guard.unwind(); + $crate::sync::parallel_guard(|guard| { + guard.run(|| $fblock); + $(guard.run(|| $blocks);)* + }); } }; } @@ -435,20 +436,18 @@ cfg_if! { t: T, for_each: impl Fn(I) + DynSync + DynSend ) { - if mode::is_dyn_thread_safe() { - let for_each = FromDyn::from(for_each); - let guard = ParallelGuard::new(); - t.into_par_iter().for_each(|i| { - guard.run(|| for_each(i)); - }); - guard.unwind(); - } else { - let guard = ParallelGuard::new(); - t.into_iter().for_each(|i| { - guard.run(|| for_each(i)); - }); - guard.unwind(); - } + parallel_guard(|guard| { + if mode::is_dyn_thread_safe() { + let for_each = FromDyn::from(for_each); + t.into_par_iter().for_each(|i| { + guard.run(|| for_each(i)); + }); + } else { + t.into_iter().for_each(|i| { + guard.run(|| for_each(i)); + }); + } + }); } pub fn par_map< @@ -460,18 +459,14 @@ cfg_if! { t: T, map: impl Fn(I) -> R + DynSync + DynSend ) -> C { - if mode::is_dyn_thread_safe() { - let map = FromDyn::from(map); - let guard = ParallelGuard::new(); - let r = t.into_par_iter().filter_map(|i| guard.run(|| map(i))).collect(); - guard.unwind(); - r - } else { - let guard = ParallelGuard::new(); - let r = t.into_iter().filter_map(|i| guard.run(|| map(i))).collect(); - guard.unwind(); - r - } + parallel_guard(|guard| { + if mode::is_dyn_thread_safe() { + let map = FromDyn::from(map); + t.into_par_iter().filter_map(|i| guard.run(|| map(i))).collect() + } else { + t.into_iter().filter_map(|i| guard.run(|| map(i))).collect() + } + }) } /// This makes locks panic if they are already held. -- cgit 1.4.1-3-g733a5