use crate::sync::atomic::{AtomicIsize, AtomicU32, Ordering};
const MAX_HARTS: usize = crate::config::MAX_HARTS;
#[cfg(not(loom))]
static NESTING: [AtomicU32; MAX_HARTS] = [const { AtomicU32::new(0) }; MAX_HARTS];
#[cfg(loom)]
loom::lazy_static! {
static ref NESTING: [AtomicU32; MAX_HARTS] = core::array::from_fn(|_| AtomicU32::new(0));
}
#[cfg(not(loom))]
static LOCK_OWNER: AtomicIsize = AtomicIsize::new(-1);
#[cfg(loom)]
loom::lazy_static! {
static ref LOCK_OWNER: AtomicIsize = AtomicIsize::new(-1);
}
#[inline]
pub fn enter<F, R>(f: F) -> R
where
F: FnOnce() -> R,
{
enter_locked(f)
}
fn enter_locked<F, R>(f: F) -> R
where
F: FnOnce() -> R,
{
crate::port::arch::critical_section(|| {
#[cfg(feature = "latency-histograms")]
let start = crate::port::arch::cycle_count();
let hart = crate::port::arch::hart_id();
let depth = NESTING[hart].load(Ordering::Relaxed);
if depth == 0 {
while LOCK_OWNER
.compare_exchange_weak(-1, hart as isize, Ordering::Acquire, Ordering::Relaxed)
.is_err()
{
core::hint::spin_loop();
}
}
NESTING[hart].store(depth + 1, Ordering::Relaxed);
let r = f();
NESTING[hart].store(depth, Ordering::Relaxed);
if depth == 0 {
LOCK_OWNER.store(-1, Ordering::Release);
}
#[cfg(feature = "latency-histograms")]
crate::latency::record(
crate::latency::Kind::CriticalSection,
crate::port::arch::cycle_count().wrapping_sub(start),
);
r
})
}
#[cfg(all(test, not(loom)))]
mod tests {
use super::*;
#[test]
fn nested_enter_does_not_deadlock() {
let r = enter(|| enter(|| enter(|| 42)));
assert_eq!(r, 42);
}
#[test]
fn lock_is_released_after_outermost_exit() {
enter(|| {});
assert_eq!(LOCK_OWNER.load(Ordering::Acquire), -1);
assert_eq!(NESTING[crate::port::arch::hart_id()].load(Ordering::Relaxed), 0);
}
}