#[cfg(debug_assertions)]
use std::{cell::RefCell, thread_local};
use std::{ops::{Deref, DerefMut}, sync::PoisonError};
#[cfg(debug_assertions)]
thread_local! {
pub static LOCK_LEVELS: RefCell<Vec<u32>> = RefCell::new(Vec::new());
}
pub struct Mutex<T> {
#[cfg(debug_assertions)]
level: u32,
inner: std::sync::Mutex<T>,
}
impl<T> Mutex<T> {
pub fn new(t: T) -> Self {
Self::with_level(t, 0)
}
pub fn with_level(t: T, level: u32) -> Self {
#[cfg(not(debug_assertions))]
let _ = level;
Mutex {
#[cfg(debug_assertions)]
level,
inner: std::sync::Mutex::new(t),
}
}
pub fn lock(&self) -> Result<MutexGuard<T>, PoisonError<std::sync::MutexGuard<'_, T>>> {
#[cfg(debug_assertions)]
LOCK_LEVELS.with(|levels| {
let mut levels = levels.borrow_mut();
if let Some(&lowest) = levels.last() {
assert!(lowest > self.level)
}
levels.push(self.level);
});
self.inner.lock().map(|guard| MutexGuard {
#[cfg(debug_assertions)]
level: self.level,
inner: guard,
})
}
}
pub struct MutexGuard<'a, T> {
#[cfg(debug_assertions)]
level: u32,
inner: std::sync::MutexGuard<'a, T>,
}
impl<T> Drop for MutexGuard<'_, T> {
fn drop(&mut self) {
#[cfg(debug_assertions)]
LOCK_LEVELS.with(|levels| {
let mut levels = levels.borrow_mut();
let index = levels
.iter()
.rposition(|&level| level == self.level)
.expect("Position must exist, because we inserted it during lock!");
levels.remove(index);
});
}
}
impl<'a, T> Deref for MutexGuard<'a, T> {
type Target = T;
fn deref(&self) -> &T {
self.inner.deref()
}
}
impl<'a, T> DerefMut for MutexGuard<'a, T> {
fn deref_mut(&mut self) -> &mut Self::Target {
self.inner.deref_mut()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn acquire_resource() {
let mutex = Mutex::new(42);
let guard = mutex.lock().unwrap();
assert_eq!(42, *guard)
}
#[test]
fn should_allow_mutation() {
let mutex = Mutex::new(42);
let mut guard = mutex.lock().unwrap();
*guard = 43;
assert_eq!(43, *guard)
}
#[test]
#[cfg(debug_assertions)]
#[should_panic]
fn should_panic_if_two_mutices_with_level_0_are_acquired() {
let mutex_a = Mutex::new(()); let mutex_b = Mutex::new(()); let _guard_a = mutex_a.lock().unwrap();
let _guard_b = mutex_b.lock().unwrap();
}
#[test]
#[cfg(not(debug_assertions))]
fn should_not_check_in_release_build() {
let mutex_a = Mutex::new(5); let mutex_b = Mutex::new(42); let _guard_a = mutex_a.lock().unwrap();
let _guard_b = mutex_b.lock().unwrap();
}
#[test]
fn should_allow_for_two_level_0_in_succession() {
let mutex_a = Mutex::new(5); let mutex_b = Mutex::new(42); let guard_a = mutex_a.lock().unwrap();
drop(guard_a);
let _guard_b = mutex_b.lock().unwrap();
}
#[test]
fn should_allow_for_simultanous_lock_if_higher_is_acquired_first() {
let mutex_a = Mutex::with_level(5, 1); let mutex_b = Mutex::new(42); let _guard_a = mutex_a.lock().unwrap();
let _guard_b = mutex_b.lock().unwrap();
}
#[test]
fn should_allow_for_any_order_of_release() {
let mutex_a = Mutex::with_level((), 2);
let mutex_b = Mutex::with_level((), 1);
let mutex_c = Mutex::new(());
let _guard_a = mutex_a.lock().unwrap();
let guard_b = mutex_b.lock().unwrap();
let _guard_c = mutex_c.lock().unwrap();
drop(guard_b)
}
}