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 WRITE_LOCKED: u32 = 1 << 30;
const ONE_READER: u32 = 1;
const READERS_MASK: u32 = WRITE_LOCKED - 1;
pub struct NoxuRawRwLock {
pub(crate) state: AtomicU32,
read_waiters: AtomicUsize,
write_waiters: AtomicUsize,
pub(crate) exclusive_owner: AtomicU64,
}
unsafe impl lock_api::RawRwLock for NoxuRawRwLock {
const INIT: Self = NoxuRawRwLock {
state: AtomicU32::new(0),
read_waiters: AtomicUsize::new(0),
write_waiters: AtomicUsize::new(0),
exclusive_owner: AtomicU64::new(0),
};
type GuardMarker = lock_api::GuardSend;
#[inline]
fn lock_shared(&self) {
if !self.try_lock_shared_fast() {
self.lock_shared_slow(None);
}
}
#[inline]
fn try_lock_shared(&self) -> bool {
self.try_lock_shared_fast()
}
#[inline]
unsafe fn unlock_shared(&self) {
let prev = self.state.fetch_sub(ONE_READER, Ordering::Release);
if prev == ONE_READER && self.write_waiters.load(Ordering::Relaxed) > 0
{
futex_wake(&self.state, 1);
}
}
#[inline]
fn lock_exclusive(&self) {
if self
.state
.compare_exchange(
0,
WRITE_LOCKED,
Ordering::Acquire,
Ordering::Relaxed,
)
.is_ok()
{
self.exclusive_owner
.store(crate::raw_mutex::thread_id(), Ordering::Relaxed);
return;
}
self.lock_exclusive_slow(None);
}
#[inline]
fn try_lock_exclusive(&self) -> bool {
if self
.state
.compare_exchange(
0,
WRITE_LOCKED,
Ordering::Acquire,
Ordering::Relaxed,
)
.is_ok()
{
self.exclusive_owner
.store(crate::raw_mutex::thread_id(), Ordering::Relaxed);
true
} else {
false
}
}
#[inline]
unsafe fn unlock_exclusive(&self) {
self.exclusive_owner.store(0, Ordering::Relaxed);
self.state.store(0, Ordering::Release);
if self.write_waiters.load(Ordering::Relaxed) > 0 {
futex_wake(&self.state, 1);
} else if self.read_waiters.load(Ordering::Relaxed) > 0 {
futex_wake(&self.state, i32::MAX as u32);
}
}
#[inline]
fn is_locked(&self) -> bool {
self.state.load(Ordering::Relaxed) != 0
}
#[inline]
fn is_locked_exclusive(&self) -> bool {
self.state.load(Ordering::Relaxed) & WRITE_LOCKED != 0
}
}
unsafe impl lock_api::RawRwLockTimed for NoxuRawRwLock {
type Duration = Duration;
type Instant = Instant;
fn try_lock_shared_for(&self, timeout: Duration) -> bool {
if self.try_lock_shared_fast() {
return true;
}
self.lock_shared_slow(Some(Instant::now() + timeout))
}
fn try_lock_shared_until(&self, deadline: Instant) -> bool {
if self.try_lock_shared_fast() {
return true;
}
self.lock_shared_slow(Some(deadline))
}
fn try_lock_exclusive_for(&self, timeout: Duration) -> bool {
if self
.state
.compare_exchange(
0,
WRITE_LOCKED,
Ordering::Acquire,
Ordering::Relaxed,
)
.is_ok()
{
self.exclusive_owner
.store(crate::raw_mutex::thread_id(), Ordering::Relaxed);
return true;
}
self.lock_exclusive_slow(Some(Instant::now() + timeout))
}
fn try_lock_exclusive_until(&self, deadline: Instant) -> bool {
if self
.state
.compare_exchange(
0,
WRITE_LOCKED,
Ordering::Acquire,
Ordering::Relaxed,
)
.is_ok()
{
self.exclusive_owner
.store(crate::raw_mutex::thread_id(), Ordering::Relaxed);
return true;
}
self.lock_exclusive_slow(Some(deadline))
}
}
impl NoxuRawRwLock {
#[inline]
fn try_lock_shared_fast(&self) -> bool {
let state = self.state.load(Ordering::Relaxed);
if state & WRITE_LOCKED != 0 {
return false;
}
self.state
.compare_exchange(
state,
state + ONE_READER,
Ordering::Acquire,
Ordering::Relaxed,
)
.is_ok()
}
fn lock_shared_slow(&self, deadline: Option<Instant>) -> bool {
loop {
let state = self.state.load(Ordering::Relaxed);
if state & WRITE_LOCKED == 0 {
if self
.state
.compare_exchange_weak(
state,
state + ONE_READER,
Ordering::Acquire,
Ordering::Relaxed,
)
.is_ok()
{
return true;
}
continue;
}
let timeout = match deadline {
Some(dl) => {
let now = Instant::now();
if now >= dl {
return false;
}
Some(dl - now)
}
None => None,
};
self.read_waiters.fetch_add(1, Ordering::Relaxed);
futex_wait(&self.state, state, timeout);
let did_timeout =
deadline.map(|dl| Instant::now() >= dl).unwrap_or(false);
self.read_waiters.fetch_sub(1, Ordering::Relaxed);
if did_timeout {
return false;
}
}
}
fn lock_exclusive_slow(&self, deadline: Option<Instant>) -> bool {
self.write_waiters.fetch_add(1, Ordering::Relaxed);
loop {
let state = self.state.load(Ordering::Relaxed);
if state == 0 {
if self
.state
.compare_exchange_weak(
0,
WRITE_LOCKED,
Ordering::Acquire,
Ordering::Relaxed,
)
.is_ok()
{
self.exclusive_owner.store(
crate::raw_mutex::thread_id(),
Ordering::Relaxed,
);
self.write_waiters.fetch_sub(1, Ordering::Relaxed);
return true;
}
continue;
}
let timeout = match deadline {
Some(dl) => {
let now = Instant::now();
if now >= dl {
self.write_waiters.fetch_sub(1, Ordering::Relaxed);
return false;
}
Some(dl - now)
}
None => None,
};
futex_wait(&self.state, state, timeout);
if deadline.map(|dl| Instant::now() >= dl).unwrap_or(false) {
self.write_waiters.fetch_sub(1, Ordering::Relaxed);
return false;
}
}
}
#[inline]
pub fn is_write_locked(&self) -> bool {
self.state.load(Ordering::Relaxed) & WRITE_LOCKED != 0
}
#[inline]
pub fn get_n_waiters(&self) -> usize {
self.read_waiters.load(Ordering::Relaxed)
+ self.write_waiters.load(Ordering::Relaxed)
}
#[inline]
pub fn reader_count(&self) -> u32 {
self.state.load(Ordering::Relaxed) & READERS_MASK
}
#[inline]
pub fn get_exclusive_owner(&self) -> u64 {
self.exclusive_owner.load(Ordering::Relaxed)
}
}