use crate::{InterruptControl, NoInterruptControl};
use core::cell::UnsafeCell;
use core::marker::PhantomData;
use core::sync::atomic::{AtomicBool, Ordering};
const MAX_BACKOFF: usize = 32;
pub(crate) struct Lock<I: InterruptControl = NoInterruptControl> {
locked: AtomicBool,
saved: UnsafeCell<I::State>,
_interrupt: PhantomData<fn() -> I>,
}
impl<I: InterruptControl> Lock<I> {
pub(crate) const fn new() -> Self {
Self {
locked: AtomicBool::new(false),
saved: UnsafeCell::new(I::INIT),
_interrupt: PhantomData,
}
}
#[inline(always)]
pub(crate) fn acquire(&self) {
let state = I::disable();
let mut backoff = 1usize;
loop {
if self
.locked
.compare_exchange_weak(false, true, Ordering::Acquire, Ordering::Relaxed)
.is_ok()
{
unsafe { *self.saved.get() = state };
return;
}
for _ in 0..backoff {
core::hint::spin_loop();
}
backoff = (backoff * 2).min(MAX_BACKOFF);
while self.locked.load(Ordering::Relaxed) {
core::hint::spin_loop();
}
}
}
#[inline(always)]
pub(crate) fn release(&self) {
let state = unsafe { *self.saved.get() };
self.locked.store(false, Ordering::Release);
unsafe { I::restore(state) };
}
}