tracing_mutex/parkinglot/
tracing.rs

1//! Dependency tracing wrappers for [`::parking_lot`].
2pub use parking_lot::OnceState;
3pub use parking_lot::RawThreadId;
4
5use crate::LazyMutexId;
6use crate::lockapi::TracingWrapper;
7
8pub type RawFairMutex = TracingWrapper<::parking_lot::RawFairMutex>;
9pub type RawMutex = TracingWrapper<::parking_lot::RawMutex>;
10pub type RawRwLock = TracingWrapper<::parking_lot::RawRwLock>;
11
12/// Dependency tracking fair mutex. See: [`::parking_lot::FairMutex`].
13pub type FairMutex<T> = lock_api::Mutex<RawFairMutex, T>;
14/// Mutex guard for [`FairMutex`].
15pub type FairMutexGuard<'a, T> = lock_api::MutexGuard<'a, RawFairMutex, T>;
16/// RAII guard for [`FairMutexGuard::map`].
17pub type MappedFairMutexGuard<'a, T> = lock_api::MappedMutexGuard<'a, RawFairMutex, T>;
18
19/// Dependency tracking mutex. See: [`::parking_lot::Mutex`].
20pub type Mutex<T> = lock_api::Mutex<RawMutex, T>;
21/// Mutex guard for [`Mutex`].
22pub type MutexGuard<'a, T> = lock_api::MutexGuard<'a, RawMutex, T>;
23/// RAII guard for [`MutexGuard::map`].
24pub type MappedMutexGuard<'a, T> = lock_api::MappedMutexGuard<'a, RawMutex, T>;
25
26/// Dependency tracking reentrant mutex. See: [`::parking_lot::ReentrantMutex`].
27///
28/// **Note:** due to the way dependencies are tracked, this mutex can only be acquired directly
29/// after itself. Acquiring any other mutex in between introduces a dependency cycle, and will
30/// therefore be rejected.
31pub type ReentrantMutex<T> = lock_api::ReentrantMutex<RawMutex, parking_lot::RawThreadId, T>;
32/// Mutex guard for [`ReentrantMutex`].
33pub type ReentrantMutexGuard<'a, T> =
34    lock_api::ReentrantMutexGuard<'a, RawMutex, parking_lot::RawThreadId, T>;
35/// RAII guard for `ReentrantMutexGuard::map`.
36pub type MappedReentrantMutexGuard<'a, T> =
37    lock_api::MappedReentrantMutexGuard<'a, RawMutex, parking_lot::RawThreadId, T>;
38
39/// Dependency tracking RwLock. See: [`::parking_lot::RwLock`].
40pub type RwLock<T> = lock_api::RwLock<RawRwLock, T>;
41/// Read guard for [`RwLock`].
42pub type RwLockReadGuard<'a, T> = lock_api::RwLockReadGuard<'a, RawRwLock, T>;
43/// Upgradable Read guard for [`RwLock`].
44pub type RwLockUpgradableReadGuard<'a, T> = lock_api::RwLockUpgradableReadGuard<'a, RawRwLock, T>;
45/// Write guard for [`RwLock`].
46pub type RwLockWriteGuard<'a, T> = lock_api::RwLockWriteGuard<'a, RawRwLock, T>;
47/// RAII guard for `RwLockReadGuard::map`.
48pub type MappedRwLockReadGuard<'a, T> = lock_api::MappedRwLockReadGuard<'a, RawRwLock, T>;
49/// RAII guard for `RwLockWriteGuard::map`.
50pub type MappedRwLockWriteGuard<'a, T> = lock_api::MappedRwLockWriteGuard<'a, RawRwLock, T>;
51
52/// A dependency-tracking wrapper for [`::parking_lot::Once`].
53#[derive(Debug, Default)]
54pub struct Once {
55    inner: ::parking_lot::Once,
56    id: LazyMutexId,
57}
58
59impl Once {
60    /// Create a new `Once` value.
61    pub const fn new() -> Self {
62        Self {
63            inner: ::parking_lot::Once::new(),
64            id: LazyMutexId::new(),
65        }
66    }
67
68    /// Returns the current state of this `Once`.
69    pub fn state(&self) -> OnceState {
70        self.inner.state()
71    }
72
73    /// This call is considered as "locking this `Once`" and it participates in dependency
74    /// tracking as such.
75    ///
76    /// # Panics
77    ///
78    /// This method will panic if `f` panics, poisoning this `Once`. In addition, this function
79    /// panics when the lock acquisition order is determined to be inconsistent.
80    pub fn call_once(&self, f: impl FnOnce()) {
81        self.id.with_held(|| self.inner.call_once(f));
82    }
83
84    /// Performs the given initialization routine once and only once.
85    ///
86    /// This method is identical to [`Once::call_once`] except it ignores poisoning.
87    pub fn call_once_force(&self, f: impl FnOnce(OnceState)) {
88        self.id.with_held(|| self.inner.call_once_force(f));
89    }
90}
91
92/// Creates a new fair mutex in an unlocked state ready for use.
93pub const fn const_fair_mutex<T>(val: T) -> FairMutex<T> {
94    FairMutex::const_new(<RawFairMutex as lock_api::RawMutex>::INIT, val)
95}
96
97/// Creates a new mutex in an unlocked state ready for use.
98pub const fn const_mutex<T>(val: T) -> Mutex<T> {
99    Mutex::const_new(<RawMutex as lock_api::RawMutex>::INIT, val)
100}
101
102/// Creates a new reentrant mutex in an unlocked state ready for use.
103pub const fn const_reentrant_mutex<T>(val: T) -> ReentrantMutex<T> {
104    ReentrantMutex::const_new(
105        <RawMutex as lock_api::RawMutex>::INIT,
106        <RawThreadId as lock_api::GetThreadId>::INIT,
107        val,
108    )
109}
110
111/// Creates a new rwlock in an unlocked state ready for use.
112pub const fn const_rwlock<T>(val: T) -> RwLock<T> {
113    RwLock::const_new(<RawRwLock as lock_api::RawRwLock>::INIT, val)
114}
115
116#[cfg(test)]
117mod tests {
118    use std::sync::Arc;
119    use std::thread;
120
121    use super::*;
122
123    #[test]
124    fn test_mutex_usage() {
125        let mutex = Arc::new(Mutex::new(()));
126        let local_lock = mutex.lock();
127        drop(local_lock);
128
129        thread::spawn(move || {
130            let _remote_lock = mutex.lock();
131        })
132        .join()
133        .unwrap();
134    }
135
136    #[test]
137    #[should_panic]
138    fn test_mutex_conflict() {
139        let mutexes = [Mutex::new(()), Mutex::new(()), Mutex::new(())];
140
141        for i in 0..3 {
142            let _first_lock = mutexes[i].lock();
143            let _second_lock = mutexes[(i + 1) % 3].lock();
144        }
145    }
146
147    #[test]
148    fn test_rwlock_usage() {
149        let lock = Arc::new(RwLock::new(()));
150        let lock2 = Arc::clone(&lock);
151
152        let _read_lock = lock.read();
153
154        // Should be able to acquire lock in the background
155        thread::spawn(move || {
156            let _read_lock = lock2.read();
157        })
158        .join()
159        .unwrap();
160    }
161
162    #[test]
163    fn test_rwlock_upgradable_read_usage() {
164        let lock = RwLock::new(());
165
166        // Should be able to acquire an upgradable read lock.
167        let upgradable_guard: RwLockUpgradableReadGuard<'_, _> = lock.upgradable_read();
168
169        // Should be able to upgrade the guard.
170        let _write_guard: RwLockWriteGuard<'_, _> =
171            RwLockUpgradableReadGuard::upgrade(upgradable_guard);
172    }
173
174    #[test]
175    fn test_once_usage() {
176        let once = Arc::new(Once::new());
177        let once_clone = once.clone();
178
179        assert!(!once_clone.state().done());
180
181        let handle = thread::spawn(move || {
182            assert!(!once_clone.state().done());
183
184            once_clone.call_once(|| {});
185
186            assert!(once_clone.state().done());
187        });
188
189        handle.join().unwrap();
190
191        assert!(once.state().done());
192    }
193}