From 6e5516eaa70049fd1078f1de1c5dbce2ee29fc43 Mon Sep 17 00:00:00 2001 From: Bert Peters Date: Mon, 4 Jul 2022 21:25:46 +0200 Subject: [PATCH 1/3] Restructure std::sync wrappers --- src/lib.rs | 2 +- src/stdsync.rs | 1101 +++++++++++++++++++++++------------------------- 2 files changed, 526 insertions(+), 577 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 6d5bef8..39a1efc 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -41,7 +41,7 @@ //! //! These operations have been reasonably optimized, but the performance penalty may yet be too much //! for production use. In those cases, it may be beneficial to instead use debug-only versions -//! (such as [`stdsync::DebugMutex`]) which evaluate to a tracing mutex when debug assertions are +//! (such as [`stdsync::Mutex`]) which evaluate to a tracing mutex when debug assertions are //! enabled, and to the underlying mutex when they're not. //! //! [paper]: https://whileydave.com/publications/pk07_jea/ diff --git a/src/stdsync.rs b/src/stdsync.rs index 3cdb4d3..c6e95f7 100644 --- a/src/stdsync.rs +++ b/src/stdsync.rs @@ -1,625 +1,574 @@ //! Tracing mutex wrappers for locks found in `std::sync`. //! //! This module provides wrappers for `std::sync` primitives with exactly the same API and -//! functionality as their counterparts, with the exception that their acquisition order is -//! tracked. +//! functionality as their counterparts, with the exception that their acquisition order is tracked. +//! +//! Dedicated wrappers that provide the dependency tracing can be found in the [`tracing`] module. +//! The original primitives are available from [`std::sync`], imported as [`raw`] for convenience. +//! +//! If debug assertions are enabled, this module imports the primitives from [`tracing`], otherwise +//! it will import from [`raw`]. //! //! ```rust -//! # use tracing_mutex::stdsync::TracingMutex; -//! # use tracing_mutex::stdsync::TracingRwLock; -//! let mutex = TracingMutex::new(()); +//! # use tracing_mutex::stdsync::tracing::Mutex; +//! # use tracing_mutex::stdsync::tracing::RwLock; +//! let mutex = Mutex::new(()); //! mutex.lock().unwrap(); //! -//! let rwlock = TracingRwLock::new(()); +//! let rwlock = RwLock::new(()); //! rwlock.read().unwrap(); //! ``` -use std::fmt; -use std::ops::Deref; -use std::ops::DerefMut; -use std::sync::Condvar; -use std::sync::LockResult; -use std::sync::Mutex; -use std::sync::MutexGuard; -use std::sync::Once; -use std::sync::OnceState; -use std::sync::PoisonError; -use std::sync::RwLock; -use std::sync::RwLockReadGuard; -use std::sync::RwLockWriteGuard; -use std::sync::TryLockError; -use std::sync::TryLockResult; -use std::sync::WaitTimeoutResult; -use std::time::Duration; +pub use std::sync as raw; -use crate::BorrowedMutex; -use crate::LazyMutexId; -use crate::MutexId; - -/// Debug-only tracing `Mutex`. -/// -/// Type alias that resolves to [`TracingMutex`] when debug assertions are enabled and to -/// [`std::sync::Mutex`] when they're not. Use this if you want to have the benefits of cycle -/// detection in development but do not want to pay the performance penalty in release. -#[cfg(debug_assertions)] -pub type DebugMutex = TracingMutex; #[cfg(not(debug_assertions))] -pub type DebugMutex = Mutex; +pub use std::sync::{Condvar, Mutex, MutexGuard, Once, RwLock, RwLockReadGuard, RwLockWriteGuard}; -/// Mutex guard for [`DebugMutex`]. #[cfg(debug_assertions)] -pub type DebugMutexGuard<'a, T> = TracingMutexGuard<'a, T>; -#[cfg(not(debug_assertions))] -pub type DebugMutexGuard<'a, T> = MutexGuard<'a, T>; +pub use tracing::{Condvar, Mutex, MutexGuard, Once, RwLock, RwLockReadGuard, RwLockWriteGuard}; -/// Debug-only `Condvar` -/// -/// Type alias that accepts the mutex guard emitted from [`DebugMutex`]. -#[cfg(debug_assertions)] -pub type DebugCondvar = TracingCondvar; -#[cfg(not(debug_assertions))] -pub type DebugCondvar = Condvar; +/// Dependency tracing versions of [`std::sync`]. +pub mod tracing { + use std::fmt; + use std::ops::Deref; + use std::ops::DerefMut; + use std::sync; + use std::sync::LockResult; + use std::sync::OnceState; + use std::sync::PoisonError; + use std::sync::TryLockError; + use std::sync::TryLockResult; + use std::sync::WaitTimeoutResult; + use std::time::Duration; -/// Debug-only tracing `RwLock`. -/// -/// Type alias that resolves to [`TracingRwLock`] when debug assertions are enabled and to -/// [`std::sync::RwLock`] when they're not. Use this if you want to have the benefits of cycle -/// detection in development but do not want to pay the performance penalty in release. -#[cfg(debug_assertions)] -pub type DebugRwLock = TracingRwLock; -#[cfg(not(debug_assertions))] -pub type DebugRwLock = RwLock; + use crate::BorrowedMutex; + use crate::LazyMutexId; + use crate::MutexId; -/// Read guard for [`DebugRwLock`]. -#[cfg(debug_assertions)] -pub type DebugReadGuard<'a, T> = TracingReadGuard<'a, T>; -#[cfg(not(debug_assertions))] -pub type DebugReadGuard<'a, T> = RwLockReadGuard<'a, T>; - -/// Write guard for [`DebugRwLock`]. -#[cfg(debug_assertions)] -pub type DebugWriteGuard<'a, T> = TracingWriteGuard<'a, T>; -#[cfg(not(debug_assertions))] -pub type DebugWriteGuard<'a, T> = RwLockWriteGuard<'a, T>; - -/// Debug-only tracing `Once`. -/// -/// Type alias that resolves to [`TracingOnce`] when debug assertions are enabled and to -/// [`std::sync::Once`] when they're not. Use this if you want to have the benefits of cycle -/// detection in development but do not want to pay the performance penalty in release. -#[cfg(debug_assertions)] -pub type DebugOnce = TracingOnce; -#[cfg(not(debug_assertions))] -pub type DebugOnce = Once; - -/// Wrapper for [`std::sync::Mutex`]. -/// -/// Refer to the [crate-level][`crate`] documentaiton for the differences between this struct and -/// the one it wraps. -#[derive(Debug, Default)] -pub struct TracingMutex { - inner: Mutex, - id: MutexId, -} - -/// Wrapper for [`std::sync::MutexGuard`]. -/// -/// Refer to the [crate-level][`crate`] documentaiton for the differences between this struct and -/// the one it wraps. -#[derive(Debug)] -pub struct TracingMutexGuard<'a, T> { - inner: MutexGuard<'a, T>, - _mutex: BorrowedMutex<'a>, -} - -fn map_lockresult(result: LockResult, mapper: F) -> LockResult -where - F: FnOnce(I) -> T, -{ - match result { - Ok(inner) => Ok(mapper(inner)), - Err(poisoned) => Err(PoisonError::new(mapper(poisoned.into_inner()))), + /// Wrapper for [`std::sync::Mutex`]. + /// + /// Refer to the [crate-level][`crate`] documentation for the differences between this struct and + /// the one it wraps. + #[derive(Debug, Default)] + pub struct Mutex { + inner: sync::Mutex, + id: MutexId, } -} -fn map_trylockresult(result: TryLockResult, mapper: F) -> TryLockResult -where - F: FnOnce(I) -> T, -{ - match result { - Ok(inner) => Ok(mapper(inner)), - Err(TryLockError::WouldBlock) => Err(TryLockError::WouldBlock), - Err(TryLockError::Poisoned(poisoned)) => { - Err(PoisonError::new(mapper(poisoned.into_inner())).into()) - } + /// Wrapper for [`std::sync::MutexGuard`]. + /// + /// Refer to the [crate-level][`crate`] documentation for the differences between this struct and + /// the one it wraps. + #[derive(Debug)] + pub struct MutexGuard<'a, T> { + inner: sync::MutexGuard<'a, T>, + _mutex: BorrowedMutex<'a>, } -} -impl TracingMutex { - /// Create a new tracing mutex with the provided value. - pub fn new(t: T) -> Self { - Self { - inner: Mutex::new(t), - id: MutexId::new(), + fn map_lockresult(result: LockResult, mapper: F) -> LockResult + where + F: FnOnce(I) -> T, + { + match result { + Ok(inner) => Ok(mapper(inner)), + Err(poisoned) => Err(PoisonError::new(mapper(poisoned.into_inner()))), } } - /// Wrapper for [`std::sync::Mutex::lock`]. - /// - /// # Panics - /// - /// This method participates in lock dependency tracking. If acquiring this lock introduces a - /// dependency cycle, this method will panic. - #[track_caller] - pub fn lock(&self) -> LockResult> { - let mutex = self.id.get_borrowed(); - let result = self.inner.lock(); - - let mapper = |guard| TracingMutexGuard { - _mutex: mutex, - inner: guard, - }; - - map_lockresult(result, mapper) - } - - /// Wrapper for [`std::sync::Mutex::try_lock`]. - /// - /// # Panics - /// - /// This method participates in lock dependency tracking. If acquiring this lock introduces a - /// dependency cycle, this method will panic. - #[track_caller] - pub fn try_lock(&self) -> TryLockResult> { - let mutex = self.id.get_borrowed(); - let result = self.inner.try_lock(); - - let mapper = |guard| TracingMutexGuard { - _mutex: mutex, - inner: guard, - }; - - map_trylockresult(result, mapper) - } - - /// Wrapper for [`std::sync::Mutex::is_poisoned`]. - pub fn is_poisoned(&self) -> bool { - self.inner.is_poisoned() - } - - /// Return a mutable reference to the underlying data. - /// - /// This method does not block as the locking is handled compile-time by the type system. - pub fn get_mut(&mut self) -> LockResult<&mut T> { - self.inner.get_mut() - } - - /// Unwrap the mutex and return its inner value. - pub fn into_inner(self) -> LockResult { - self.inner.into_inner() - } -} - -impl From for TracingMutex { - fn from(t: T) -> Self { - Self::new(t) - } -} - -impl<'a, T> Deref for TracingMutexGuard<'a, T> { - type Target = T; - - fn deref(&self) -> &Self::Target { - &self.inner - } -} - -impl<'a, T> DerefMut for TracingMutexGuard<'a, T> { - fn deref_mut(&mut self) -> &mut Self::Target { - &mut self.inner - } -} - -impl<'a, T: fmt::Display> fmt::Display for TracingMutexGuard<'a, T> { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - self.inner.fmt(f) - } -} - -/// Wrapper around [`std::sync::Condvar`]. -/// -/// Allows `TracingMutexGuard` to be used with a `Condvar`. Unlike other structs in this module, -/// this wrapper does not add any additional dependency tracking or other overhead on top of the -/// primitive it wraps. All dependency tracking happens through the mutexes itself. -/// -/// # Panics -/// -/// This struct does not add any panics over the base implementation of `Condvar`, but panics due to -/// dependency tracking may poison associated mutexes. -/// -/// # Examples -/// -/// ``` -/// use std::sync::Arc; -/// use std::thread; -/// -/// use tracing_mutex::stdsync::{TracingCondvar, TracingMutex}; -/// -/// let pair = Arc::new((TracingMutex::new(false), TracingCondvar::new())); -/// let pair2 = Arc::clone(&pair); -/// -/// // Spawn a thread that will unlock the condvar -/// thread::spawn(move || { -/// let (lock, condvar) = &*pair2; -/// *lock.lock().unwrap() = true; -/// condvar.notify_one(); -/// }); -/// -/// // Wait until the thread unlocks the condvar -/// let (lock, condvar) = &*pair; -/// let guard = lock.lock().unwrap(); -/// let guard = condvar.wait_while(guard, |started| !*started).unwrap(); -/// -/// // Guard should read true now -/// assert!(*guard); -/// ``` -#[derive(Debug, Default)] -pub struct TracingCondvar(Condvar); - -impl TracingCondvar { - /// Creates a new condition variable which is ready to be waited on and notified. - pub fn new() -> Self { - Default::default() - } - - /// Wrapper for [`std::sync::Condvar::wait`]. - pub fn wait<'a, T>( - &self, - guard: TracingMutexGuard<'a, T>, - ) -> LockResult> { - let TracingMutexGuard { _mutex, inner } = guard; - - map_lockresult(self.0.wait(inner), |inner| TracingMutexGuard { - _mutex, - inner, - }) - } - - /// Wrapper for [`std::sync::Condvar::wait_while`]. - pub fn wait_while<'a, T, F>( - &self, - guard: TracingMutexGuard<'a, T>, - condition: F, - ) -> LockResult> + fn map_trylockresult(result: TryLockResult, mapper: F) -> TryLockResult where - F: FnMut(&mut T) -> bool, + F: FnOnce(I) -> T, { - let TracingMutexGuard { _mutex, inner } = guard; - - map_lockresult(self.0.wait_while(inner, condition), |inner| { - TracingMutexGuard { _mutex, inner } - }) - } - - /// Wrapper for [`std::sync::Condvar::wait_timeout`]. - pub fn wait_timeout<'a, T>( - &self, - guard: TracingMutexGuard<'a, T>, - dur: Duration, - ) -> LockResult<(TracingMutexGuard<'a, T>, WaitTimeoutResult)> { - let TracingMutexGuard { _mutex, inner } = guard; - - map_lockresult(self.0.wait_timeout(inner, dur), |(inner, result)| { - (TracingMutexGuard { _mutex, inner }, result) - }) - } - - /// Wrapper for [`std::sync::Condvar::wait_timeout_while`]. - pub fn wait_timeout_while<'a, T, F>( - &self, - guard: TracingMutexGuard<'a, T>, - dur: Duration, - condition: F, - ) -> LockResult<(TracingMutexGuard<'a, T>, WaitTimeoutResult)> - where - F: FnMut(&mut T) -> bool, - { - let TracingMutexGuard { _mutex, inner } = guard; - - map_lockresult( - self.0.wait_timeout_while(inner, dur, condition), - |(inner, result)| (TracingMutexGuard { _mutex, inner }, result), - ) - } - - /// Wrapper for [`std::sync::Condvar::notify_one`]. - pub fn notify_one(&self) { - self.0.notify_one(); - } - - /// Wrapper for [`std::sync::Condvar::notify_all`]. - pub fn notify_all(&self) { - self.0.notify_all(); - } -} - -/// Wrapper for [`std::sync::RwLock`]. -#[derive(Debug, Default)] -pub struct TracingRwLock { - inner: RwLock, - id: MutexId, -} - -/// Hybrid wrapper for both [`std::sync::RwLockReadGuard`] and [`std::sync::RwLockWriteGuard`]. -/// -/// Please refer to [`TracingReadGuard`] and [`TracingWriteGuard`] for usable types. -#[derive(Debug)] -pub struct TracingRwLockGuard<'a, L> { - inner: L, - _mutex: BorrowedMutex<'a>, -} - -/// Wrapper around [`std::sync::RwLockReadGuard`]. -pub type TracingReadGuard<'a, T> = TracingRwLockGuard<'a, RwLockReadGuard<'a, T>>; -/// Wrapper around [`std::sync::RwLockWriteGuard`]. -pub type TracingWriteGuard<'a, T> = TracingRwLockGuard<'a, RwLockWriteGuard<'a, T>>; - -impl TracingRwLock { - pub fn new(t: T) -> Self { - Self { - inner: RwLock::new(t), - id: MutexId::new(), + match result { + Ok(inner) => Ok(mapper(inner)), + Err(TryLockError::WouldBlock) => Err(TryLockError::WouldBlock), + Err(TryLockError::Poisoned(poisoned)) => { + Err(PoisonError::new(mapper(poisoned.into_inner())).into()) + } } } - /// Wrapper for [`std::sync::RwLock::read`]. - /// - /// # Panics - /// - /// This method participates in lock dependency tracking. If acquiring this lock introduces a - /// dependency cycle, this method will panic. - #[track_caller] - pub fn read(&self) -> LockResult> { - let mutex = self.id.get_borrowed(); - let result = self.inner.read(); + impl Mutex { + /// Create a new tracing mutex with the provided value. + pub fn new(t: T) -> Self { + Self { + inner: sync::Mutex::new(t), + id: MutexId::new(), + } + } - map_lockresult(result, |inner| TracingRwLockGuard { - inner, - _mutex: mutex, - }) - } + /// Wrapper for [`std::sync::Mutex::lock`]. + /// + /// # Panics + /// + /// This method participates in lock dependency tracking. If acquiring this lock introduces a + /// dependency cycle, this method will panic. + #[track_caller] + pub fn lock(&self) -> LockResult> { + let mutex = self.id.get_borrowed(); + let result = self.inner.lock(); - /// Wrapper for [`std::sync::RwLock::write`]. - /// - /// # Panics - /// - /// This method participates in lock dependency tracking. If acquiring this lock introduces a - /// dependency cycle, this method will panic. - #[track_caller] - pub fn write(&self) -> LockResult> { - let mutex = self.id.get_borrowed(); - let result = self.inner.write(); + let mapper = |guard| MutexGuard { + _mutex: mutex, + inner: guard, + }; - map_lockresult(result, |inner| TracingRwLockGuard { - inner, - _mutex: mutex, - }) - } + map_lockresult(result, mapper) + } - /// Wrapper for [`std::sync::RwLock::try_read`]. - /// - /// # Panics - /// - /// This method participates in lock dependency tracking. If acquiring this lock introduces a - /// dependency cycle, this method will panic. - #[track_caller] - pub fn try_read(&self) -> TryLockResult> { - let mutex = self.id.get_borrowed(); - let result = self.inner.try_read(); + /// Wrapper for [`std::sync::Mutex::try_lock`]. + /// + /// # Panics + /// + /// This method participates in lock dependency tracking. If acquiring this lock introduces a + /// dependency cycle, this method will panic. + #[track_caller] + pub fn try_lock(&self) -> TryLockResult> { + let mutex = self.id.get_borrowed(); + let result = self.inner.try_lock(); - map_trylockresult(result, |inner| TracingRwLockGuard { - inner, - _mutex: mutex, - }) - } + let mapper = |guard| MutexGuard { + _mutex: mutex, + inner: guard, + }; - /// Wrapper for [`std::sync::RwLock::try_write`]. - /// - /// # Panics - /// - /// This method participates in lock dependency tracking. If acquiring this lock introduces a - /// dependency cycle, this method will panic. - #[track_caller] - pub fn try_write(&self) -> TryLockResult> { - let mutex = self.id.get_borrowed(); - let result = self.inner.try_write(); + map_trylockresult(result, mapper) + } - map_trylockresult(result, |inner| TracingRwLockGuard { - inner, - _mutex: mutex, - }) - } + /// Wrapper for [`std::sync::Mutex::is_poisoned`]. + pub fn is_poisoned(&self) -> bool { + self.inner.is_poisoned() + } - /// Return a mutable reference to the underlying data. - /// - /// This method does not block as the locking is handled compile-time by the type system. - pub fn get_mut(&mut self) -> LockResult<&mut T> { - self.inner.get_mut() - } + /// Return a mutable reference to the underlying data. + /// + /// This method does not block as the locking is handled compile-time by the type system. + pub fn get_mut(&mut self) -> LockResult<&mut T> { + self.inner.get_mut() + } - /// Unwrap the mutex and return its inner value. - pub fn into_inner(self) -> LockResult { - self.inner.into_inner() - } -} - -impl From for TracingRwLock { - fn from(t: T) -> Self { - Self::new(t) - } -} - -impl<'a, L, T> Deref for TracingRwLockGuard<'a, L> -where - L: Deref, -{ - type Target = T; - - fn deref(&self) -> &Self::Target { - self.inner.deref() - } -} - -impl<'a, T, L> DerefMut for TracingRwLockGuard<'a, L> -where - L: Deref + DerefMut, -{ - fn deref_mut(&mut self) -> &mut Self::Target { - self.inner.deref_mut() - } -} - -/// Wrapper around [`std::sync::Once`]. -/// -/// Refer to the [crate-level][`crate`] documentaiton for the differences between this struct and -/// the one it wraps. -#[derive(Debug)] -pub struct TracingOnce { - inner: Once, - mutex_id: LazyMutexId, -} - -impl TracingOnce { - /// Create a new `Once` value. - pub const fn new() -> Self { - Self { - inner: Once::new(), - mutex_id: LazyMutexId::new(), + /// Unwrap the mutex and return its inner value. + pub fn into_inner(self) -> LockResult { + self.inner.into_inner() } } - /// Wrapper for [`std::sync::Once::call_once`]. + impl From for Mutex { + fn from(t: T) -> Self { + Self::new(t) + } + } + + impl<'a, T> Deref for MutexGuard<'a, T> { + type Target = T; + + fn deref(&self) -> &Self::Target { + &self.inner + } + } + + impl<'a, T> DerefMut for MutexGuard<'a, T> { + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.inner + } + } + + impl<'a, T: fmt::Display> fmt::Display for MutexGuard<'a, T> { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + self.inner.fmt(f) + } + } + + /// Wrapper around [`std::sync::Condvar`]. + /// + /// Allows `TracingMutexGuard` to be used with a `Condvar`. Unlike other structs in this module, + /// this wrapper does not add any additional dependency tracking or other overhead on top of the + /// primitive it wraps. All dependency tracking happens through the mutexes itself. /// /// # Panics /// - /// In addition to the panics that `Once` can cause, this method will panic if calling it - /// introduces a cycle in the lock dependency graph. - pub fn call_once(&self, f: F) - where - F: FnOnce(), - { - let _guard = self.mutex_id.get_borrowed(); - self.inner.call_once(f); + /// This struct does not add any panics over the base implementation of `Condvar`, but panics due to + /// dependency tracking may poison associated mutexes. + /// + /// # Examples + /// + /// ``` + /// use std::sync::Arc; + /// use std::thread; + /// + /// use tracing_mutex::stdsync::tracing::{Condvar, Mutex}; + /// + /// let pair = Arc::new((Mutex::new(false), Condvar::new())); + /// let pair2 = Arc::clone(&pair); + /// + /// // Spawn a thread that will unlock the condvar + /// thread::spawn(move || { + /// let (lock, condvar) = &*pair2; + /// *lock.lock().unwrap() = true; + /// condvar.notify_one(); + /// }); + /// + /// // Wait until the thread unlocks the condvar + /// let (lock, condvar) = &*pair; + /// let guard = lock.lock().unwrap(); + /// let guard = condvar.wait_while(guard, |started| !*started).unwrap(); + /// + /// // Guard should read true now + /// assert!(*guard); + /// ``` + #[derive(Debug, Default)] + pub struct Condvar(sync::Condvar); + + impl Condvar { + /// Creates a new condition variable which is ready to be waited on and notified. + pub fn new() -> Self { + Default::default() + } + + /// Wrapper for [`std::sync::Condvar::wait`]. + pub fn wait<'a, T>(&self, guard: MutexGuard<'a, T>) -> LockResult> { + let MutexGuard { _mutex, inner } = guard; + + map_lockresult(self.0.wait(inner), |inner| MutexGuard { _mutex, inner }) + } + + /// Wrapper for [`std::sync::Condvar::wait_while`]. + pub fn wait_while<'a, T, F>( + &self, + guard: MutexGuard<'a, T>, + condition: F, + ) -> LockResult> + where + F: FnMut(&mut T) -> bool, + { + let MutexGuard { _mutex, inner } = guard; + + map_lockresult(self.0.wait_while(inner, condition), |inner| MutexGuard { + _mutex, + inner, + }) + } + + /// Wrapper for [`std::sync::Condvar::wait_timeout`]. + pub fn wait_timeout<'a, T>( + &self, + guard: MutexGuard<'a, T>, + dur: Duration, + ) -> LockResult<(MutexGuard<'a, T>, WaitTimeoutResult)> { + let MutexGuard { _mutex, inner } = guard; + + map_lockresult(self.0.wait_timeout(inner, dur), |(inner, result)| { + (MutexGuard { _mutex, inner }, result) + }) + } + + /// Wrapper for [`std::sync::Condvar::wait_timeout_while`]. + pub fn wait_timeout_while<'a, T, F>( + &self, + guard: MutexGuard<'a, T>, + dur: Duration, + condition: F, + ) -> LockResult<(MutexGuard<'a, T>, WaitTimeoutResult)> + where + F: FnMut(&mut T) -> bool, + { + let MutexGuard { _mutex, inner } = guard; + + map_lockresult( + self.0.wait_timeout_while(inner, dur, condition), + |(inner, result)| (MutexGuard { _mutex, inner }, result), + ) + } + + /// Wrapper for [`std::sync::Condvar::notify_one`]. + pub fn notify_one(&self) { + self.0.notify_one(); + } + + /// Wrapper for [`std::sync::Condvar::notify_all`]. + pub fn notify_all(&self) { + self.0.notify_all(); + } } - /// Performs the same operation as [`call_once`][TracingOnce::call_once] except it ignores - /// poisoning. - /// - /// # Panics - /// - /// This method participates in lock dependency tracking. If acquiring this lock introduces a - /// dependency cycle, this method will panic. - pub fn call_once_force(&self, f: F) - where - F: FnOnce(&OnceState), - { - let _guard = self.mutex_id.get_borrowed(); - self.inner.call_once_force(f); + /// Wrapper for [`std::sync::RwLock`]. + #[derive(Debug, Default)] + pub struct RwLock { + inner: sync::RwLock, + id: MutexId, } - /// Returns true if some `call_once` has completed successfully. - pub fn is_completed(&self) -> bool { - self.inner.is_completed() - } -} - -#[cfg(test)] -mod tests { - use std::sync::Arc; - use std::thread; - - use super::*; - - #[test] - fn test_mutex_usage() { - let mutex = Arc::new(TracingMutex::new(0)); - - assert_eq!(*mutex.lock().unwrap(), 0); - *mutex.lock().unwrap() = 1; - assert_eq!(*mutex.lock().unwrap(), 1); - - let mutex_clone = mutex.clone(); - - let _guard = mutex.lock().unwrap(); - - // Now try to cause a blocking exception in another thread - let handle = thread::spawn(move || { - let result = mutex_clone.try_lock().unwrap_err(); - - assert!(matches!(result, TryLockError::WouldBlock)); - }); - - handle.join().unwrap(); - } - - #[test] - fn test_rwlock_usage() { - let rwlock = Arc::new(TracingRwLock::new(0)); - - assert_eq!(*rwlock.read().unwrap(), 0); - assert_eq!(*rwlock.write().unwrap(), 0); - *rwlock.write().unwrap() = 1; - assert_eq!(*rwlock.read().unwrap(), 1); - assert_eq!(*rwlock.write().unwrap(), 1); - - let rwlock_clone = rwlock.clone(); - - let _read_lock = rwlock.read().unwrap(); - - // Now try to cause a blocking exception in another thread - let handle = thread::spawn(move || { - let write_result = rwlock_clone.try_write().unwrap_err(); - - assert!(matches!(write_result, TryLockError::WouldBlock)); - - // Should be able to get a read lock just fine. - let _read_lock = rwlock_clone.read().unwrap(); - }); - - handle.join().unwrap(); - } - - #[test] - fn test_once_usage() { - let once = Arc::new(TracingOnce::new()); - let once_clone = once.clone(); - - assert!(!once.is_completed()); - - let handle = thread::spawn(move || { - assert!(!once_clone.is_completed()); - - once_clone.call_once(|| {}); - - assert!(once_clone.is_completed()); - }); - - handle.join().unwrap(); - - assert!(once.is_completed()); - } - - #[test] - #[should_panic(expected = "Mutex order graph should not have cycles")] - fn test_detect_cycle() { - let a = TracingMutex::new(()); - let b = TracingMutex::new(()); - - let hold_a = a.lock().unwrap(); - let _ = b.lock(); - - drop(hold_a); - - let _hold_b = b.lock().unwrap(); - let _ = a.lock(); + /// Hybrid wrapper for both [`std::sync::RwLockReadGuard`] and [`std::sync::RwLockWriteGuard`]. + /// + /// Please refer to [`RwLockReadGuard`] and [`RwLockWriteGuard`] for usable types. + #[derive(Debug)] + pub struct TracingRwLockGuard<'a, L> { + inner: L, + _mutex: BorrowedMutex<'a>, + } + + /// Wrapper around [`std::sync::RwLockReadGuard`]. + pub type RwLockReadGuard<'a, T> = TracingRwLockGuard<'a, sync::RwLockReadGuard<'a, T>>; + /// Wrapper around [`std::sync::RwLockWriteGuard`]. + pub type RwLockWriteGuard<'a, T> = TracingRwLockGuard<'a, sync::RwLockWriteGuard<'a, T>>; + + impl RwLock { + pub fn new(t: T) -> Self { + Self { + inner: sync::RwLock::new(t), + id: MutexId::new(), + } + } + + /// Wrapper for [`std::sync::RwLock::read`]. + /// + /// # Panics + /// + /// This method participates in lock dependency tracking. If acquiring this lock introduces a + /// dependency cycle, this method will panic. + #[track_caller] + pub fn read(&self) -> LockResult> { + let mutex = self.id.get_borrowed(); + let result = self.inner.read(); + + map_lockresult(result, |inner| TracingRwLockGuard { + inner, + _mutex: mutex, + }) + } + + /// Wrapper for [`std::sync::RwLock::write`]. + /// + /// # Panics + /// + /// This method participates in lock dependency tracking. If acquiring this lock introduces a + /// dependency cycle, this method will panic. + #[track_caller] + pub fn write(&self) -> LockResult> { + let mutex = self.id.get_borrowed(); + let result = self.inner.write(); + + map_lockresult(result, |inner| TracingRwLockGuard { + inner, + _mutex: mutex, + }) + } + + /// Wrapper for [`std::sync::RwLock::try_read`]. + /// + /// # Panics + /// + /// This method participates in lock dependency tracking. If acquiring this lock introduces a + /// dependency cycle, this method will panic. + #[track_caller] + pub fn try_read(&self) -> TryLockResult> { + let mutex = self.id.get_borrowed(); + let result = self.inner.try_read(); + + map_trylockresult(result, |inner| TracingRwLockGuard { + inner, + _mutex: mutex, + }) + } + + /// Wrapper for [`std::sync::RwLock::try_write`]. + /// + /// # Panics + /// + /// This method participates in lock dependency tracking. If acquiring this lock introduces a + /// dependency cycle, this method will panic. + #[track_caller] + pub fn try_write(&self) -> TryLockResult> { + let mutex = self.id.get_borrowed(); + let result = self.inner.try_write(); + + map_trylockresult(result, |inner| TracingRwLockGuard { + inner, + _mutex: mutex, + }) + } + + /// Return a mutable reference to the underlying data. + /// + /// This method does not block as the locking is handled compile-time by the type system. + pub fn get_mut(&mut self) -> LockResult<&mut T> { + self.inner.get_mut() + } + + /// Unwrap the mutex and return its inner value. + pub fn into_inner(self) -> LockResult { + self.inner.into_inner() + } + } + + impl From for RwLock { + fn from(t: T) -> Self { + Self::new(t) + } + } + + impl<'a, L, T> Deref for TracingRwLockGuard<'a, L> + where + L: Deref, + { + type Target = T; + + fn deref(&self) -> &Self::Target { + self.inner.deref() + } + } + + impl<'a, T, L> DerefMut for TracingRwLockGuard<'a, L> + where + L: Deref + DerefMut, + { + fn deref_mut(&mut self) -> &mut Self::Target { + self.inner.deref_mut() + } + } + + /// Wrapper around [`std::sync::Once`]. + /// + /// Refer to the [crate-level][`crate`] documentaiton for the differences between this struct and + /// the one it wraps. + #[derive(Debug)] + pub struct Once { + inner: sync::Once, + mutex_id: LazyMutexId, + } + + impl Once { + /// Create a new `Once` value. + pub const fn new() -> Self { + Self { + inner: sync::Once::new(), + mutex_id: LazyMutexId::new(), + } + } + + /// Wrapper for [`std::sync::Once::call_once`]. + /// + /// # Panics + /// + /// In addition to the panics that `Once` can cause, this method will panic if calling it + /// introduces a cycle in the lock dependency graph. + pub fn call_once(&self, f: F) + where + F: FnOnce(), + { + let _guard = self.mutex_id.get_borrowed(); + self.inner.call_once(f); + } + + /// Performs the same operation as [`call_once`][Once::call_once] except it ignores + /// poisoning. + /// + /// # Panics + /// + /// This method participates in lock dependency tracking. If acquiring this lock introduces a + /// dependency cycle, this method will panic. + pub fn call_once_force(&self, f: F) + where + F: FnOnce(&OnceState), + { + let _guard = self.mutex_id.get_borrowed(); + self.inner.call_once_force(f); + } + + /// Returns true if some `call_once` has completed successfully. + pub fn is_completed(&self) -> bool { + self.inner.is_completed() + } + } + + #[cfg(test)] + mod tests { + use std::sync::Arc; + use std::thread; + + use super::*; + + #[test] + fn test_mutex_usage() { + let mutex = Arc::new(Mutex::new(0)); + + assert_eq!(*mutex.lock().unwrap(), 0); + *mutex.lock().unwrap() = 1; + assert_eq!(*mutex.lock().unwrap(), 1); + + let mutex_clone = mutex.clone(); + + let _guard = mutex.lock().unwrap(); + + // Now try to cause a blocking exception in another thread + let handle = thread::spawn(move || { + let result = mutex_clone.try_lock().unwrap_err(); + + assert!(matches!(result, TryLockError::WouldBlock)); + }); + + handle.join().unwrap(); + } + + #[test] + fn test_rwlock_usage() { + let rwlock = Arc::new(RwLock::new(0)); + + assert_eq!(*rwlock.read().unwrap(), 0); + assert_eq!(*rwlock.write().unwrap(), 0); + *rwlock.write().unwrap() = 1; + assert_eq!(*rwlock.read().unwrap(), 1); + assert_eq!(*rwlock.write().unwrap(), 1); + + let rwlock_clone = rwlock.clone(); + + let _read_lock = rwlock.read().unwrap(); + + // Now try to cause a blocking exception in another thread + let handle = thread::spawn(move || { + let write_result = rwlock_clone.try_write().unwrap_err(); + + assert!(matches!(write_result, TryLockError::WouldBlock)); + + // Should be able to get a read lock just fine. + let _read_lock = rwlock_clone.read().unwrap(); + }); + + handle.join().unwrap(); + } + + #[test] + fn test_once_usage() { + let once = Arc::new(Once::new()); + let once_clone = once.clone(); + + assert!(!once.is_completed()); + + let handle = thread::spawn(move || { + assert!(!once_clone.is_completed()); + + once_clone.call_once(|| {}); + + assert!(once_clone.is_completed()); + }); + + handle.join().unwrap(); + + assert!(once.is_completed()); + } + + #[test] + #[should_panic(expected = "Mutex order graph should not have cycles")] + fn test_detect_cycle() { + let a = Mutex::new(()); + let b = Mutex::new(()); + + let hold_a = a.lock().unwrap(); + let _ = b.lock(); + + drop(hold_a); + + let _hold_b = b.lock().unwrap(); + let _ = a.lock(); + } } } From 56b0604448da71ed93cbf3199a4a17108f952742 Mon Sep 17 00:00:00 2001 From: Bert Peters Date: Sat, 27 Aug 2022 09:56:04 +0200 Subject: [PATCH 2/3] Restructure parking_lot wrappers --- benches/mutex.rs | 2 +- src/parkinglot.rs | 294 ++++++++++++++++++---------------------------- 2 files changed, 118 insertions(+), 178 deletions(-) diff --git a/benches/mutex.rs b/benches/mutex.rs index 0c75198..3fe8b83 100644 --- a/benches/mutex.rs +++ b/benches/mutex.rs @@ -7,7 +7,7 @@ use criterion::BenchmarkId; use criterion::Criterion; use criterion::Throughput; use rand::prelude::*; -use tracing_mutex::stdsync::TracingMutex; +use tracing_mutex::stdsync::tracing::Mutex as TracingMutex; const SAMPLE_SIZES: [usize; 5] = [10, 30, 100, 300, 1000]; diff --git a/src/parkinglot.rs b/src/parkinglot.rs index fff2cb4..6f12a0a 100644 --- a/src/parkinglot.rs +++ b/src/parkinglot.rs @@ -1,19 +1,20 @@ //! Wrapper types and type aliases for tracing [`parking_lot`] mutexes. //! //! This module provides type aliases that use the [`lockapi`][crate::lockapi] module to provide -//! tracing variants of the `parking_lot` primitives. Each of the `TracingX` type aliases wraps an -//! `X` in the `parkint_lot` api with dependency tracking, and a `DebugX` will refer to a `TracingX` -//! when `debug_assertions` are enabled and to `X` when they're not. This can be used to aid -//! debugging in development while enjoying maximum performance in production. +//! tracing variants of the `parking_lot` primitives. The [`tracing`] module contains type aliases +//! that use dependency tracking, while the main `parking_lot` primitives are reexported as [`raw`]. +//! +//! This main module imports from [`tracing`] when `debug_assertions` are enabled, and from [`raw`] +//! when they're not. Note that primitives for which no tracing wrapper exists are not imported into +//! the main module. //! //! # Usage //! //! ``` //! # use std::sync::Arc; //! # use std::thread; -//! # use lock_api::Mutex; -//! # use tracing_mutex::parkinglot::TracingMutex; -//! let mutex = Arc::new(TracingMutex::new(0)); +//! use tracing_mutex::parkinglot::Mutex; +//! let mutex = Arc::new(Mutex::new(0)); //! //! let handles: Vec<_> = (0..10).map(|_| { //! let mutex = Arc::clone(&mutex); @@ -37,191 +38,130 @@ //! In addition, the mutex guards returned by the tracing wrappers are `!Send`, regardless of //! whether `parking_lot` is configured to have `Send` mutex guards. This is a limitation of the //! current bookkeeping system. -use parking_lot::Once; -use parking_lot::OnceState; -use crate::lockapi::TracingWrapper; -use crate::LazyMutexId; +pub use parking_lot as raw; -macro_rules! debug_variant { - ($debug_name:ident, $tracing_name:ident, $normal_name:ty) => { - type $tracing_name = TracingWrapper<$normal_name>; +#[cfg(debug_assertions)] +pub use tracing::{ + FairMutex, FairMutexGuard, MappedFairMutexGuard, MappedMutexGuard, MappedReentrantMutexGuard, + MappedRwLockReadGuard, MappedRwLockWriteGuard, Mutex, MutexGuard, Once, OnceState, + ReentrantMutex, ReentrantMutexGuard, RwLock, RwLockReadGuard, RwLockUpgradableReadGuard, + RwLockWriteGuard, +}; - #[cfg(debug_assertions)] - type $debug_name = TracingWrapper<$normal_name>; - #[cfg(not(debug_assertions))] - type $debug_name = $normal_name; - }; -} +#[cfg(not(debug_assertions))] +pub use parking_lot::{ + FairMutex, FairMutexGuard, MappedFairMutexGuard, MappedMutexGuard, MappedReentrantMutexGuard, + MappedRwLockReadGuard, MappedRwLockWriteGuard, Mutex, MutexGuard, Once, OnceState, + ReentrantMutex, ReentrantMutexGuard, RwLock, RwLockReadGuard, RwLockUpgradableReadGuard, + RwLockWriteGuard, +}; -debug_variant!( - DebugRawFairMutex, - TracingRawFairMutex, - parking_lot::RawFairMutex -); -debug_variant!(DebugRawMutex, TracingRawMutex, parking_lot::RawMutex); -debug_variant!(DebugRawRwLock, TracingRawRwLock, parking_lot::RawRwLock); +/// Dependency tracing wrappers for [`parking_lot`]. +pub mod tracing { + pub use parking_lot::OnceState; -/// Dependency tracking fair mutex. See: [`parking_lot::FairMutex`]. -pub type TracingFairMutex = lock_api::Mutex; -/// Mutex guard for [`TracingFairMutex`]. -pub type TracingFairMutexGuard<'a, T> = lock_api::MutexGuard<'a, TracingRawFairMutex, T>; -/// RAII guard for `TracingFairMutexGuard::map`. -pub type TracingMappedFairMutexGuard<'a, T> = - lock_api::MappedMutexGuard<'a, TracingRawFairMutex, T>; -/// Debug-only dependency tracking fair mutex. -/// -/// If debug assertions are enabled this resolves to [`TracingFairMutex`] and to -/// [`parking_lot::FairMutex`] otherwise. -pub type DebugFairMutex = lock_api::Mutex; -/// Mutex guard for [`DebugFairMutex`]. -pub type DebugFairMutexGuard<'a, T> = lock_api::MutexGuard<'a, DebugRawFairMutex, T>; -/// RAII guard for `DebugFairMutexGuard::map`. -pub type DebugMappedFairMutexGuard<'a, T> = lock_api::MappedMutexGuard<'a, DebugRawFairMutex, T>; + use crate::lockapi::TracingWrapper; + use crate::LazyMutexId; -/// Dependency tracking mutex. See: [`parking_lot::Mutex`]. -pub type TracingMutex = lock_api::Mutex; -/// Mutex guard for [`TracingMutex`]. -pub type TracingMutexGuard<'a, T> = lock_api::MutexGuard<'a, TracingRawMutex, T>; -/// RAII guard for `TracingMutexGuard::map`. -pub type TracingMappedMutexGuard<'a, T> = lock_api::MappedMutexGuard<'a, TracingRawMutex, T>; -/// Debug-only dependency tracking mutex. -/// -/// If debug assertions are enabled this resolves to [`TracingMutex`] and to [`parking_lot::Mutex`] -/// otherwise. -pub type DebugMutex = lock_api::Mutex; -/// Mutex guard for [`DebugMutex`]. -pub type DebugMutexGuard<'a, T> = lock_api::MutexGuard<'a, DebugRawMutex, T>; -/// RAII guard for `TracingMutexGuard::map`. -pub type DebugMappedMutexGuard<'a, T> = lock_api::MappedMutexGuard<'a, DebugRawMutex, T>; + type RawFairMutex = TracingWrapper; + type RawMutex = TracingWrapper; + type RawRwLock = TracingWrapper; -/// 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 TracingReentrantMutex = - lock_api::ReentrantMutex, parking_lot::RawThreadId, T>; -/// Mutex guard for [`TracingReentrantMutex`]. -pub type TracingReentrantMutexGuard<'a, T> = lock_api::ReentrantMutexGuard< - 'a, - TracingWrapper, - parking_lot::RawThreadId, - T, ->; -/// RAII guard for `TracingReentrantMutexGuard::map`. -pub type TracingMappedReentrantMutexGuard<'a, T> = - lock_api::MappedReentrantMutexGuard<'a, TracingRawMutex, parking_lot::RawThreadId, T>; + /// 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>; -/// Debug-only dependency tracking reentrant mutex. -/// -/// If debug assertions are enabled this resolves to [`TracingReentrantMutex`] and to -/// [`parking_lot::ReentrantMutex`] otherwise. -pub type DebugReentrantMutex = - lock_api::ReentrantMutex; -/// Mutex guard for [`DebugReentrantMutex`]. -pub type DebugReentrantMutexGuard<'a, T> = - lock_api::ReentrantMutexGuard<'a, DebugRawMutex, parking_lot::RawThreadId, T>; -/// RAII guard for `DebugReentrantMutexGuard::map`. -pub type DebugMappedReentrantMutexGuard<'a, T> = - lock_api::MappedReentrantMutexGuard<'a, DebugRawMutex, parking_lot::RawThreadId, 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 RwLock. See: [`parking_lot::RwLock`]. -pub type TracingRwLock = lock_api::RwLock; -/// Read guard for [`TracingRwLock`]. -pub type TracingRwLockReadGuard<'a, T> = lock_api::RwLockReadGuard<'a, TracingRawRwLock, T>; -/// Upgradable Read guard for [`TracingRwLock`]. -pub type TracingRwLockUpgradableReadGuard<'a, T> = - lock_api::RwLockUpgradableReadGuard<'a, TracingRawRwLock, T>; -/// Write guard for [`TracingRwLock`]. -pub type TracingRwLockWriteGuard<'a, T> = lock_api::RwLockWriteGuard<'a, TracingRawRwLock, T>; -/// RAII guard for `TracingRwLockReadGuard::map`. -pub type TracingMappedRwLockReadGuard<'a, T> = - lock_api::MappedRwLockReadGuard<'a, TracingRawRwLock, T>; -/// RAII guard for `TracingRwLockWriteGuard::map`. -pub type TracingMappedRwLockWriteGuard<'a, T> = - lock_api::MappedRwLockWriteGuard<'a, TracingRawRwLock, 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>; -/// Debug-only dependency tracking RwLock. -/// -/// If debug assertions are enabled this resolved to [`TracingRwLock`] and to -/// [`parking_lot::RwLock`] otherwise. -pub type DebugRwLock = lock_api::RwLock; -/// Read guard for [`TracingRwLock`]. -pub type DebugRwLockReadGuard<'a, T> = lock_api::RwLockReadGuard<'a, DebugRawRwLock, T>; -/// Upgradable Read guard for [`TracingRwLock`]. -pub type DebugRwLockUpgradableReadGuard<'a, T> = - lock_api::RwLockUpgradableReadGuard<'a, DebugRawRwLock, T>; -/// Write guard for [`TracingRwLock`]. -pub type DebugRwLockWriteGuard<'a, T> = lock_api::RwLockWriteGuard<'a, DebugRawRwLock, T>; -/// RAII guard for `DebugRwLockReadGuard::map`. -pub type DebugMappedRwLockReadGuard<'a, T> = lock_api::MappedRwLockReadGuard<'a, DebugRawRwLock, T>; -/// RAII guard for `DebugRwLockWriteGuard::map`. -pub type DebugMappedRwLockWriteGuard<'a, T> = - lock_api::MappedRwLockWriteGuard<'a, DebugRawRwLock, 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 TracingOnce { - inner: Once, - id: LazyMutexId, -} + /// A dependency-tracking wrapper for [`parking_lot::Once`]. + #[derive(Debug, Default)] + pub struct Once { + inner: parking_lot::Once, + id: LazyMutexId, + } -impl TracingOnce { - /// Create a new `TracingOnce` value. - pub const fn new() -> Self { - Self { - inner: Once::new(), - id: LazyMutexId::new(), + 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()) { + let _borrow = self.id.get_borrowed(); + 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)) { + let _borrow = self.id.get_borrowed(); + self.inner.call_once_force(f); } } - - /// Returns the current state of this `Once`. - pub fn state(&self) -> OnceState { - self.inner.state() - } - - /// - /// This call is considered as "locking this `TracingOnce`" 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()) { - let _borrow = self.id.get_borrowed(); - self.inner.call_once(f); - } - - /// Performs the given initialization routine once and only once. - /// - /// This method is identical to [`TracingOnce::call_once`] except it ignores poisoning. - pub fn call_once_force(&self, f: impl FnOnce(OnceState)) { - let _borrow = self.id.get_borrowed(); - self.inner.call_once_force(f); - } } -/// Debug-only `Once`. -/// -/// If debug assertions are enabled this resolves to [`TracingOnce`] and to [`parking_lot::Once`] -/// otherwise. -#[cfg(debug_assertions)] -pub type DebugOnce = TracingOnce; -#[cfg(not(debug_assertions))] -pub type DebugOnce = Once; - #[cfg(test)] mod tests { use std::sync::Arc; use std::thread; - use super::*; + use super::tracing; #[test] fn test_mutex_usage() { - let mutex = Arc::new(TracingMutex::new(())); + let mutex = Arc::new(tracing::Mutex::new(())); let local_lock = mutex.lock(); drop(local_lock); @@ -236,9 +176,9 @@ mod tests { #[should_panic] fn test_mutex_conflict() { let mutexes = [ - TracingMutex::new(()), - TracingMutex::new(()), - TracingMutex::new(()), + tracing::Mutex::new(()), + tracing::Mutex::new(()), + tracing::Mutex::new(()), ]; for i in 0..3 { @@ -249,7 +189,7 @@ mod tests { #[test] fn test_rwlock_usage() { - let lock = Arc::new(TracingRwLock::new(())); + let lock = Arc::new(tracing::RwLock::new(())); let lock2 = Arc::clone(&lock); let _read_lock = lock.read(); @@ -264,19 +204,19 @@ mod tests { #[test] fn test_rwlock_upgradable_read_usage() { - let lock = TracingRwLock::new(()); + let lock = tracing::RwLock::new(()); // Should be able to acquire an upgradable read lock. - let upgradable_guard: TracingRwLockUpgradableReadGuard<'_, _> = lock.upgradable_read(); + let upgradable_guard: tracing::RwLockUpgradableReadGuard<'_, _> = lock.upgradable_read(); // Should be able to upgrade the guard. - let _write_guard: TracingRwLockWriteGuard<'_, _> = - TracingRwLockUpgradableReadGuard::upgrade(upgradable_guard); + let _write_guard: tracing::RwLockWriteGuard<'_, _> = + tracing::RwLockUpgradableReadGuard::upgrade(upgradable_guard); } #[test] fn test_once_usage() { - let once = Arc::new(TracingOnce::new()); + let once = Arc::new(tracing::Once::new()); let once_clone = once.clone(); assert!(!once_clone.state().done()); From f78969ebf79a49289f0e14e4b8a5928ba79cb733 Mon Sep 17 00:00:00 2001 From: Bert Peters Date: Sat, 27 Aug 2022 10:08:51 +0200 Subject: [PATCH 3/3] Update documentation --- CHANGELOG.md | 1 + README.md | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index fa01a3c..538c4c8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,7 @@ adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). ### Breaking - Update [`parking_lot`][parking_lot] dependency to `0.12`. +- Restructured the crate to reduce typename verbosity. For details, see: #25. ### Fixed diff --git a/README.md b/README.md index 4513fac..99f2791 100644 --- a/README.md +++ b/README.md @@ -42,9 +42,9 @@ Replacements for the synchronization primitives in `std::sync` can be found in t Support for other synchronization primitives is planned. ```rust -use tracing_mutex::stdsync::TracingMutex; +use tracing_mutex::stdsync::Mutex; -let some_mutex = TracingMutex::new(42); +let some_mutex = Mutex::new(42); *some_mutex.lock().unwrap() += 1; println!("{:?}", some_mutex); ```