use std::mem::MaybeUninit;
#[cfg(not(loom))]
use std::{
cell::Cell,
sync::{Mutex, MutexGuard},
thread_local,
};
#[cfg(loom)]
use loom::{
cell::Cell,
sync::{Mutex, MutexGuard},
thread_local,
};
#[cfg(not(loom))]
static GLOBAL_MUTEX: Mutex<()> = Mutex::new(());
#[cfg(loom)]
loom::lazy_static! {
static ref GLOBAL_MUTEX: Mutex<()> = Mutex::new(());
}
static mut GLOBAL_GUARD: MaybeUninit<MutexGuard<'static, ()>> = MaybeUninit::uninit();
thread_local!(static IS_LOCKED: Cell<bool> = Cell::new(false));
struct StdCriticalSection;
crate::set_impl!(StdCriticalSection);
unsafe impl crate::Impl for StdCriticalSection {
unsafe fn acquire() -> bool {
IS_LOCKED.with(|l| {
if l.get() {
return true;
}
l.set(true);
let guard = match GLOBAL_MUTEX.lock() {
Ok(guard) => guard,
Err(err) => {
err.into_inner()
}
};
GLOBAL_GUARD.write(guard);
false
})
}
unsafe fn release(nested_cs: bool) {
if !nested_cs {
#[allow(let_underscore_lock)]
let _ = GLOBAL_GUARD.assume_init_read();
IS_LOCKED.with(|l| l.set(false));
}
}
}
#[cfg(test)]
#[cfg(not(loom))]
mod tests {
use std::thread;
use crate as critical_section;
#[cfg(feature = "std")]
#[test]
#[should_panic(expected = "Not a PoisonError!")]
fn reusable_after_panic() {
let _ = thread::spawn(|| {
critical_section::with(|_| {
panic!("Boom!");
})
})
.join();
critical_section::with(|_| {
panic!("Not a PoisonError!");
})
}
}
#[cfg(test)]
#[cfg(loom)]
mod tests {
use crate as critical_section;
#[cfg(feature = "std")]
#[test]
#[should_panic(expected = "Not a PoisonError!")]
fn reusable_after_panic_loom() {
loom::model(|| {
let _ = std::thread::spawn(|| {
critical_section::with(|_| {
panic!("Boom!");
});
})
.join();
critical_section::with(|_| {
panic!("Not a PoisonError!");
})
})
}
}