use crate::futex::{futex_wait, futex_wake};
use lock_api;
use std::sync::atomic::{AtomicU32, AtomicU64, AtomicUsize, Ordering};
use std::time::{Duration, Instant};
pub(crate) const UNLOCKED: u32 = 0;
const LOCKED: u32 = 1;
const LOCKED_CONTENDED: u32 = 2;
const SPIN_LIMIT: usize = 40;
pub(crate) fn thread_id() -> u64 {
use std::hash::{Hash, Hasher};
let mut hasher = std::collections::hash_map::DefaultHasher::new();
std::thread::current().id().hash(&mut hasher);
hasher.finish() | 1
}
pub struct NoxuRawMutex {
pub(crate) state: AtomicU32,
pub(crate) waiters: AtomicUsize,
pub(crate) owner: AtomicU64,
}
unsafe impl lock_api::RawMutex for NoxuRawMutex {
const INIT: Self = NoxuRawMutex {
state: AtomicU32::new(UNLOCKED),
waiters: AtomicUsize::new(0),
owner: AtomicU64::new(0),
};
type GuardMarker = lock_api::GuardSend;
#[inline]
fn lock(&self) {
if self
.state
.compare_exchange(
UNLOCKED,
LOCKED,
Ordering::Acquire,
Ordering::Relaxed,
)
.is_ok()
{
self.owner.store(thread_id(), Ordering::Relaxed);
return;
}
self.lock_slow(None);
}
#[inline]
fn try_lock(&self) -> bool {
if self
.state
.compare_exchange(
UNLOCKED,
LOCKED,
Ordering::Acquire,
Ordering::Relaxed,
)
.is_ok()
{
self.owner.store(thread_id(), Ordering::Relaxed);
true
} else {
false
}
}
#[inline]
unsafe fn unlock(&self) {
self.owner.store(0, Ordering::Relaxed);
let prev = self.state.swap(UNLOCKED, Ordering::Release);
if prev == LOCKED_CONTENDED {
futex_wake(&self.state, 1);
}
}
#[inline]
fn is_locked(&self) -> bool {
self.state.load(Ordering::Relaxed) != UNLOCKED
}
}
unsafe impl lock_api::RawMutexTimed for NoxuRawMutex {
type Duration = Duration;
type Instant = Instant;
#[inline]
fn try_lock_for(&self, timeout: Duration) -> bool {
if self
.state
.compare_exchange(
UNLOCKED,
LOCKED,
Ordering::Acquire,
Ordering::Relaxed,
)
.is_ok()
{
self.owner.store(thread_id(), Ordering::Relaxed);
return true;
}
self.lock_slow(Some(Instant::now() + timeout))
}
#[inline]
fn try_lock_until(&self, deadline: Instant) -> bool {
if self
.state
.compare_exchange(
UNLOCKED,
LOCKED,
Ordering::Acquire,
Ordering::Relaxed,
)
.is_ok()
{
self.owner.store(thread_id(), Ordering::Relaxed);
return true;
}
self.lock_slow(Some(deadline))
}
}
impl NoxuRawMutex {
fn lock_slow(&self, deadline: Option<Instant>) -> bool {
let mut spin = 0usize;
loop {
let state = self.state.load(Ordering::Relaxed);
if state == UNLOCKED {
if self
.state
.compare_exchange_weak(
UNLOCKED,
LOCKED_CONTENDED,
Ordering::Acquire,
Ordering::Relaxed,
)
.is_ok()
{
self.owner.store(thread_id(), Ordering::Relaxed);
return true;
}
continue;
}
if spin < SPIN_LIMIT {
spin += 1;
std::hint::spin_loop();
continue;
}
if state == LOCKED {
if self
.state
.compare_exchange_weak(
LOCKED,
LOCKED_CONTENDED,
Ordering::Relaxed,
Ordering::Relaxed,
)
.is_err()
{
continue;
}
}
let timeout = match deadline {
Some(dl) => {
let now = Instant::now();
if now >= dl {
return false;
}
Some(dl - now)
}
None => None,
};
self.waiters.fetch_add(1, Ordering::Relaxed);
let woke = futex_wait(&self.state, LOCKED_CONTENDED, timeout);
self.waiters.fetch_sub(1, Ordering::Relaxed);
if !woke {
return false;
}
spin = 0;
}
}
#[inline]
pub fn get_n_waiters(&self) -> usize {
self.waiters.load(Ordering::Relaxed)
}
#[inline]
pub fn get_owner(&self) -> u64 {
self.owner.load(Ordering::Relaxed)
}
}