Rollup merge of #84687 - a1phyr:improve_rwlock, r=m-ou-se

Multiple improvements to RwLocks

This PR replicates #77147, #77380 and #84650 on RWLocks :
- Split `sys_common::RWLock` in `StaticRWLock` and `MovableRWLock`
- Unbox rwlocks on some platforms (Windows, Wasm and unsupported)
- Simplify `RwLock::into_inner`

Notes to reviewers :
- For each target, I copied `MovableMutex` to guess if `MovableRWLock` should be boxed.
- ~A comment says that `StaticMutex` is not re-entrant, I don't understand why and I don't know whether it applies to `StaticRWLock`.~

r? `@m-ou-se`
This commit is contained in:
Yuki Okushi 2021-06-10 11:02:10 +09:00 committed by GitHub
commit 578eb6d65f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 112 additions and 134 deletions

View File

@ -19,7 +19,7 @@
use crate::sync::atomic::{AtomicBool, Ordering};
use crate::sys::stdio::panic_output;
use crate::sys_common::backtrace::{self, RustBacktrace};
use crate::sys_common::rwlock::RWLock;
use crate::sys_common::rwlock::StaticRWLock;
use crate::sys_common::thread_info;
use crate::thread;
@ -74,7 +74,7 @@ enum Hook {
Custom(*mut (dyn Fn(&PanicInfo<'_>) + 'static + Sync + Send)),
}
static HOOK_LOCK: RWLock = RWLock::new();
static HOOK_LOCK: StaticRWLock = StaticRWLock::new();
static mut HOOK: Hook = Hook::Default;
/// Registers a custom panic hook, replacing any that was previously registered.
@ -117,10 +117,10 @@ pub fn set_hook(hook: Box<dyn Fn(&PanicInfo<'_>) + 'static + Sync + Send>) {
}
unsafe {
HOOK_LOCK.write();
let guard = HOOK_LOCK.write();
let old_hook = HOOK;
HOOK = Hook::Custom(Box::into_raw(hook));
HOOK_LOCK.write_unlock();
drop(guard);
if let Hook::Custom(ptr) = old_hook {
#[allow(unused_must_use)]
@ -165,10 +165,10 @@ pub fn take_hook() -> Box<dyn Fn(&PanicInfo<'_>) + 'static + Sync + Send> {
}
unsafe {
HOOK_LOCK.write();
let guard = HOOK_LOCK.write();
let hook = HOOK;
HOOK = Hook::Default;
HOOK_LOCK.write_unlock();
drop(guard);
match hook {
Hook::Default => Box::new(default_hook),
@ -608,7 +608,7 @@ fn rust_panic_with_hook(
unsafe {
let mut info = PanicInfo::internal_constructor(message, location);
HOOK_LOCK.read();
let _guard = HOOK_LOCK.read();
match HOOK {
// 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
@ -626,7 +626,6 @@ fn rust_panic_with_hook(
(*ptr)(&info);
}
};
HOOK_LOCK.read_unlock();
}
if panics > 1 {

View File

@ -3,9 +3,7 @@
use crate::cell::UnsafeCell;
use crate::fmt;
use crate::mem;
use crate::ops::{Deref, DerefMut};
use crate::ptr;
use crate::sync::{poison, LockResult, TryLockError, TryLockResult};
use crate::sys_common::rwlock as sys;
@ -66,7 +64,7 @@
/// [`Mutex`]: super::Mutex
#[stable(feature = "rust1", since = "1.0.0")]
pub struct RwLock<T: ?Sized> {
inner: Box<sys::RWLock>,
inner: sys::MovableRWLock,
poison: poison::Flag,
data: UnsafeCell<T>,
}
@ -130,7 +128,7 @@ impl<T> RwLock<T> {
#[stable(feature = "rust1", since = "1.0.0")]
pub fn new(t: T) -> RwLock<T> {
RwLock {
inner: box sys::RWLock::new(),
inner: sys::MovableRWLock::new(),
poison: poison::Flag::new(),
data: UnsafeCell::new(t),
}
@ -376,24 +374,8 @@ pub fn into_inner(self) -> LockResult<T>
where
T: Sized,
{
// We know statically that there are no outstanding references to
// `self` so there's no need to lock the inner lock.
//
// To get the inner value, we'd like to call `data.into_inner()`,
// but because `RwLock` impl-s `Drop`, we can't move out of it, so
// we'll have to destructure it manually instead.
unsafe {
// Like `let RwLock { inner, poison, data } = self`.
let (inner, poison, data) = {
let RwLock { ref inner, ref poison, ref data } = self;
(ptr::read(inner), ptr::read(poison), ptr::read(data))
};
mem::forget(self);
inner.destroy(); // Keep in sync with the `Drop` impl.
drop(inner);
poison::map_result(poison.borrow(), |_| data.into_inner())
}
let data = self.data.into_inner();
poison::map_result(self.poison.borrow(), |_| data)
}
/// Returns a mutable reference to the underlying data.
@ -424,14 +406,6 @@ pub fn get_mut(&mut self) -> LockResult<&mut T> {
}
}
#[stable(feature = "rust1", since = "1.0.0")]
unsafe impl<#[may_dangle] T: ?Sized> Drop for RwLock<T> {
fn drop(&mut self) {
// IMPORTANT: This code needs to be kept in sync with `RwLock::into_inner`.
unsafe { self.inner.destroy() }
}
}
#[stable(feature = "rust1", since = "1.0.0")]
impl<T: ?Sized + fmt::Debug> fmt::Debug for RwLock<T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {

View File

@ -8,6 +8,8 @@ pub struct RWLock {
state: UnsafeCell<State>,
}
pub type MovableRWLock = Box<RWLock>;
enum State {
Unlocked,
Reading(usize),

View File

@ -13,6 +13,8 @@ pub struct RWLock {
writer: SpinMutex<WaitVariable<bool>>,
}
pub type MovableRWLock = Box<RWLock>;
// Check at compile time that RWLock size matches C definition (see test_c_rwlock_initializer below)
//
// # Safety

View File

@ -20,8 +20,7 @@
use crate::sys::cvt;
use crate::sys::fd;
use crate::sys::memchr;
use crate::sys::rwlock::{RWLockReadGuard, StaticRWLock};
use crate::sys_common::mutex::{StaticMutex, StaticMutexGuard};
use crate::sys_common::rwlock::{StaticRWLock, StaticRWLockReadGuard};
use crate::vec;
use libc::{c_char, c_int, c_void};
@ -490,8 +489,8 @@ pub unsafe fn environ() -> *mut *const *const c_char {
static ENV_LOCK: StaticRWLock = StaticRWLock::new();
pub fn env_read_lock() -> RWLockReadGuard {
ENV_LOCK.read_with_guard()
pub fn env_read_lock() -> StaticRWLockReadGuard {
ENV_LOCK.read()
}
/// Returns a vector of (variable, value) byte-vector pairs for all the
@ -551,7 +550,7 @@ pub fn setenv(k: &OsStr, v: &OsStr) -> io::Result<()> {
let v = CString::new(v.as_bytes())?;
unsafe {
let _guard = ENV_LOCK.write_with_guard();
let _guard = ENV_LOCK.write();
cvt(libc::setenv(k.as_ptr(), v.as_ptr(), 1)).map(drop)
}
}
@ -560,7 +559,7 @@ pub fn unsetenv(n: &OsStr) -> io::Result<()> {
let nbuf = CString::new(n.as_bytes())?;
unsafe {
let _guard = ENV_LOCK.write_with_guard();
let _guard = ENV_LOCK.write();
cvt(libc::unsetenv(nbuf.as_ptr())).map(drop)
}
}

View File

@ -7,6 +7,8 @@ pub struct RWLock {
num_readers: AtomicUsize,
}
pub type MovableRWLock = Box<RWLock>;
unsafe impl Send for RWLock {}
unsafe impl Sync for RWLock {}
@ -139,55 +141,3 @@ pub unsafe fn destroy(&self) {
}
}
}
pub struct StaticRWLock(RWLock);
impl StaticRWLock {
pub const fn new() -> StaticRWLock {
StaticRWLock(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_with_guard(&'static self) -> RWLockReadGuard {
// SAFETY: All methods require static references, therefore self
// cannot be moved between invocations.
unsafe {
self.0.read();
}
RWLockReadGuard(&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_with_guard(&'static self) -> RWLockWriteGuard {
// SAFETY: All methods require static references, therefore self
// cannot be moved between invocations.
unsafe {
self.0.write();
}
RWLockWriteGuard(&self.0)
}
}
pub struct RWLockReadGuard(&'static RWLock);
impl Drop for RWLockReadGuard {
fn drop(&mut self) {
unsafe { self.0.read_unlock() }
}
}
pub struct RWLockWriteGuard(&'static RWLock);
impl Drop for RWLockWriteGuard {
fn drop(&mut self) {
unsafe { self.0.write_unlock() }
}
}

View File

@ -5,6 +5,8 @@ pub struct RWLock {
mode: Cell<isize>,
}
pub type MovableRWLock = RWLock;
unsafe impl Send for RWLock {}
unsafe impl Sync for RWLock {} // no threads on this platform

View File

@ -8,6 +8,8 @@ pub struct RWLock {
state: UnsafeCell<State>,
}
pub type MovableRWLock = RWLock;
enum State {
Unlocked,
Reading(usize),

View File

@ -5,6 +5,8 @@ pub struct RWLock {
inner: UnsafeCell<c::SRWLOCK>,
}
pub type MovableRWLock = RWLock;
unsafe impl Send for RWLock {}
unsafe impl Sync for RWLock {}

View File

@ -1,63 +1,112 @@
use crate::sys::rwlock as imp;
/// An OS-based reader-writer lock.
/// An OS-based reader-writer lock, meant for use in static variables.
///
/// This structure is entirely unsafe and serves as the lowest layer of a
/// cross-platform binding of system rwlocks. It is recommended to use the
/// safer types at the top level of this crate instead of this type.
pub struct RWLock(imp::RWLock);
/// 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 RWLock {
/// Creates a new reader-writer lock for use.
///
/// Behavior is undefined if the reader-writer lock is moved after it is
/// first used with any of the functions below.
pub const fn new() -> RWLock {
RWLock(imp::RWLock::new())
impl StaticRWLock {
/// Creates a new rwlock for use.
pub const fn new() -> Self {
Self(imp::RWLock::new())
}
/// Acquires shared access to the underlying lock, blocking the current
/// thread to do so.
///
/// Behavior is undefined if the rwlock has been moved between this and any
/// previous method call.
/// The lock is automatically unlocked when the returned guard is dropped.
#[inline]
pub unsafe fn read(&self) {
self.0.read()
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.
///
/// This rwlock does *not* have a const constructor, cleans up its resources in
/// its `Drop` implementation and may safely be moved (when not borrowed).
///
/// This rwlock does not implement poisoning.
///
/// This is either a wrapper around `Box<imp::RWLock>` or `imp::RWLock`,
/// depending on the platform. It is boxed on platforms where `imp::RWLock` may
/// not be moved.
pub struct MovableRWLock(imp::MovableRWLock);
impl MovableRWLock {
/// Creates a new reader-writer lock for use.
pub fn new() -> Self {
Self(imp::MovableRWLock::from(imp::RWLock::new()))
}
/// Acquires shared access to the underlying lock, blocking the current
/// thread to do so.
#[inline]
pub fn read(&self) {
unsafe { self.0.read() }
}
/// Attempts to acquire shared access to this lock, returning whether it
/// succeeded or not.
///
/// This function does not block the current thread.
///
/// Behavior is undefined if the rwlock has been moved between this and any
/// previous method call.
#[inline]
pub unsafe fn try_read(&self) -> bool {
self.0.try_read()
pub fn try_read(&self) -> bool {
unsafe { self.0.try_read() }
}
/// Acquires write access to the underlying lock, blocking the current thread
/// to do so.
///
/// Behavior is undefined if the rwlock has been moved between this and any
/// previous method call.
#[inline]
pub unsafe fn write(&self) {
self.0.write()
pub fn write(&self) {
unsafe { self.0.write() }
}
/// Attempts to acquire exclusive access to this lock, returning whether it
/// succeeded or not.
///
/// This function does not block the current thread.
///
/// Behavior is undefined if the rwlock has been moved between this and any
/// previous method call.
#[inline]
pub unsafe fn try_write(&self) -> bool {
self.0.try_write()
pub fn try_write(&self) -> bool {
unsafe { self.0.try_write() }
}
/// Unlocks previously acquired shared access to this lock.
@ -76,13 +125,10 @@ pub unsafe fn read_unlock(&self) {
pub unsafe fn write_unlock(&self) {
self.0.write_unlock()
}
}
/// Destroys OS-related resources with this RWLock.
///
/// Behavior is undefined if there are any currently active users of this
/// lock.
#[inline]
pub unsafe fn destroy(&self) {
self.0.destroy()
impl Drop for MovableRWLock {
fn drop(&mut self) {
unsafe { self.0.destroy() };
}
}