From 0f201989e0bf1beb344d4bf180805da371024d2c Mon Sep 17 00:00:00 2001 From: Bert Peters Date: Sat, 18 Jan 2025 19:57:32 +0100 Subject: [PATCH] Add missing functions to parking_lot API wrappers --- src/parkinglot.rs | 192 +++---------------------------------- src/parkinglot/tracing.rs | 193 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 205 insertions(+), 180 deletions(-) create mode 100644 src/parkinglot/tracing.rs diff --git a/src/parkinglot.rs b/src/parkinglot.rs index 79616d2..eed59b7 100644 --- a/src/parkinglot.rs +++ b/src/parkinglot.rs @@ -41,194 +41,26 @@ pub use parking_lot as raw; +pub use parking_lot::OnceState; +pub use parking_lot::RawThreadId; +pub use parking_lot::WaitTimeoutResult; + +pub mod tracing; + #[cfg(debug_assertions)] pub use tracing::{ - FairMutex, FairMutexGuard, MappedFairMutexGuard, MappedMutexGuard, MappedReentrantMutexGuard, - MappedRwLockReadGuard, MappedRwLockWriteGuard, Mutex, MutexGuard, Once, OnceState, + const_fair_mutex, const_mutex, const_reentrant_mutex, const_rwlock, FairMutex, FairMutexGuard, + MappedFairMutexGuard, MappedMutexGuard, MappedReentrantMutexGuard, MappedRwLockReadGuard, + MappedRwLockWriteGuard, Mutex, MutexGuard, Once, RawFairMutex, RawMutex, RawRwLock, ReentrantMutex, ReentrantMutexGuard, RwLock, RwLockReadGuard, RwLockUpgradableReadGuard, RwLockWriteGuard, }; #[cfg(not(debug_assertions))] pub use parking_lot::{ - FairMutex, FairMutexGuard, MappedFairMutexGuard, MappedMutexGuard, MappedReentrantMutexGuard, - MappedRwLockReadGuard, MappedRwLockWriteGuard, Mutex, MutexGuard, Once, OnceState, + const_fair_mutex, const_mutex, const_reentrant_mutex, const_rwlock, FairMutex, FairMutexGuard, + MappedFairMutexGuard, MappedMutexGuard, MappedReentrantMutexGuard, MappedRwLockReadGuard, + MappedRwLockWriteGuard, Mutex, MutexGuard, Once, RawFairMutex, RawMutex, RawRwLock, ReentrantMutex, ReentrantMutexGuard, RwLock, RwLockReadGuard, RwLockUpgradableReadGuard, RwLockWriteGuard, }; - -/// Dependency tracing wrappers for [`parking_lot`]. -pub mod tracing { - pub use parking_lot::OnceState; - - use crate::lockapi::TracingWrapper; - use crate::LazyMutexId; - - type RawFairMutex = TracingWrapper; - type RawMutex = TracingWrapper; - type RawRwLock = TracingWrapper; - - /// Dependency tracking fair mutex. See: [`parking_lot::FairMutex`]. - pub type FairMutex = lock_api::Mutex; - /// Mutex guard for [`FairMutex`]. - pub type FairMutexGuard<'a, T> = lock_api::MutexGuard<'a, RawFairMutex, T>; - /// RAII guard for [`FairMutexGuard::map`]. - pub type MappedFairMutexGuard<'a, T> = lock_api::MappedMutexGuard<'a, RawFairMutex, T>; - - /// Dependency tracking mutex. See: [`parking_lot::Mutex`]. - pub type Mutex = lock_api::Mutex; - /// Mutex guard for [`Mutex`]. - pub type MutexGuard<'a, T> = lock_api::MutexGuard<'a, RawMutex, T>; - /// RAII guard for [`MutexGuard::map`]. - pub type MappedMutexGuard<'a, T> = lock_api::MappedMutexGuard<'a, RawMutex, T>; - - /// Dependency tracking reentrant mutex. See: [`parking_lot::ReentrantMutex`]. - /// - /// **Note:** due to the way dependencies are tracked, this mutex can only be acquired directly - /// after itself. Acquiring any other mutex in between introduces a dependency cycle, and will - /// therefore be rejected. - pub type ReentrantMutex = lock_api::ReentrantMutex; - /// Mutex guard for [`ReentrantMutex`]. - pub type ReentrantMutexGuard<'a, T> = - lock_api::ReentrantMutexGuard<'a, RawMutex, parking_lot::RawThreadId, T>; - /// RAII guard for `ReentrantMutexGuard::map`. - pub type MappedReentrantMutexGuard<'a, T> = - lock_api::MappedReentrantMutexGuard<'a, RawMutex, parking_lot::RawThreadId, T>; - - /// Dependency tracking RwLock. See: [`parking_lot::RwLock`]. - pub type RwLock = lock_api::RwLock; - /// Read guard for [`RwLock`]. - pub type RwLockReadGuard<'a, T> = lock_api::RwLockReadGuard<'a, RawRwLock, T>; - /// Upgradable Read guard for [`RwLock`]. - pub type RwLockUpgradableReadGuard<'a, T> = - lock_api::RwLockUpgradableReadGuard<'a, RawRwLock, T>; - /// Write guard for [`RwLock`]. - pub type RwLockWriteGuard<'a, T> = lock_api::RwLockWriteGuard<'a, RawRwLock, T>; - /// RAII guard for `RwLockReadGuard::map`. - pub type MappedRwLockReadGuard<'a, T> = lock_api::MappedRwLockReadGuard<'a, RawRwLock, T>; - /// RAII guard for `RwLockWriteGuard::map`. - pub type MappedRwLockWriteGuard<'a, T> = lock_api::MappedRwLockWriteGuard<'a, RawRwLock, T>; - - /// A dependency-tracking wrapper for [`parking_lot::Once`]. - #[derive(Debug, Default)] - pub struct Once { - inner: parking_lot::Once, - id: LazyMutexId, - } - - impl Once { - /// Create a new `Once` value. - pub const fn new() -> Self { - Self { - inner: parking_lot::Once::new(), - id: LazyMutexId::new(), - } - } - - /// Returns the current state of this `Once`. - pub fn state(&self) -> OnceState { - self.inner.state() - } - - /// This call is considered as "locking this `Once`" and it participates in dependency - /// tracking as such. - /// - /// # Panics - /// - /// This method will panic if `f` panics, poisoning this `Once`. In addition, this function - /// panics when the lock acquisition order is determined to be inconsistent. - pub fn call_once(&self, f: impl FnOnce()) { - self.id.with_held(|| self.inner.call_once(f)); - } - - /// Performs the given initialization routine once and only once. - /// - /// This method is identical to [`Once::call_once`] except it ignores poisoning. - pub fn call_once_force(&self, f: impl FnOnce(OnceState)) { - self.id.with_held(|| self.inner.call_once_force(f)); - } - } -} - -#[cfg(test)] -mod tests { - use std::sync::Arc; - use std::thread; - - use super::tracing; - - #[test] - fn test_mutex_usage() { - let mutex = Arc::new(tracing::Mutex::new(())); - let local_lock = mutex.lock(); - drop(local_lock); - - thread::spawn(move || { - let _remote_lock = mutex.lock(); - }) - .join() - .unwrap(); - } - - #[test] - #[should_panic] - fn test_mutex_conflict() { - let mutexes = [ - tracing::Mutex::new(()), - tracing::Mutex::new(()), - tracing::Mutex::new(()), - ]; - - for i in 0..3 { - let _first_lock = mutexes[i].lock(); - let _second_lock = mutexes[(i + 1) % 3].lock(); - } - } - - #[test] - fn test_rwlock_usage() { - let lock = Arc::new(tracing::RwLock::new(())); - let lock2 = Arc::clone(&lock); - - let _read_lock = lock.read(); - - // Should be able to acquire lock in the background - thread::spawn(move || { - let _read_lock = lock2.read(); - }) - .join() - .unwrap(); - } - - #[test] - fn test_rwlock_upgradable_read_usage() { - let lock = tracing::RwLock::new(()); - - // Should be able to acquire an upgradable read lock. - let upgradable_guard: tracing::RwLockUpgradableReadGuard<'_, _> = lock.upgradable_read(); - - // Should be able to upgrade the guard. - let _write_guard: tracing::RwLockWriteGuard<'_, _> = - tracing::RwLockUpgradableReadGuard::upgrade(upgradable_guard); - } - - #[test] - fn test_once_usage() { - let once = Arc::new(tracing::Once::new()); - let once_clone = once.clone(); - - assert!(!once_clone.state().done()); - - let handle = thread::spawn(move || { - assert!(!once_clone.state().done()); - - once_clone.call_once(|| {}); - - assert!(once_clone.state().done()); - }); - - handle.join().unwrap(); - - assert!(once.state().done()); - } -} diff --git a/src/parkinglot/tracing.rs b/src/parkinglot/tracing.rs new file mode 100644 index 0000000..5d65260 --- /dev/null +++ b/src/parkinglot/tracing.rs @@ -0,0 +1,193 @@ +//! Dependency tracing wrappers for [`::parking_lot`]. +pub use parking_lot::OnceState; +pub use parking_lot::RawThreadId; + +use crate::lockapi::TracingWrapper; +use crate::LazyMutexId; + +pub type RawFairMutex = TracingWrapper<::parking_lot::RawFairMutex>; +pub type RawMutex = TracingWrapper<::parking_lot::RawMutex>; +pub type RawRwLock = TracingWrapper<::parking_lot::RawRwLock>; + +/// Dependency tracking fair mutex. See: [`::parking_lot::FairMutex`]. +pub type FairMutex = lock_api::Mutex; +/// Mutex guard for [`FairMutex`]. +pub type FairMutexGuard<'a, T> = lock_api::MutexGuard<'a, RawFairMutex, T>; +/// RAII guard for [`FairMutexGuard::map`]. +pub type MappedFairMutexGuard<'a, T> = lock_api::MappedMutexGuard<'a, RawFairMutex, T>; + +/// Dependency tracking mutex. See: [`::parking_lot::Mutex`]. +pub type Mutex = lock_api::Mutex; +/// Mutex guard for [`Mutex`]. +pub type MutexGuard<'a, T> = lock_api::MutexGuard<'a, RawMutex, T>; +/// RAII guard for [`MutexGuard::map`]. +pub type MappedMutexGuard<'a, T> = lock_api::MappedMutexGuard<'a, RawMutex, T>; + +/// Dependency tracking reentrant mutex. See: [`::parking_lot::ReentrantMutex`]. +/// +/// **Note:** due to the way dependencies are tracked, this mutex can only be acquired directly +/// after itself. Acquiring any other mutex in between introduces a dependency cycle, and will +/// therefore be rejected. +pub type ReentrantMutex = lock_api::ReentrantMutex; +/// Mutex guard for [`ReentrantMutex`]. +pub type ReentrantMutexGuard<'a, T> = + lock_api::ReentrantMutexGuard<'a, RawMutex, parking_lot::RawThreadId, T>; +/// RAII guard for `ReentrantMutexGuard::map`. +pub type MappedReentrantMutexGuard<'a, T> = + lock_api::MappedReentrantMutexGuard<'a, RawMutex, parking_lot::RawThreadId, T>; + +/// Dependency tracking RwLock. See: [`::parking_lot::RwLock`]. +pub type RwLock = lock_api::RwLock; +/// Read guard for [`RwLock`]. +pub type RwLockReadGuard<'a, T> = lock_api::RwLockReadGuard<'a, RawRwLock, T>; +/// Upgradable Read guard for [`RwLock`]. +pub type RwLockUpgradableReadGuard<'a, T> = lock_api::RwLockUpgradableReadGuard<'a, RawRwLock, T>; +/// Write guard for [`RwLock`]. +pub type RwLockWriteGuard<'a, T> = lock_api::RwLockWriteGuard<'a, RawRwLock, T>; +/// RAII guard for `RwLockReadGuard::map`. +pub type MappedRwLockReadGuard<'a, T> = lock_api::MappedRwLockReadGuard<'a, RawRwLock, T>; +/// RAII guard for `RwLockWriteGuard::map`. +pub type MappedRwLockWriteGuard<'a, T> = lock_api::MappedRwLockWriteGuard<'a, RawRwLock, T>; + +/// A dependency-tracking wrapper for [`::parking_lot::Once`]. +#[derive(Debug, Default)] +pub struct Once { + inner: ::parking_lot::Once, + id: LazyMutexId, +} + +impl Once { + /// Create a new `Once` value. + pub const fn new() -> Self { + Self { + inner: ::parking_lot::Once::new(), + id: LazyMutexId::new(), + } + } + + /// Returns the current state of this `Once`. + pub fn state(&self) -> OnceState { + self.inner.state() + } + + /// This call is considered as "locking this `Once`" and it participates in dependency + /// tracking as such. + /// + /// # Panics + /// + /// This method will panic if `f` panics, poisoning this `Once`. In addition, this function + /// panics when the lock acquisition order is determined to be inconsistent. + pub fn call_once(&self, f: impl FnOnce()) { + self.id.with_held(|| self.inner.call_once(f)); + } + + /// Performs the given initialization routine once and only once. + /// + /// This method is identical to [`Once::call_once`] except it ignores poisoning. + pub fn call_once_force(&self, f: impl FnOnce(OnceState)) { + self.id.with_held(|| self.inner.call_once_force(f)); + } +} + +/// Creates a new fair mutex in an unlocked state ready for use. +pub const fn const_fair_mutex(val: T) -> FairMutex { + FairMutex::const_new(::INIT, val) +} + +/// Creates a new mutex in an unlocked state ready for use. +pub const fn const_mutex(val: T) -> Mutex { + Mutex::const_new(::INIT, val) +} + +/// Creates a new reentrant mutex in an unlocked state ready for use. +pub const fn const_reentrant_mutex(val: T) -> ReentrantMutex { + ReentrantMutex::const_new( + ::INIT, + ::INIT, + val, + ) +} + +/// Creates a new rwlock in an unlocked state ready for use. +pub const fn const_rwlock(val: T) -> RwLock { + RwLock::const_new(::INIT, val) +} + +#[cfg(test)] +mod tests { + use std::sync::Arc; + use std::thread; + + use super::*; + + #[test] + fn test_mutex_usage() { + let mutex = Arc::new(Mutex::new(())); + let local_lock = mutex.lock(); + drop(local_lock); + + thread::spawn(move || { + let _remote_lock = mutex.lock(); + }) + .join() + .unwrap(); + } + + #[test] + #[should_panic] + fn test_mutex_conflict() { + let mutexes = [Mutex::new(()), Mutex::new(()), Mutex::new(())]; + + for i in 0..3 { + let _first_lock = mutexes[i].lock(); + let _second_lock = mutexes[(i + 1) % 3].lock(); + } + } + + #[test] + fn test_rwlock_usage() { + let lock = Arc::new(RwLock::new(())); + let lock2 = Arc::clone(&lock); + + let _read_lock = lock.read(); + + // Should be able to acquire lock in the background + thread::spawn(move || { + let _read_lock = lock2.read(); + }) + .join() + .unwrap(); + } + + #[test] + fn test_rwlock_upgradable_read_usage() { + let lock = RwLock::new(()); + + // Should be able to acquire an upgradable read lock. + let upgradable_guard: RwLockUpgradableReadGuard<'_, _> = lock.upgradable_read(); + + // Should be able to upgrade the guard. + let _write_guard: RwLockWriteGuard<'_, _> = + RwLockUpgradableReadGuard::upgrade(upgradable_guard); + } + + #[test] + fn test_once_usage() { + let once = Arc::new(Once::new()); + let once_clone = once.clone(); + + assert!(!once_clone.state().done()); + + let handle = thread::spawn(move || { + assert!(!once_clone.state().done()); + + once_clone.call_once(|| {}); + + assert!(once_clone.state().done()); + }); + + handle.join().unwrap(); + + assert!(once.state().done()); + } +}