const DEADLOCK_DURATION: std::time::Duration = std::time::Duration::from_secs(10);
#[derive(Default)]
pub struct Mutex<T>(parking_lot::Mutex<T>);
pub use parking_lot::MutexGuard;
impl<T> Mutex<T> {
#[inline(always)]
pub fn new(val: T) -> Self {
Self(parking_lot::Mutex::new(val))
}
#[inline(always)]
#[cfg_attr(debug_assertions, track_caller)]
pub fn lock(&self) -> MutexGuard<'_, T> {
if cfg!(debug_assertions) {
self.0.try_lock_for(DEADLOCK_DURATION).unwrap_or_else(|| {
panic!(
"DEBUG PANIC: Failed to acquire Mutex after {}s. Deadlock?",
DEADLOCK_DURATION.as_secs()
)
})
} else {
self.0.lock()
}
}
}
pub use parking_lot::MappedRwLockReadGuard as RwLockReadGuard;
pub use parking_lot::MappedRwLockWriteGuard as RwLockWriteGuard;
#[derive(Default)]
pub struct RwLock<T: ?Sized>(parking_lot::RwLock<T>);
impl<T> RwLock<T> {
#[inline(always)]
pub fn new(val: T) -> Self {
Self(parking_lot::RwLock::new(val))
}
}
impl<T: ?Sized> RwLock<T> {
#[inline(always)]
#[cfg_attr(debug_assertions, track_caller)]
pub fn read(&self) -> RwLockReadGuard<'_, T> {
let guard = if cfg!(debug_assertions) {
self.0.try_read_for(DEADLOCK_DURATION).unwrap_or_else(|| {
panic!(
"DEBUG PANIC: Failed to acquire RwLock read after {}s. Deadlock?",
DEADLOCK_DURATION.as_secs()
)
})
} else {
self.0.read()
};
parking_lot::RwLockReadGuard::map(guard, |v| v)
}
#[inline(always)]
#[cfg_attr(debug_assertions, track_caller)]
pub fn write(&self) -> RwLockWriteGuard<'_, T> {
let guard = if cfg!(debug_assertions) {
self.0.try_write_for(DEADLOCK_DURATION).unwrap_or_else(|| {
panic!(
"DEBUG PANIC: Failed to acquire RwLock write after {}s. Deadlock?",
DEADLOCK_DURATION.as_secs()
)
})
} else {
self.0.write()
};
parking_lot::RwLockWriteGuard::map(guard, |v| v)
}
}
impl<T> Clone for Mutex<T>
where
T: Clone,
{
fn clone(&self) -> Self {
Self::new(self.lock().clone())
}
}
#[cfg(test)]
mod tests {
#![allow(clippy::disallowed_methods)]
use crate::mutex::Mutex;
use std::time::Duration;
#[test]
fn lock_two_different_mutexes_single_thread() {
let one = Mutex::new(());
let two = Mutex::new(());
let _a = one.lock();
let _b = two.lock();
}
#[test]
fn lock_multiple_threads() {
use std::sync::Arc;
let one = Arc::new(Mutex::new(()));
let our_lock = one.lock();
let other_thread = {
let one = Arc::clone(&one);
std::thread::spawn(move || {
let _lock = one.lock();
})
};
std::thread::sleep(Duration::from_millis(200));
drop(our_lock);
other_thread.join().unwrap();
}
}
#[cfg(not(target_arch = "wasm32"))]
#[cfg(test)]
mod tests_rwlock {
#![allow(clippy::disallowed_methods)]
use crate::mutex::RwLock;
use std::time::Duration;
#[test]
fn lock_two_different_rwlocks_single_thread() {
let one = RwLock::new(());
let two = RwLock::new(());
let _a = one.write();
let _b = two.write();
}
#[test]
fn rwlock_multiple_threads() {
use std::sync::Arc;
let one = Arc::new(RwLock::new(()));
let our_lock = one.write();
let other_thread1 = {
let one = Arc::clone(&one);
std::thread::spawn(move || {
let _ = one.write();
})
};
let other_thread2 = {
let one = Arc::clone(&one);
std::thread::spawn(move || {
let _ = one.read();
})
};
std::thread::sleep(Duration::from_millis(200));
drop(our_lock);
other_thread1.join().unwrap();
other_thread2.join().unwrap();
}
#[test]
#[should_panic]
fn rwlock_write_write_reentrancy() {
let one = RwLock::new(());
let _a1 = one.write();
let _a2 = one.write(); }
#[test]
#[should_panic]
fn rwlock_write_read_reentrancy() {
let one = RwLock::new(());
let _a1 = one.write();
let _a2 = one.read(); }
#[test]
#[should_panic]
fn rwlock_read_write_reentrancy() {
let one = RwLock::new(());
let _a1 = one.read();
let _a2 = one.write(); }
#[test]
fn rwlock_read_read_reentrancy() {
let one = RwLock::new(());
let _a1 = one.read();
let _a2 = one.read();
}
#[test]
fn rwlock_short_read_foreign_read_write_reentrancy() {
use std::sync::Arc;
let lock = Arc::new(RwLock::new(()));
let t0r0 = lock.read();
let other_thread = {
let lock = Arc::clone(&lock);
std::thread::spawn(move || {
let _t1r0 = lock.read();
})
};
other_thread.join().unwrap();
drop(t0r0);
let _t0w0 = lock.write();
}
#[test]
#[should_panic]
fn rwlock_read_foreign_read_write_reentrancy() {
use std::sync::Arc;
let lock = Arc::new(RwLock::new(()));
let _t0r0 = lock.read();
let other_thread = {
let lock = Arc::clone(&lock);
std::thread::spawn(move || {
let _t1r0 = lock.read();
})
};
other_thread.join().unwrap();
let _t0w0 = lock.write(); }
}