#![allow(unsafe_code)]
use core::fmt;
use core::future::Future;
use core::ops::Deref;
use core::pin::Pin;
use core::ptr;
use core::task::{Context, Poll};
use super::internal::{AtomicPtr, AtomicU64, AtomicUsize, Ordering, UnsafeCell};
use super::owner::Owner;
use super::raw_rwlock::{READ, RawRwLock, RwWait, WRITE};
struct Record {
owner: AtomicU64,
reads: AtomicUsize,
next: *mut Record,
}
struct Records {
head: AtomicPtr<Record>,
}
impl Records {
loom_const_fn! {
fn new() -> Self {
Self { head: AtomicPtr::new(ptr::null_mut()) }
}
}
fn iter(&self) -> impl Iterator<Item = &Record> {
let mut cursor = self.head.load(Ordering::Acquire);
core::iter::from_fn(move || {
let record = unsafe { cursor.as_ref() }?;
cursor = record.next;
Some(record)
})
}
fn find(&self, id: u64) -> Option<&Record> {
self.iter()
.find(|record| record.owner.load(Ordering::Relaxed) == id)
}
fn claim(&self, id: u64) -> &Record {
if let Some(record) = self.iter().find(|record| {
record.owner.load(Ordering::Relaxed) == 0
&& record
.owner
.compare_exchange(0, id, Ordering::Acquire, Ordering::Relaxed)
.is_ok()
}) {
return record;
}
let fresh = Box::into_raw(Box::new(Record {
owner: AtomicU64::new(id),
reads: AtomicUsize::new(0),
next: ptr::null_mut(),
}));
let mut head = self.head.load(Ordering::Relaxed);
loop {
unsafe { (*fresh).next = head };
match self
.head
.compare_exchange_weak(head, fresh, Ordering::Release, Ordering::Relaxed)
{
Ok(_) => return unsafe { &*fresh },
Err(actual) => head = actual,
}
}
}
}
impl Drop for Records {
fn drop(&mut self) {
let mut cursor = self.head.load(Ordering::Acquire);
while !cursor.is_null() {
let record = unsafe { Box::from_raw(cursor) };
cursor = record.next;
}
}
}
pub struct ReentrantRwLock<T: ?Sized> {
raw: RawRwLock,
writer: AtomicU64,
write_depth: UnsafeCell<usize>,
readers: Records,
value: core::cell::UnsafeCell<T>,
}
unsafe impl<T: ?Sized + Send> Send for ReentrantRwLock<T> {}
unsafe impl<T: ?Sized + Send + Sync> Sync for ReentrantRwLock<T> {}
impl<T> ReentrantRwLock<T> {
loom_const_fn! {
pub fn new(value: T) -> Self {
Self {
raw: RawRwLock::new(),
writer: AtomicU64::new(0),
write_depth: UnsafeCell::new(0),
readers: Records::new(),
value: core::cell::UnsafeCell::new(value),
}
}
}
pub fn into_inner(self) -> T {
self.value.into_inner()
}
}
impl<T: ?Sized> ReentrantRwLock<T> {
pub fn try_read<'a>(&'a self, owner: &'a Owner) -> Option<ReentrantReadGuard<'a, T>> {
let record = match self.reenter_read(owner) {
Some(record) => record,
None if self.raw.try_kind(READ) => self.enter_read(owner),
None => return None,
};
Some(ReentrantReadGuard::new(self, owner, record))
}
pub fn try_write<'a>(&'a self, owner: &'a Owner) -> Option<ReentrantWriteGuard<'a, T>> {
if self.reenter_write(owner) {
return Some(ReentrantWriteGuard::new(self, owner));
}
(self.readers.find(owner.id()).is_none() && self.raw.try_kind(WRITE)).then(|| {
self.enter_write(owner);
ReentrantWriteGuard::new(self, owner)
})
}
pub fn read<'a>(&'a self, owner: &'a Owner) -> ReentrantRead<'a, T> {
ReentrantRead {
lock: self,
owner,
wait: RwWait::new(),
}
}
pub fn write<'a>(&'a self, owner: &'a Owner) -> ReentrantWrite<'a, T> {
ReentrantWrite {
lock: self,
owner,
wait: RwWait::new(),
}
}
pub fn get_mut(&mut self) -> &mut T {
self.value.get_mut()
}
fn reenter_read(&self, owner: &Owner) -> Option<&Record> {
let id = owner.id();
if let Some(record) = self.readers.find(id) {
record.reads.fetch_add(1, Ordering::Relaxed);
return Some(record);
}
(self.writer.load(Ordering::Relaxed) == id).then(|| self.enter_read(owner))
}
fn enter_read(&self, owner: &Owner) -> &Record {
let record = self.readers.claim(owner.id());
record.reads.store(1, Ordering::Relaxed);
record
}
fn read_unlock(&self, id: u64, record: &Record) {
if record.reads.fetch_sub(1, Ordering::Relaxed) == 1 {
record.owner.store(0, Ordering::Release);
if self.writer.load(Ordering::Relaxed) != id {
self.raw.read_unlock();
}
}
}
#[expect(
clippy::panic,
reason = "a count that cannot overflow in practice, checked rather than wrapped"
)]
fn reenter_write(&self, owner: &Owner) -> bool {
if self.writer.load(Ordering::Relaxed) != owner.id() {
return false;
}
self.write_depth.with_mut(|depth| unsafe {
*depth = (*depth)
.checked_add(1)
.unwrap_or_else(|| panic!("a ReentrantRwLock was write-locked usize::MAX times"));
});
true
}
fn enter_write(&self, owner: &Owner) {
self.write_depth.with_mut(|depth| unsafe { *depth = 1 });
self.writer.store(owner.id(), Ordering::Relaxed);
}
fn write_unlock(&self, id: u64) {
let left = self.write_depth.with_mut(|depth| unsafe {
*depth -= 1;
*depth
});
if left == 0 {
self.writer.store(0, Ordering::Relaxed);
if self.readers.find(id).is_some() {
self.raw.downgrade();
} else {
self.raw.write_unlock();
}
}
}
}
impl<T: Default> Default for ReentrantRwLock<T> {
fn default() -> Self {
Self::new(T::default())
}
}
impl<T: ?Sized> fmt::Debug for ReentrantRwLock<T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("ReentrantRwLock").finish_non_exhaustive()
}
}
#[must_use = "dropping the guard releases one read at once"]
pub struct ReentrantReadGuard<'a, T: ?Sized> {
lock: &'a ReentrantRwLock<T>,
owner: &'a Owner,
record: &'a Record,
}
impl<'a, T: ?Sized> ReentrantReadGuard<'a, T> {
fn new(lock: &'a ReentrantRwLock<T>, owner: &'a Owner, record: &'a Record) -> Self {
Self {
lock,
owner,
record,
}
}
}
impl<T: ?Sized> Deref for ReentrantReadGuard<'_, T> {
type Target = T;
fn deref(&self) -> &T {
unsafe { &*self.lock.value.get() }
}
}
impl<T: ?Sized> Drop for ReentrantReadGuard<'_, T> {
fn drop(&mut self) {
self.lock.read_unlock(self.owner.id(), self.record);
}
}
impl<T: ?Sized + fmt::Debug> fmt::Debug for ReentrantReadGuard<'_, T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt::Debug::fmt(&**self, f)
}
}
#[must_use = "dropping the guard releases one write at once"]
pub struct ReentrantWriteGuard<'a, T: ?Sized> {
lock: &'a ReentrantRwLock<T>,
owner: &'a Owner,
}
impl<'a, T: ?Sized> ReentrantWriteGuard<'a, T> {
fn new(lock: &'a ReentrantRwLock<T>, owner: &'a Owner) -> Self {
Self { lock, owner }
}
}
impl<T: ?Sized> Deref for ReentrantWriteGuard<'_, T> {
type Target = T;
fn deref(&self) -> &T {
unsafe { &*self.lock.value.get() }
}
}
impl<T: ?Sized> Drop for ReentrantWriteGuard<'_, T> {
fn drop(&mut self) {
self.lock.write_unlock(self.owner.id());
}
}
impl<T: ?Sized + fmt::Debug> fmt::Debug for ReentrantWriteGuard<'_, T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt::Debug::fmt(&**self, f)
}
}
#[must_use = "futures do nothing unless polled"]
pub struct ReentrantRead<'a, T: ?Sized> {
lock: &'a ReentrantRwLock<T>,
owner: &'a Owner,
wait: RwWait,
}
impl<'a, T: ?Sized> Future for ReentrantRead<'a, T> {
type Output = ReentrantReadGuard<'a, T>;
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<ReentrantReadGuard<'a, T>> {
let this = self.get_mut();
if !this.wait.is_queued()
&& let Some(record) = this.lock.reenter_read(this.owner)
{
return Poll::Ready(ReentrantReadGuard::new(this.lock, this.owner, record));
}
this.wait.poll(&this.lock.raw, READ, cx.waker()).map(|()| {
let record = this.lock.enter_read(this.owner);
ReentrantReadGuard::new(this.lock, this.owner, record)
})
}
}
impl<T: ?Sized> Drop for ReentrantRead<'_, T> {
fn drop(&mut self) {
self.wait.cancel(&self.lock.raw, READ);
}
}
impl<T: ?Sized> fmt::Debug for ReentrantRead<'_, T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("ReentrantRead")
.field("owner", self.owner)
.finish_non_exhaustive()
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
#[non_exhaustive]
pub struct ReadHeldByOwner;
impl fmt::Display for ReadHeldByOwner {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str("this owner holds a read of the lock; release it before writing")
}
}
impl std::error::Error for ReadHeldByOwner {}
#[must_use = "futures do nothing unless polled"]
pub struct ReentrantWrite<'a, T: ?Sized> {
lock: &'a ReentrantRwLock<T>,
owner: &'a Owner,
wait: RwWait,
}
impl<'a, T: ?Sized> Future for ReentrantWrite<'a, T> {
type Output = Result<ReentrantWriteGuard<'a, T>, ReadHeldByOwner>;
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Self::Output> {
let this = self.get_mut();
if !this.wait.is_queued() {
if this.lock.reenter_write(this.owner) {
return Poll::Ready(Ok(ReentrantWriteGuard::new(this.lock, this.owner)));
}
if this.lock.readers.find(this.owner.id()).is_some() {
return Poll::Ready(Err(ReadHeldByOwner));
}
}
this.wait.poll(&this.lock.raw, WRITE, cx.waker()).map(|()| {
this.lock.enter_write(this.owner);
Ok(ReentrantWriteGuard::new(this.lock, this.owner))
})
}
}
impl<T: ?Sized> Drop for ReentrantWrite<'_, T> {
fn drop(&mut self) {
self.wait.cancel(&self.lock.raw, WRITE);
}
}
impl<T: ?Sized> fmt::Debug for ReentrantWrite<'_, T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("ReentrantWrite")
.field("owner", self.owner)
.finish_non_exhaustive()
}
}
#[cfg(all(test, not(loom)))]
mod tests {
use super::*;
use crate::sync::MAX_BYPASS;
use crate::sync::test_support::Polled;
#[test]
fn a_reader_reads_again_while_a_writer_waits() {
let lock = ReentrantRwLock::new(());
let (me, writer) = (Owner::new(), Owner::new());
let first = lock.try_read(&me).expect("free");
let mut write = Polled::new(lock.write(&writer));
write.pending();
let second = Polled::new(lock.read(&me)).ready();
drop(first);
drop(second);
assert!(write.wakes() >= 1, "the last read out nudges the writer");
let held = write.ready().expect("the writer holds no read");
assert!(lock.try_read(&Owner::new()).is_none());
drop(held);
}
#[test]
fn an_owned_read_never_waits_behind_an_owed_writer() {
let lock = ReentrantRwLock::new(());
let (me, writer) = (Owner::new(), Owner::new());
let others: Vec<Owner> = (0..=MAX_BYPASS).map(|_| Owner::new()).collect();
let mine = lock.try_read(&me).expect("free");
let mut write = Polled::new(lock.write(&writer));
write.pending();
let mut other = lock.try_read(&others[0]).expect("barge");
for owner in &others[1..] {
let next = lock.try_read(owner).expect("a new owner barges");
drop(other);
other = next;
write.pending();
}
assert!(lock.try_read(&Owner::new()).is_none(), "the writer is owed");
let again = Polled::new(lock.read(&me)).ready();
drop((other, again, mine));
assert!(write.ready().is_ok());
}
#[test]
fn a_writer_reads_and_keeps_its_read_after_the_write() {
let lock = ReentrantRwLock::new(());
let (me, other) = (Owner::new(), Owner::new());
let write = lock.try_write(&me).expect("free");
let nested = lock.try_write(&me).expect("the writer re-enters");
let read = lock.try_read(&me).expect("a writer may also read");
drop((nested, write));
assert!(
lock.try_write(&other).is_none(),
"the read survives the write"
);
let shared = lock.try_read(&other).expect("the write became a read");
drop((read, shared));
assert!(lock.try_write(&other).is_some());
}
#[test]
fn a_reader_asking_to_write_is_refused_rather_than_deadlocked() {
let lock = ReentrantRwLock::new(());
let me = Owner::new();
let read = lock.try_read(&me).expect("free");
assert!(lock.try_write(&me).is_none());
assert_eq!(
Polled::new(lock.write(&me)).ready().err(),
Some(ReadHeldByOwner)
);
drop(read);
assert!(Polled::new(lock.write(&me)).ready().is_ok());
}
}