diff --git a/src/parkinglot.rs b/src/parkinglot.rs index 25b258c..2524aa3 100644 --- a/src/parkinglot.rs +++ b/src/parkinglot.rs @@ -1,4 +1,8 @@ +use parking_lot::Once; +use parking_lot::OnceState; + use crate::lockapi::TracingWrapper; +use crate::LazyMutexId; macro_rules! debug_variant { ($debug_name:ident, $tracing_name:ident, $normal_name:ty) => { @@ -25,7 +29,7 @@ pub type TracingFairMutexGuard<'a, T> = lock_api::MutexGuard<'a, TracingRawFairM /// Debug-only dependency tracking fair mutex. /// /// If debug assertions are enabled this resolves to [`TracingFairMutex`] and to -/// [`parking_lot::FairMutex`] if it's not. +/// [`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>; @@ -37,7 +41,7 @@ pub type TracingMutexGuard<'a, T> = lock_api::MutexGuard<'a, TracingRawMutex, T> /// Debug-only dependency tracking mutex. /// /// If debug assertions are enabled this resolves to [`TracingMutex`] and to [`parking_lot::Mutex`] -/// if it's not. +/// otherwise. pub type DebugMutex = lock_api::Mutex; /// Mutex guard for [`DebugMutex`]. pub type DebugMutexGuard<'a, T> = lock_api::MutexGuard<'a, DebugRawMutex, T>; @@ -56,13 +60,65 @@ pub type TracingReentrantMutexGuard<'a, T> = lock_api::ReentrantMutexGuard< /// Debug-only dependency tracking reentrant mutex. /// /// If debug assertions are enabled this resolves to [`TracingReentrantMutex`] and to -/// [`parking_lot::ReentrantMutex`] if it's not. +/// [`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>; +/// A dependency-tracking wrapper for [`parking_lot::Once`]. +#[derive(Debug, Default)] +pub struct TracingOnce { + inner: Once, + id: LazyMutexId, +} + +impl TracingOnce { + /// Create a new `TracingOnce` value. + pub const fn new() -> Self { + Self { + inner: 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 `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 routeine once and only once. + /// + /// This method is identical to [`TracingOnce::call_once`] except it ignores poisining. + 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; @@ -97,4 +153,24 @@ mod tests { let _second_lock = mutexes[(i + 1) % 3].lock(); } } + + #[test] + fn test_once_usage() { + let once = Arc::new(TracingOnce::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()); + } }