Auto merge of #100581 - joboet:sync_rwlock_everywhere, r=thomcc

std: use `sync::RwLock` for internal statics

Since `sync::RwLock` is now `const`-constructible, it can be used for internal statics, removing the need for `sys_common::StaticRwLock`. This adds some extra allocations on platforms which need to box their locks (currently SGX and some UNIX), but these will become unnecessary with the lock improvements tracked in #93740.
This commit is contained in:
bors 2022-09-20 22:00:08 +00:00
commit 7743aa836e
8 changed files with 62 additions and 151 deletions

View File

@ -18,9 +18,9 @@ use crate::intrinsics;
use crate::mem::{self, ManuallyDrop}; use crate::mem::{self, ManuallyDrop};
use crate::process; use crate::process;
use crate::sync::atomic::{AtomicBool, Ordering}; use crate::sync::atomic::{AtomicBool, Ordering};
use crate::sync::{PoisonError, RwLock};
use crate::sys::stdio::panic_output; use crate::sys::stdio::panic_output;
use crate::sys_common::backtrace; use crate::sys_common::backtrace;
use crate::sys_common::rwlock::StaticRwLock;
use crate::sys_common::thread_info; use crate::sys_common::thread_info;
use crate::thread; use crate::thread;
@ -71,20 +71,29 @@ extern "C" fn __rust_foreign_exception() -> ! {
rtabort!("Rust cannot catch foreign exceptions"); rtabort!("Rust cannot catch foreign exceptions");
} }
#[derive(Copy, Clone)]
enum Hook { enum Hook {
Default, Default,
Custom(*mut (dyn Fn(&PanicInfo<'_>) + 'static + Sync + Send)), Custom(Box<dyn Fn(&PanicInfo<'_>) + 'static + Sync + Send>),
} }
impl Hook { impl Hook {
fn custom(f: impl Fn(&PanicInfo<'_>) + 'static + Sync + Send) -> Self { #[inline]
Self::Custom(Box::into_raw(Box::new(f))) fn into_box(self) -> Box<dyn Fn(&PanicInfo<'_>) + 'static + Sync + Send> {
match self {
Hook::Default => Box::new(default_hook),
Hook::Custom(hook) => hook,
}
} }
} }
static HOOK_LOCK: StaticRwLock = StaticRwLock::new(); impl Default for Hook {
static mut HOOK: Hook = Hook::Default; #[inline]
fn default() -> Hook {
Hook::Default
}
}
static HOOK: RwLock<Hook> = RwLock::new(Hook::Default);
/// Registers a custom panic hook, replacing any that was previously registered. /// Registers a custom panic hook, replacing any that was previously registered.
/// ///
@ -125,24 +134,13 @@ pub fn set_hook(hook: Box<dyn Fn(&PanicInfo<'_>) + 'static + Sync + Send>) {
panic!("cannot modify the panic hook from a panicking thread"); panic!("cannot modify the panic hook from a panicking thread");
} }
// SAFETY: let new = Hook::Custom(hook);
// let mut hook = HOOK.write().unwrap_or_else(PoisonError::into_inner);
// - `HOOK` can only be modified while holding write access to `HOOK_LOCK`. let old = mem::replace(&mut *hook, new);
// - The argument of `Box::from_raw` is always a valid pointer that was created using drop(hook);
// `Box::into_raw`. // Only drop the old hook after releasing the lock to avoid deadlocking
unsafe { // if its destructor panics.
let guard = HOOK_LOCK.write(); drop(old);
let old_hook = HOOK;
HOOK = Hook::Custom(Box::into_raw(hook));
drop(guard);
if let Hook::Custom(ptr) = old_hook {
#[allow(unused_must_use)]
{
Box::from_raw(ptr);
}
}
}
} }
/// Unregisters the current panic hook, returning it. /// Unregisters the current panic hook, returning it.
@ -179,22 +177,11 @@ pub fn take_hook() -> Box<dyn Fn(&PanicInfo<'_>) + 'static + Sync + Send> {
panic!("cannot modify the panic hook from a panicking thread"); panic!("cannot modify the panic hook from a panicking thread");
} }
// SAFETY: let mut hook = HOOK.write().unwrap_or_else(PoisonError::into_inner);
// let old_hook = mem::take(&mut *hook);
// - `HOOK` can only be modified while holding write access to `HOOK_LOCK`. drop(hook);
// - The argument of `Box::from_raw` is always a valid pointer that was created using
// `Box::into_raw`.
unsafe {
let guard = HOOK_LOCK.write();
let hook = HOOK;
HOOK = Hook::Default;
drop(guard);
match hook { old_hook.into_box()
Hook::Default => Box::new(default_hook),
Hook::Custom(ptr) => Box::from_raw(ptr),
}
}
} }
/// Atomic combination of [`take_hook`] and [`set_hook`]. Use this to replace the panic handler with /// Atomic combination of [`take_hook`] and [`set_hook`]. Use this to replace the panic handler with
@ -240,24 +227,9 @@ where
panic!("cannot modify the panic hook from a panicking thread"); panic!("cannot modify the panic hook from a panicking thread");
} }
// SAFETY: let mut hook = HOOK.write().unwrap_or_else(PoisonError::into_inner);
// let prev = mem::take(&mut *hook).into_box();
// - `HOOK` can only be modified while holding write access to `HOOK_LOCK`. *hook = Hook::Custom(Box::new(move |info| hook_fn(&prev, info)));
// - The argument of `Box::from_raw` is always a valid pointer that was created using
// `Box::into_raw`.
unsafe {
let guard = HOOK_LOCK.write();
let old_hook = HOOK;
HOOK = Hook::Default;
let prev = match old_hook {
Hook::Default => Box::new(default_hook),
Hook::Custom(ptr) => Box::from_raw(ptr),
};
HOOK = Hook::custom(move |info| hook_fn(&prev, info));
drop(guard);
}
} }
fn default_hook(info: &PanicInfo<'_>) { fn default_hook(info: &PanicInfo<'_>) {
@ -682,27 +654,26 @@ fn rust_panic_with_hook(
crate::sys::abort_internal(); crate::sys::abort_internal();
} }
unsafe { let mut info = PanicInfo::internal_constructor(message, location, can_unwind);
let mut info = PanicInfo::internal_constructor(message, location, can_unwind); let hook = HOOK.read().unwrap_or_else(PoisonError::into_inner);
let _guard = HOOK_LOCK.read(); match *hook {
match HOOK { // Some platforms (like wasm) know that printing to stderr won't ever actually
// Some platforms (like wasm) know that printing to stderr won't ever actually // print anything, and if that's the case we can skip the default
// print anything, and if that's the case we can skip the default // hook. Since string formatting happens lazily when calling `payload`
// hook. Since string formatting happens lazily when calling `payload` // methods, this means we avoid formatting the string at all!
// methods, this means we avoid formatting the string at all! // (The panic runtime might still call `payload.take_box()` though and trigger
// (The panic runtime might still call `payload.take_box()` though and trigger // formatting.)
// formatting.) Hook::Default if panic_output().is_none() => {}
Hook::Default if panic_output().is_none() => {} Hook::Default => {
Hook::Default => { info.set_payload(payload.get());
info.set_payload(payload.get()); default_hook(&info);
default_hook(&info); }
} Hook::Custom(ref hook) => {
Hook::Custom(ptr) => { info.set_payload(payload.get());
info.set_payload(payload.get()); hook(&info);
(*ptr)(&info); }
} };
}; drop(hook);
}
if panics > 1 || !can_unwind { if panics > 1 || !can_unwind {
// If a thread panics while it's already unwinding then we // If a thread panics while it's already unwinding then we

View File

@ -8,7 +8,7 @@ use crate::os::{
solid::ffi::{OsStrExt, OsStringExt}, solid::ffi::{OsStrExt, OsStringExt},
}; };
use crate::path::{self, PathBuf}; use crate::path::{self, PathBuf};
use crate::sys_common::rwlock::StaticRwLock; use crate::sync::RwLock;
use crate::vec; use crate::vec;
use super::{error, itron, memchr}; use super::{error, itron, memchr};
@ -78,7 +78,7 @@ pub fn current_exe() -> io::Result<PathBuf> {
unsupported() unsupported()
} }
static ENV_LOCK: StaticRwLock = StaticRwLock::new(); static ENV_LOCK: RwLock<()> = RwLock::new(());
pub struct Env { pub struct Env {
iter: vec::IntoIter<(OsString, OsString)>, iter: vec::IntoIter<(OsString, OsString)>,

View File

@ -11,21 +11,21 @@ cfg_if::cfg_if! {
mod futex_rwlock; mod futex_rwlock;
mod futex_condvar; mod futex_condvar;
pub(crate) use futex_mutex::{Mutex, MovableMutex}; pub(crate) use futex_mutex::{Mutex, MovableMutex};
pub(crate) use futex_rwlock::{RwLock, MovableRwLock}; pub(crate) use futex_rwlock::MovableRwLock;
pub(crate) use futex_condvar::MovableCondvar; pub(crate) use futex_condvar::MovableCondvar;
} else if #[cfg(target_os = "fuchsia")] { } else if #[cfg(target_os = "fuchsia")] {
mod fuchsia_mutex; mod fuchsia_mutex;
mod futex_rwlock; mod futex_rwlock;
mod futex_condvar; mod futex_condvar;
pub(crate) use fuchsia_mutex::{Mutex, MovableMutex}; pub(crate) use fuchsia_mutex::{Mutex, MovableMutex};
pub(crate) use futex_rwlock::{RwLock, MovableRwLock}; pub(crate) use futex_rwlock::MovableRwLock;
pub(crate) use futex_condvar::MovableCondvar; pub(crate) use futex_condvar::MovableCondvar;
} else { } else {
mod pthread_mutex; mod pthread_mutex;
mod pthread_rwlock; mod pthread_rwlock;
mod pthread_condvar; mod pthread_condvar;
pub(crate) use pthread_mutex::{Mutex, MovableMutex}; pub(crate) use pthread_mutex::{Mutex, MovableMutex};
pub(crate) use pthread_rwlock::{RwLock, MovableRwLock}; pub(crate) use pthread_rwlock::MovableRwLock;
pub(crate) use pthread_condvar::MovableCondvar; pub(crate) use pthread_condvar::MovableCondvar;
} }
} }

View File

@ -17,10 +17,10 @@ use crate::path::{self, PathBuf};
use crate::ptr; use crate::ptr;
use crate::slice; use crate::slice;
use crate::str; use crate::str;
use crate::sync::{PoisonError, RwLock};
use crate::sys::cvt; use crate::sys::cvt;
use crate::sys::fd; use crate::sys::fd;
use crate::sys::memchr; use crate::sys::memchr;
use crate::sys_common::rwlock::{StaticRwLock, StaticRwLockReadGuard};
use crate::vec; use crate::vec;
#[cfg(all(target_env = "gnu", not(target_os = "vxworks")))] #[cfg(all(target_env = "gnu", not(target_os = "vxworks")))]
@ -501,10 +501,10 @@ pub unsafe fn environ() -> *mut *const *const c_char {
ptr::addr_of_mut!(environ) ptr::addr_of_mut!(environ)
} }
static ENV_LOCK: StaticRwLock = StaticRwLock::new(); static ENV_LOCK: RwLock<()> = RwLock::new(());
pub fn env_read_lock() -> StaticRwLockReadGuard { pub fn env_read_lock() -> impl Drop {
ENV_LOCK.read() ENV_LOCK.read().unwrap_or_else(PoisonError::into_inner)
} }
/// Returns a vector of (variable, value) byte-vector pairs for all the /// Returns a vector of (variable, value) byte-vector pairs for all the

View File

@ -3,4 +3,4 @@ mod mutex;
mod rwlock; mod rwlock;
pub use condvar::{Condvar, MovableCondvar}; pub use condvar::{Condvar, MovableCondvar};
pub use mutex::{MovableMutex, Mutex}; pub use mutex::{MovableMutex, Mutex};
pub use rwlock::{MovableRwLock, RwLock}; pub use rwlock::MovableRwLock;

View File

@ -57,7 +57,7 @@ cfg_if::cfg_if! {
mod futex_rwlock; mod futex_rwlock;
pub(crate) use futex_condvar::{Condvar, MovableCondvar}; pub(crate) use futex_condvar::{Condvar, MovableCondvar};
pub(crate) use futex_mutex::{Mutex, MovableMutex}; pub(crate) use futex_mutex::{Mutex, MovableMutex};
pub(crate) use futex_rwlock::{RwLock, MovableRwLock}; pub(crate) use futex_rwlock::MovableRwLock;
} }
#[path = "atomics/futex.rs"] #[path = "atomics/futex.rs"]
pub mod futex; pub mod futex;

View File

@ -3,4 +3,4 @@ mod mutex;
mod rwlock; mod rwlock;
pub use condvar::{Condvar, MovableCondvar}; pub use condvar::{Condvar, MovableCondvar};
pub use mutex::{MovableMutex, Mutex}; pub use mutex::{MovableMutex, Mutex};
pub use rwlock::{MovableRwLock, RwLock}; pub use rwlock::MovableRwLock;

View File

@ -1,65 +1,5 @@
use crate::sys::locks as imp; use crate::sys::locks as imp;
/// An OS-based reader-writer lock, meant for use in static variables.
///
/// This rwlock does not implement poisoning.
///
/// This rwlock has a const constructor ([`StaticRwLock::new`]), does not
/// implement `Drop` to cleanup resources.
pub struct StaticRwLock(imp::RwLock);
impl StaticRwLock {
/// Creates a new rwlock for use.
#[inline]
pub const fn new() -> Self {
Self(imp::RwLock::new())
}
/// Acquires shared access to the underlying lock, blocking the current
/// thread to do so.
///
/// The lock is automatically unlocked when the returned guard is dropped.
#[inline]
pub fn read(&'static self) -> StaticRwLockReadGuard {
unsafe { self.0.read() };
StaticRwLockReadGuard(&self.0)
}
/// Acquires write access to the underlying lock, blocking the current thread
/// to do so.
///
/// The lock is automatically unlocked when the returned guard is dropped.
#[inline]
pub fn write(&'static self) -> StaticRwLockWriteGuard {
unsafe { self.0.write() };
StaticRwLockWriteGuard(&self.0)
}
}
#[must_use]
pub struct StaticRwLockReadGuard(&'static imp::RwLock);
impl Drop for StaticRwLockReadGuard {
#[inline]
fn drop(&mut self) {
unsafe {
self.0.read_unlock();
}
}
}
#[must_use]
pub struct StaticRwLockWriteGuard(&'static imp::RwLock);
impl Drop for StaticRwLockWriteGuard {
#[inline]
fn drop(&mut self) {
unsafe {
self.0.write_unlock();
}
}
}
/// An OS-based reader-writer lock. /// An OS-based reader-writer lock.
/// ///
/// This rwlock cleans up its resources in its `Drop` implementation and may /// This rwlock cleans up its resources in its `Drop` implementation and may