use core::sync::atomic::Ordering;
use crate::{
condvar::{CondVar, CondVarWake},
park::{DefaultPark, Park, ParkYield, Unpark},
};
const UNLOCKED: u8 = 0;
const MAX_SHARED: u8 = 254;
const EXCLUSIVE_LOCK: u8 = 255;
#[cfg(feature = "std")]
pub type StdRawRwLock = RawRwLock<std::thread::Thread>;
pub type YieldRawRwLock = RawRwLock<ParkYield>;
pub struct RawRwLock<T> {
condvar: CondVar<T>,
}
impl<T> Default for RawRwLock<T> {
#[inline]
fn default() -> Self {
Self::new()
}
}
impl<T> RawRwLock<T> {
#[inline]
pub const fn new() -> Self {
RawRwLock {
condvar: CondVar::zero(),
}
}
#[inline]
pub fn is_locked(&self) -> bool {
self.condvar.load(Ordering::Relaxed) != UNLOCKED
}
#[inline]
pub fn try_lock_exclusive(&self) -> bool {
self.condvar
.optimistic_update(Ordering::Acquire, UNLOCKED, EXCLUSIVE_LOCK)
}
#[inline]
pub fn try_lock_shared(&self) -> bool {
self.condvar
.update_break_no_wake(Ordering::Relaxed, Ordering::Acquire, |state| match state {
EXCLUSIVE_LOCK | MAX_SHARED => None,
readers => Some(readers + 1),
})
.is_ok()
}
}
impl<T> RawRwLock<T>
where
T: Unpark,
{
#[inline]
pub fn lock_shared_park(&self, park: impl Park<T>) {
if self
.condvar
.optimistic_update(Ordering::Acquire, UNLOCKED, 1)
{
return;
}
self.condvar.update_wait_park(
park,
CondVarWake::None,
Ordering::Relaxed,
Ordering::Acquire,
|state| match state {
EXCLUSIVE_LOCK | MAX_SHARED => None,
readers => Some(readers + 1),
},
);
}
#[inline]
pub fn lock_exclusive_park(&self, park: impl Park<T>) {
if self
.condvar
.optimistic_update(Ordering::Acquire, UNLOCKED, EXCLUSIVE_LOCK)
{
return;
}
self.condvar.update_wait_park(
park,
CondVarWake::None,
Ordering::Relaxed,
Ordering::Acquire,
|state| match state {
UNLOCKED => Some(EXCLUSIVE_LOCK),
_ => None,
},
);
}
#[inline]
pub unsafe fn unlock_shared(&self) {
self.condvar.update(
CondVarWake::None,
Ordering::Relaxed,
Ordering::Acquire,
|state| match state {
UNLOCKED => unreachable!("unlock_shared called on unlocked RwLock"),
readers => readers - 1,
},
);
}
#[inline]
pub unsafe fn unlock_exclusive(&self) {
self.condvar.set(CondVarWake::One, Ordering::Release, 0);
}
}
impl<T> RawRwLock<T>
where
T: DefaultPark,
{
#[inline]
pub fn lock_shared(&self) {
if self
.condvar
.optimistic_update(Ordering::Acquire, UNLOCKED, 1)
{
return;
}
self.condvar.update_wait(
CondVarWake::None,
Ordering::Relaxed,
Ordering::Acquire,
|state| match state {
EXCLUSIVE_LOCK | MAX_SHARED => None,
readers => Some(readers + 1),
},
);
}
#[inline]
pub fn lock_exclusive(&self) {
if self
.condvar
.optimistic_update(Ordering::Acquire, UNLOCKED, EXCLUSIVE_LOCK)
{
return;
}
self.condvar.update_wait(
CondVarWake::None,
Ordering::Relaxed,
Ordering::Acquire,
|state| match state {
UNLOCKED => Some(EXCLUSIVE_LOCK),
_ => None,
},
);
}
}
unsafe impl<T> lock_api::RawRwLock for RawRwLock<T>
where
T: DefaultPark,
{
type GuardMarker = lock_api::GuardSend;
const INIT: Self = Self::new();
#[inline]
fn is_locked(&self) -> bool {
self.is_locked()
}
#[inline]
fn try_lock_shared(&self) -> bool {
self.try_lock_shared()
}
#[inline]
fn lock_shared(&self) {
self.lock_shared()
}
#[inline]
unsafe fn unlock_shared(&self) {
self.unlock_shared()
}
#[inline]
fn try_lock_exclusive(&self) -> bool {
self.try_lock_exclusive()
}
#[inline]
fn lock_exclusive(&self) {
self.lock_exclusive()
}
#[inline]
unsafe fn unlock_exclusive(&self) {
self.unlock_exclusive()
}
}