use super::wait_wake::{futex_wait, futex_wake};
use core::sync::atomic::{
AtomicU32,
Ordering::{Acquire, Relaxed, Release},
};
pub type MovableMutex = Mutex;
pub struct Mutex {
futex: AtomicU32,
}
impl Mutex {
#[inline]
pub const fn new() -> Self {
Self {
futex: AtomicU32::new(0),
}
}
#[inline]
pub unsafe fn try_lock(&self) -> bool {
self.futex.compare_exchange(0, 1, Acquire, Relaxed).is_ok()
}
#[inline]
pub unsafe fn lock(&self) {
if self.futex.compare_exchange(0, 1, Acquire, Relaxed).is_err() {
self.lock_contended();
}
}
#[cold]
fn lock_contended(&self) {
let mut state = self.spin();
if state == 0 {
match self.futex.compare_exchange(0, 1, Acquire, Relaxed) {
Ok(_) => return, Err(s) => state = s,
}
}
loop {
if state != 2 && self.futex.swap(2, Acquire) == 0 {
return;
}
futex_wait(&self.futex, 2, None);
state = self.spin();
}
}
fn spin(&self) -> u32 {
let mut spin = 100;
loop {
let state = self.futex.load(Relaxed);
if state != 1 || spin == 0 {
return state;
}
core::hint::spin_loop();
spin -= 1;
}
}
#[inline]
pub unsafe fn unlock(&self) {
if self.futex.swap(0, Release) == 2 {
self.wake();
}
}
#[cold]
fn wake(&self) {
futex_wake(&self.futex);
}
}