mirror of
https://github.com/bertptrs/tracing-mutex.git
synced 2025-12-25 20:50:32 +01:00
Merge pull request #41 from bertptrs/37-make-parking_lot-api-consistent-so-importing-from-toml-works-seamlessly
Complete `parking_lot` API wrappers
This commit is contained in:
@@ -11,6 +11,10 @@ adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|||||||
- On Rust 1.80 or newer, a wrapper for `std::sync::LazyLock` is now available. The MSRV has not been
|
- On Rust 1.80 or newer, a wrapper for `std::sync::LazyLock` is now available. The MSRV has not been
|
||||||
changed; older versions simply don't get this wrapper.
|
changed; older versions simply don't get this wrapper.
|
||||||
|
|
||||||
|
- Added missing const-initialisation wrappers for `parking_lot`. The API interface for
|
||||||
|
`tracing_mutex::parkinglot` is now identical to `parking_lot`, and an example showing how to use
|
||||||
|
it as a drop-in replacement was added.
|
||||||
|
|
||||||
### Changed
|
### Changed
|
||||||
|
|
||||||
- Reworked CI to better test continued support for the minimum supported Rust version
|
- Reworked CI to better test continued support for the minimum supported Rust version
|
||||||
|
|||||||
@@ -30,6 +30,10 @@ rand = "0.8"
|
|||||||
name = "mutex"
|
name = "mutex"
|
||||||
harness = false
|
harness = false
|
||||||
|
|
||||||
|
[[example]]
|
||||||
|
name = "drop_in_parking_lot"
|
||||||
|
required-features = ["parkinglot"]
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
default = ["backtraces"]
|
default = ["backtraces"]
|
||||||
backtraces = []
|
backtraces = []
|
||||||
|
|||||||
32
examples/drop_in_parking_lot.rs
Normal file
32
examples/drop_in_parking_lot.rs
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
//! This example shows how you can use the [`tracing-mutex`] crate as a drop-in replacement for the
|
||||||
|
//! parking_lot crate. By default, `tracing-mutex` offers a set of type aliases that allows you to
|
||||||
|
//! use cycle-checking in development, and raw primitives in release mode, but this way, you can
|
||||||
|
//! even remove the dependency altogether, or hide it behind a feature.
|
||||||
|
//!
|
||||||
|
//! You can use whatever conditional compilation makes sense in context.
|
||||||
|
use std::sync::Arc;
|
||||||
|
|
||||||
|
#[cfg(not(debug_assertions))]
|
||||||
|
use parking_lot;
|
||||||
|
|
||||||
|
#[cfg(debug_assertions)]
|
||||||
|
// Note: specifically use the `tracing` module, because at this point we are very sure we want to do
|
||||||
|
// deadlock tracing, so no need to use the automatic selection.
|
||||||
|
use tracing_mutex::parkinglot::tracing as parking_lot;
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let mutex = Arc::new(parking_lot::const_mutex(0));
|
||||||
|
|
||||||
|
let handles: Vec<_> = (0..42)
|
||||||
|
.map(|_| {
|
||||||
|
let mutex = Arc::clone(&mutex);
|
||||||
|
std::thread::spawn(move || *mutex.lock() += 1)
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
handles
|
||||||
|
.into_iter()
|
||||||
|
.for_each(|handle| handle.join().unwrap());
|
||||||
|
|
||||||
|
assert_eq!(*mutex.lock(), 42);
|
||||||
|
}
|
||||||
@@ -41,194 +41,26 @@
|
|||||||
|
|
||||||
pub use parking_lot as raw;
|
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)]
|
#[cfg(debug_assertions)]
|
||||||
pub use tracing::{
|
pub use tracing::{
|
||||||
FairMutex, FairMutexGuard, MappedFairMutexGuard, MappedMutexGuard, MappedReentrantMutexGuard,
|
const_fair_mutex, const_mutex, const_reentrant_mutex, const_rwlock, FairMutex, FairMutexGuard,
|
||||||
MappedRwLockReadGuard, MappedRwLockWriteGuard, Mutex, MutexGuard, Once, OnceState,
|
MappedFairMutexGuard, MappedMutexGuard, MappedReentrantMutexGuard, MappedRwLockReadGuard,
|
||||||
|
MappedRwLockWriteGuard, Mutex, MutexGuard, Once, RawFairMutex, RawMutex, RawRwLock,
|
||||||
ReentrantMutex, ReentrantMutexGuard, RwLock, RwLockReadGuard, RwLockUpgradableReadGuard,
|
ReentrantMutex, ReentrantMutexGuard, RwLock, RwLockReadGuard, RwLockUpgradableReadGuard,
|
||||||
RwLockWriteGuard,
|
RwLockWriteGuard,
|
||||||
};
|
};
|
||||||
|
|
||||||
#[cfg(not(debug_assertions))]
|
#[cfg(not(debug_assertions))]
|
||||||
pub use parking_lot::{
|
pub use parking_lot::{
|
||||||
FairMutex, FairMutexGuard, MappedFairMutexGuard, MappedMutexGuard, MappedReentrantMutexGuard,
|
const_fair_mutex, const_mutex, const_reentrant_mutex, const_rwlock, FairMutex, FairMutexGuard,
|
||||||
MappedRwLockReadGuard, MappedRwLockWriteGuard, Mutex, MutexGuard, Once, OnceState,
|
MappedFairMutexGuard, MappedMutexGuard, MappedReentrantMutexGuard, MappedRwLockReadGuard,
|
||||||
|
MappedRwLockWriteGuard, Mutex, MutexGuard, Once, RawFairMutex, RawMutex, RawRwLock,
|
||||||
ReentrantMutex, ReentrantMutexGuard, RwLock, RwLockReadGuard, RwLockUpgradableReadGuard,
|
ReentrantMutex, ReentrantMutexGuard, RwLock, RwLockReadGuard, RwLockUpgradableReadGuard,
|
||||||
RwLockWriteGuard,
|
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<parking_lot::RawFairMutex>;
|
|
||||||
type RawMutex = TracingWrapper<parking_lot::RawMutex>;
|
|
||||||
type RawRwLock = TracingWrapper<parking_lot::RawRwLock>;
|
|
||||||
|
|
||||||
/// Dependency tracking fair mutex. See: [`parking_lot::FairMutex`].
|
|
||||||
pub type FairMutex<T> = lock_api::Mutex<RawFairMutex, T>;
|
|
||||||
/// 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<T> = lock_api::Mutex<RawMutex, T>;
|
|
||||||
/// 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<T> = lock_api::ReentrantMutex<RawMutex, parking_lot::RawThreadId, T>;
|
|
||||||
/// 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<T> = lock_api::RwLock<RawRwLock, T>;
|
|
||||||
/// 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());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|||||||
193
src/parkinglot/tracing.rs
Normal file
193
src/parkinglot/tracing.rs
Normal file
@@ -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<T> = lock_api::Mutex<RawFairMutex, T>;
|
||||||
|
/// 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<T> = lock_api::Mutex<RawMutex, T>;
|
||||||
|
/// 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<T> = lock_api::ReentrantMutex<RawMutex, parking_lot::RawThreadId, T>;
|
||||||
|
/// 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<T> = lock_api::RwLock<RawRwLock, T>;
|
||||||
|
/// 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<T>(val: T) -> FairMutex<T> {
|
||||||
|
FairMutex::const_new(<RawFairMutex as lock_api::RawMutex>::INIT, val)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Creates a new mutex in an unlocked state ready for use.
|
||||||
|
pub const fn const_mutex<T>(val: T) -> Mutex<T> {
|
||||||
|
Mutex::const_new(<RawMutex as lock_api::RawMutex>::INIT, val)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Creates a new reentrant mutex in an unlocked state ready for use.
|
||||||
|
pub const fn const_reentrant_mutex<T>(val: T) -> ReentrantMutex<T> {
|
||||||
|
ReentrantMutex::const_new(
|
||||||
|
<RawMutex as lock_api::RawMutex>::INIT,
|
||||||
|
<RawThreadId as lock_api::GetThreadId>::INIT,
|
||||||
|
val,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Creates a new rwlock in an unlocked state ready for use.
|
||||||
|
pub const fn const_rwlock<T>(val: T) -> RwLock<T> {
|
||||||
|
RwLock::const_new(<RawRwLock as lock_api::RawRwLock>::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());
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user