Skip to content

Commit

Permalink
Auto merge of rust-lang#136772 - safinaskar:parallel-2025-02-08-11-55…
Browse files Browse the repository at this point in the history
…, r=<try>

[perf run] parallel: rustc_data_structures: sync: implement RwLock as enum { Sync(parking_lot::RwLock), NoSync(RefCell) }

Please, somebody, run perf.

parallel: rustc_data_structures: sync: implement `RwLock` as `enum { Sync(parking_lot::RwLock), NoSync(RefCell) }`

Hopefully this will make single threaded front end faster.

I carefully split changes into commits. Commit messages are self-explanatory. Squashing is not recommended.

cc "Parallel Rustc Front-end" rust-lang#113349

r? SparrowLii

`@rustbot` label: +WG-compiler-parallel
  • Loading branch information
bors committed Feb 9, 2025
2 parents 1ff2135 + 95ce6d0 commit d71a46a
Show file tree
Hide file tree
Showing 3 changed files with 279 additions and 94 deletions.
98 changes: 5 additions & 93 deletions compiler/rustc_data_structures/src/sync.rs
Original file line number Diff line number Diff line change
Expand Up @@ -111,11 +111,7 @@ pub use std::sync::atomic::{AtomicBool, AtomicU32, AtomicUsize};
pub use std::sync::{OnceLock, Weak};

pub use mode::{is_dyn_thread_safe, set_dyn_thread_safe_mode};
pub use parking_lot::{
MappedMutexGuard as MappedLockGuard, MappedRwLockReadGuard as MappedReadGuard,
MappedRwLockWriteGuard as MappedWriteGuard, RwLockReadGuard as ReadGuard,
RwLockWriteGuard as WriteGuard,
};
pub use parking_lot::MappedMutexGuard as MappedLockGuard;
#[cfg(not(target_has_atomic = "64"))]
pub use portable_atomic::AtomicU64;

Expand Down Expand Up @@ -151,12 +147,6 @@ impl<T> MTLock<T> {
}
}

use parking_lot::RwLock as InnerRwLock;

/// This makes locks panic if they are already held.
/// It is only useful when you are running in a single thread
const ERROR_CHECKING: bool = false;

pub type MTLockRef<'a, T> = LRef<'a, MTLock<T>>;

#[derive(Default)]
Expand All @@ -175,85 +165,7 @@ impl<K: Eq + Hash, V: Eq, S: BuildHasher> HashMapExt<K, V> for HashMap<K, V, S>
}
}

#[derive(Debug, Default)]
pub struct RwLock<T>(InnerRwLock<T>);

impl<T> RwLock<T> {
#[inline(always)]
pub fn new(inner: T) -> Self {
RwLock(InnerRwLock::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()
}

#[inline(always)]
pub fn read(&self) -> ReadGuard<'_, T> {
if ERROR_CHECKING {
self.0.try_read().expect("lock was already held")
} else {
self.0.read()
}
}

#[inline(always)]
#[track_caller]
pub fn with_read_lock<F: FnOnce(&T) -> R, R>(&self, f: F) -> R {
f(&*self.read())
}

#[inline(always)]
pub fn try_write(&self) -> Result<WriteGuard<'_, T>, ()> {
self.0.try_write().ok_or(())
}

#[inline(always)]
pub fn write(&self) -> WriteGuard<'_, T> {
if ERROR_CHECKING {
self.0.try_write().expect("lock was already held")
} else {
self.0.write()
}
}

#[inline(always)]
#[track_caller]
pub fn with_write_lock<F: FnOnce(&mut T) -> R, R>(&self, f: F) -> R {
f(&mut *self.write())
}

#[inline(always)]
#[track_caller]
pub fn borrow(&self) -> ReadGuard<'_, T> {
self.read()
}

#[inline(always)]
#[track_caller]
pub fn borrow_mut(&self) -> WriteGuard<'_, T> {
self.write()
}

#[inline(always)]
pub fn leak(&self) -> &T {
let guard = self.read();
let ret = unsafe { &*(&raw const *guard) };
std::mem::forget(guard);
ret
}
}

// FIXME: Probably a bad idea
impl<T: Clone> Clone for RwLock<T> {
#[inline]
fn clone(&self) -> Self {
RwLock::new(self.borrow().clone())
}
}
mod rwlock;
pub use rwlock::{
MappedReadGuard, MappedWriteGuard, ReadError, ReadGuard, RwLock, WriteError, WriteGuard,
};
271 changes: 271 additions & 0 deletions compiler/rustc_data_structures/src/sync/rwlock.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,271 @@
use std::cell::{Ref, RefCell, RefMut};
use std::intrinsics::unlikely;
use std::ops::{Deref, DerefMut};

use crate::sync::mode::might_be_dyn_thread_safe;

#[derive(Debug)]
pub enum RwLock<T> {
Sync(parking_lot::RwLock<T>),
NoSync(RefCell<T>),
}

#[clippy::has_significant_drop]
#[must_use = "if unused the RwLock will immediately unlock"]
#[derive(Debug)]
pub enum ReadGuard<'a, T> {
Sync(parking_lot::RwLockReadGuard<'a, T>),
NoSync(Ref<'a, T>),
}

#[clippy::has_significant_drop]
#[must_use = "if unused the RwLock will immediately unlock"]
#[derive(Debug)]
pub enum WriteGuard<'a, T> {
Sync(parking_lot::RwLockWriteGuard<'a, T>),
NoSync(RefMut<'a, T>),
}

#[clippy::has_significant_drop]
#[must_use = "if unused the RwLock will immediately unlock"]
#[derive(Debug)]
pub enum MappedReadGuard<'a, T> {
Sync(parking_lot::MappedRwLockReadGuard<'a, T>),
NoSync(Ref<'a, T>),
}

#[clippy::has_significant_drop]
#[must_use = "if unused the RwLock will immediately unlock"]
#[derive(Debug)]
pub enum MappedWriteGuard<'a, T> {
Sync(parking_lot::MappedRwLockWriteGuard<'a, T>),
NoSync(RefMut<'a, T>),
}

#[derive(Debug)]
pub struct ReadError;

#[derive(Debug)]
pub struct WriteError;

impl<T> RwLock<T> {
#[inline(always)]
pub fn new(inner: T) -> Self {
if unlikely(might_be_dyn_thread_safe()) {
RwLock::Sync(parking_lot::RwLock::new(inner))
} else {
RwLock::NoSync(RefCell::new(inner))
}
}

#[inline(always)]
pub fn into_inner(self) -> T {
match self {
RwLock::Sync(inner) => parking_lot::RwLock::into_inner(inner),
RwLock::NoSync(inner) => RefCell::into_inner(inner),
}
}

#[inline(always)]
pub fn get_mut(&mut self) -> &mut T {
match self {
RwLock::Sync(inner) => parking_lot::RwLock::get_mut(inner),
RwLock::NoSync(inner) => RefCell::get_mut(inner),
}
}

#[inline(always)]
#[track_caller]
pub fn read(&self) -> ReadGuard<'_, T> {
match self {
RwLock::Sync(inner) => ReadGuard::Sync(inner.read()),
RwLock::NoSync(inner) => ReadGuard::NoSync(inner.borrow()),
}
}

#[inline(always)]
pub fn try_read(&self) -> Result<ReadGuard<'_, T>, ReadError> {
match self {
RwLock::Sync(inner) => Ok(ReadGuard::Sync(inner.try_read().ok_or(ReadError)?)),
RwLock::NoSync(inner) => {
Ok(ReadGuard::NoSync(inner.try_borrow().map_err(|_| ReadError)?))
}
}
}

#[inline(always)]
#[track_caller]
pub fn write(&self) -> WriteGuard<'_, T> {
match self {
RwLock::Sync(inner) => WriteGuard::Sync(inner.write()),
RwLock::NoSync(inner) => WriteGuard::NoSync(inner.borrow_mut()),
}
}

#[inline(always)]
pub fn try_write(&self) -> Result<WriteGuard<'_, T>, WriteError> {
match self {
RwLock::Sync(inner) => Ok(WriteGuard::Sync(inner.try_write().ok_or(WriteError)?)),
RwLock::NoSync(inner) => {
Ok(WriteGuard::NoSync(inner.try_borrow_mut().map_err(|_| WriteError)?))
}
}
}

#[inline(always)]
#[track_caller]
pub fn borrow(&self) -> ReadGuard<'_, T> {
self.read()
}

#[inline(always)]
pub fn try_borrow(&self) -> Result<ReadGuard<'_, T>, ReadError> {
self.try_read()
}

#[inline(always)]
#[track_caller]
pub fn borrow_mut(&self) -> WriteGuard<'_, T> {
self.write()
}

#[inline(always)]
pub fn try_borrow_mut(&self) -> Result<WriteGuard<'_, T>, WriteError> {
self.try_write()
}
}

impl<T: Default> Default for RwLock<T> {
#[inline(always)]
fn default() -> Self {
RwLock::<T>::new(Default::default())
}
}

impl<'a, T> ReadGuard<'a, T> {
#[inline(always)]
pub fn map<U, F>(s: Self, f: F) -> MappedReadGuard<'a, U>
where
F: FnOnce(&T) -> &U,
{
match s {
ReadGuard::Sync(guard) => {
MappedReadGuard::Sync(parking_lot::RwLockReadGuard::map(guard, f))
}
ReadGuard::NoSync(guard) => MappedReadGuard::NoSync(Ref::map(guard, f)),
}
}
}

impl<'a, T> WriteGuard<'a, T> {
#[inline(always)]
pub fn map<U, F>(s: Self, f: F) -> MappedWriteGuard<'a, U>
where
F: FnOnce(&mut T) -> &mut U,
{
match s {
WriteGuard::Sync(guard) => {
MappedWriteGuard::Sync(parking_lot::RwLockWriteGuard::map(guard, f))
}
WriteGuard::NoSync(guard) => MappedWriteGuard::NoSync(RefMut::map(guard, f)),
}
}
}

impl<'a, T> Deref for ReadGuard<'a, T> {
type Target = T;

#[inline(always)]
fn deref(&self) -> &T {
match self {
ReadGuard::Sync(guard) => Deref::deref(guard),
ReadGuard::NoSync(guard) => Deref::deref(guard),
}
}
}

impl<'a, T> Deref for WriteGuard<'a, T> {
type Target = T;

#[inline(always)]
fn deref(&self) -> &T {
match self {
WriteGuard::Sync(guard) => Deref::deref(guard),
WriteGuard::NoSync(guard) => Deref::deref(guard),
}
}
}

impl<'a, T> DerefMut for WriteGuard<'a, T> {
#[inline(always)]
fn deref_mut(&mut self) -> &mut T {
match self {
WriteGuard::Sync(guard) => DerefMut::deref_mut(guard),
WriteGuard::NoSync(guard) => DerefMut::deref_mut(guard),
}
}
}

impl<'a, T> MappedReadGuard<'a, T> {
#[inline(always)]
pub fn map<U, F>(s: Self, f: F) -> MappedReadGuard<'a, U>
where
F: FnOnce(&T) -> &U,
{
match s {
MappedReadGuard::Sync(guard) => {
MappedReadGuard::Sync(parking_lot::MappedRwLockReadGuard::map(guard, f))
}
MappedReadGuard::NoSync(guard) => MappedReadGuard::NoSync(Ref::map(guard, f)),
}
}
}

impl<'a, T> MappedWriteGuard<'a, T> {
#[inline(always)]
pub fn map<U, F>(s: Self, f: F) -> MappedWriteGuard<'a, U>
where
F: FnOnce(&mut T) -> &mut U,
{
match s {
MappedWriteGuard::Sync(guard) => {
MappedWriteGuard::Sync(parking_lot::MappedRwLockWriteGuard::map(guard, f))
}
MappedWriteGuard::NoSync(guard) => MappedWriteGuard::NoSync(RefMut::map(guard, f)),
}
}
}

impl<'a, T> Deref for MappedReadGuard<'a, T> {
type Target = T;

#[inline(always)]
fn deref(&self) -> &T {
match self {
MappedReadGuard::Sync(guard) => Deref::deref(guard),
MappedReadGuard::NoSync(guard) => Deref::deref(guard),
}
}
}

impl<'a, T> Deref for MappedWriteGuard<'a, T> {
type Target = T;

#[inline(always)]
fn deref(&self) -> &T {
match self {
MappedWriteGuard::Sync(guard) => Deref::deref(guard),
MappedWriteGuard::NoSync(guard) => Deref::deref(guard),
}
}
}

impl<'a, T> DerefMut for MappedWriteGuard<'a, T> {
#[inline(always)]
fn deref_mut(&mut self) -> &mut T {
match self {
MappedWriteGuard::Sync(guard) => DerefMut::deref_mut(guard),
MappedWriteGuard::NoSync(guard) => DerefMut::deref_mut(guard),
}
}
}
Loading

0 comments on commit d71a46a

Please sign in to comment.