#![allow(unsafe_code)]
use core::fmt;
use core::future::Future;
use core::ops::Deref;
use core::pin::Pin;
use core::task::{Context, Poll};
use std::sync::Arc;
use super::internal;
use super::mutex::TryLockError;
use super::raw_rwlock::{
MAX_READERS, READ, RawRwLock, RwWait, UPGRADABLE_READ, UpgradeWait, WRITE,
};
const MAX_READS: usize = if (u32::MAX >> 3) as usize <= MAX_READERS {
(u32::MAX >> 3) as usize
} else {
MAX_READERS
};
pub struct RwLock<T: ?Sized> {
raw: RawRwLock,
value: core::cell::UnsafeCell<T>,
}
unsafe impl<T: ?Sized + Send> Send for RwLock<T> {}
unsafe impl<T: ?Sized + Send + Sync> Sync for RwLock<T> {}
impl<T> RwLock<T> {
loom_const_fn! {
pub fn new(value: T) -> Self {
Self { raw: RawRwLock::new(), value: core::cell::UnsafeCell::new(value) }
}
}
loom_const_fn! {
pub fn const_new(value: T) -> Self {
Self { raw: RawRwLock::new(), value: core::cell::UnsafeCell::new(value) }
}
}
loom_const_fn! {
pub fn with_max_readers(value: T, max_reads: u32) -> Self {
assert!(max_reads != 0, "a RwLock may not be created with 0 readers");
assert!(
max_reads as usize <= MAX_READS,
"a RwLock may not be created with more than u32::MAX >> 3 readers"
);
Self {
raw: RawRwLock::with_max_readers(max_reads as usize),
value: core::cell::UnsafeCell::new(value),
}
}
}
loom_const_fn! {
pub fn const_with_max_readers(value: T, max_reads: u32) -> Self {
assert!(max_reads != 0, "a RwLock may not be created with 0 readers");
assert!(
max_reads as usize <= MAX_READS,
"a RwLock may not be created with more than u32::MAX >> 3 readers"
);
Self {
raw: RawRwLock::with_max_readers(max_reads as usize),
value: core::cell::UnsafeCell::new(value),
}
}
}
loom_const_fn! {
#[cfg(test)]
pub(crate) fn new_without_spin(value: T) -> Self {
Self { raw: RawRwLock::without_spin(), value: core::cell::UnsafeCell::new(value) }
}
}
pub fn into_inner(self) -> T {
self.value.into_inner()
}
}
#[cfg(all(test, not(loom)))]
impl<T: ?Sized> RwLock<T> {
pub(crate) fn spin_rounds(&self) -> u32 {
self.raw.spin_rounds()
}
}
impl<T: ?Sized> RwLock<T> {
pub fn try_read(&self) -> Result<RwLockReadGuard<'_, T>, TryLockError> {
if self.raw.try_kind(READ) {
Ok(RwLockReadGuard::new(self))
} else {
Err(TryLockError(()))
}
}
pub fn try_read_owned(self: Arc<Self>) -> Result<OwnedRwLockReadGuard<T>, TryLockError> {
if self.raw.try_kind(READ) {
Ok(OwnedRwLockReadGuard::new(self))
} else {
Err(TryLockError(()))
}
}
pub fn try_write(&self) -> Result<RwLockWriteGuard<'_, T>, TryLockError> {
if self.raw.try_kind(WRITE) {
Ok(RwLockWriteGuard::new(self))
} else {
Err(TryLockError(()))
}
}
pub fn try_write_owned(self: Arc<Self>) -> Result<OwnedRwLockWriteGuard<T>, TryLockError> {
if self.raw.try_kind(WRITE) {
Ok(OwnedRwLockWriteGuard::new(self))
} else {
Err(TryLockError(()))
}
}
pub fn try_upgradable_read(&self) -> Option<RwLockUpgradableReadGuard<'_, T>> {
self.raw
.try_kind(UPGRADABLE_READ)
.then(|| RwLockUpgradableReadGuard { lock: self })
}
pub fn read(&self) -> Read<'_, T> {
Read {
lock: self,
wait: RwWait::new(),
}
}
pub fn read_owned(self: Arc<Self>) -> ReadOwned<T> {
ReadOwned {
lock: self,
wait: RwWait::new(),
}
}
pub fn write(&self) -> Write<'_, T> {
Write {
lock: self,
wait: RwWait::new(),
}
}
pub fn write_owned(self: Arc<Self>) -> WriteOwned<T> {
WriteOwned {
lock: self,
wait: RwWait::new(),
}
}
pub fn blocking_read(&self) -> RwLockReadGuard<'_, T> {
internal::blocking_in_line(self.read())
}
pub fn blocking_write(&self) -> RwLockWriteGuard<'_, T> {
internal::blocking_in_line(self.write())
}
pub fn upgradable_read(&self) -> UpgradableRead<'_, T> {
UpgradableRead {
lock: self,
wait: RwWait::new(),
}
}
pub fn get_mut(&mut self) -> &mut T {
self.value.get_mut()
}
}
impl<T> From<T> for RwLock<T> {
fn from(value: T) -> Self {
Self::new(value)
}
}
impl<T: Default> Default for RwLock<T> {
fn default() -> Self {
Self::new(T::default())
}
}
impl<T: ?Sized + fmt::Debug> fmt::Debug for RwLock<T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let mut d = f.debug_struct("RwLock");
match self.try_read() {
Ok(inner) => d.field("data", &&*inner),
Err(_) => d.field("data", &format_args!("<locked>")),
};
d.finish()
}
}
#[must_use = "dropping the guard releases the lock at once"]
pub struct RwLockUpgradableReadGuard<'a, T: ?Sized> {
lock: &'a RwLock<T>,
}
unsafe impl<T: ?Sized + Send + Sync> Send for RwLockUpgradableReadGuard<'_, T> {}
unsafe impl<T: ?Sized + Sync> Sync for RwLockUpgradableReadGuard<'_, T> {}
impl<'a, T: ?Sized> RwLockUpgradableReadGuard<'a, T> {
pub fn upgrade(self) -> Upgrade<'a, T> {
let lock = self.lock;
core::mem::forget(self);
Upgrade {
lock,
wait: UpgradeWait::new(),
done: false,
}
}
pub fn try_upgrade(self) -> Result<RwLockWriteGuard<'a, T>, Self> {
if !self.lock.raw.try_upgrade() {
return Err(self);
}
let lock = self.lock;
core::mem::forget(self);
Ok(RwLockWriteGuard::new(lock))
}
}
impl<T: ?Sized> Deref for RwLockUpgradableReadGuard<'_, T> {
type Target = T;
fn deref(&self) -> &T {
unsafe { &*self.lock.value.get() }
}
}
impl<T: ?Sized> Drop for RwLockUpgradableReadGuard<'_, T> {
fn drop(&mut self) {
self.lock.raw.upgradable_unlock();
}
}
impl<T: ?Sized + fmt::Debug> fmt::Debug for RwLockUpgradableReadGuard<'_, T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt::Debug::fmt(&**self, f)
}
}
#[must_use = "futures do nothing unless polled"]
pub struct Read<'a, T: ?Sized> {
lock: &'a RwLock<T>,
wait: RwWait,
}
impl<'a, T: ?Sized> Future for Read<'a, T> {
type Output = RwLockReadGuard<'a, T>;
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<RwLockReadGuard<'a, T>> {
let this = self.get_mut();
this.wait
.poll(&this.lock.raw, READ, cx.waker())
.map(|()| RwLockReadGuard::new(this.lock))
}
}
impl<T: ?Sized> Drop for Read<'_, T> {
fn drop(&mut self) {
self.wait.cancel(&self.lock.raw, READ);
}
}
impl<T: ?Sized> internal::Withdraw for Read<'_, T> {
fn withdraw(self: Pin<&mut Self>) {
let this = self.get_mut();
this.wait.cancel(&this.lock.raw, READ);
}
}
impl<T: ?Sized> fmt::Debug for Read<'_, T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("Read").finish_non_exhaustive()
}
}
#[must_use = "futures do nothing unless polled"]
pub struct ReadOwned<T: ?Sized> {
lock: Arc<RwLock<T>>,
wait: RwWait,
}
impl<T: ?Sized> Future for ReadOwned<T> {
type Output = OwnedRwLockReadGuard<T>;
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<OwnedRwLockReadGuard<T>> {
let this = self.get_mut();
this.wait
.poll(&this.lock.raw, READ, cx.waker())
.map(|()| OwnedRwLockReadGuard::new(Arc::clone(&this.lock)))
}
}
impl<T: ?Sized> Drop for ReadOwned<T> {
fn drop(&mut self) {
self.wait.cancel(&self.lock.raw, READ);
}
}
impl<T: ?Sized> fmt::Debug for ReadOwned<T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("ReadOwned").finish_non_exhaustive()
}
}
#[must_use = "futures do nothing unless polled"]
pub struct Write<'a, T: ?Sized> {
lock: &'a RwLock<T>,
wait: RwWait,
}
impl<'a, T: ?Sized> Future for Write<'a, T> {
type Output = RwLockWriteGuard<'a, T>;
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<RwLockWriteGuard<'a, T>> {
let this = self.get_mut();
this.wait
.poll(&this.lock.raw, WRITE, cx.waker())
.map(|()| RwLockWriteGuard::new(this.lock))
}
}
impl<T: ?Sized> Drop for Write<'_, T> {
fn drop(&mut self) {
self.wait.cancel(&self.lock.raw, WRITE);
}
}
impl<T: ?Sized> internal::Withdraw for Write<'_, T> {
fn withdraw(self: Pin<&mut Self>) {
let this = self.get_mut();
this.wait.cancel(&this.lock.raw, WRITE);
}
}
impl<T: ?Sized> fmt::Debug for Write<'_, T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("Write").finish_non_exhaustive()
}
}
#[must_use = "futures do nothing unless polled"]
pub struct WriteOwned<T: ?Sized> {
lock: Arc<RwLock<T>>,
wait: RwWait,
}
impl<T: ?Sized> Future for WriteOwned<T> {
type Output = OwnedRwLockWriteGuard<T>;
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<OwnedRwLockWriteGuard<T>> {
let this = self.get_mut();
this.wait
.poll(&this.lock.raw, WRITE, cx.waker())
.map(|()| OwnedRwLockWriteGuard::new(Arc::clone(&this.lock)))
}
}
impl<T: ?Sized> Drop for WriteOwned<T> {
fn drop(&mut self) {
self.wait.cancel(&self.lock.raw, WRITE);
}
}
impl<T: ?Sized> fmt::Debug for WriteOwned<T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("WriteOwned").finish_non_exhaustive()
}
}
#[must_use = "futures do nothing unless polled"]
pub struct UpgradableRead<'a, T: ?Sized> {
lock: &'a RwLock<T>,
wait: RwWait,
}
impl<'a, T: ?Sized> Future for UpgradableRead<'a, T> {
type Output = RwLockUpgradableReadGuard<'a, T>;
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<RwLockUpgradableReadGuard<'a, T>> {
let this = self.get_mut();
this.wait
.poll(&this.lock.raw, UPGRADABLE_READ, cx.waker())
.map(|()| RwLockUpgradableReadGuard { lock: this.lock })
}
}
impl<T: ?Sized> Drop for UpgradableRead<'_, T> {
fn drop(&mut self) {
self.wait.cancel(&self.lock.raw, UPGRADABLE_READ);
}
}
impl<T: ?Sized> fmt::Debug for UpgradableRead<'_, T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("UpgradableRead").finish_non_exhaustive()
}
}
#[must_use = "futures do nothing unless polled"]
pub struct Upgrade<'a, T: ?Sized> {
lock: &'a RwLock<T>,
wait: UpgradeWait,
done: bool,
}
unsafe impl<T: ?Sized + Send + Sync> Send for Upgrade<'_, T> {}
unsafe impl<T: ?Sized + Send + Sync> Sync for Upgrade<'_, T> {}
impl<'a, T: ?Sized> Future for Upgrade<'a, T> {
type Output = RwLockWriteGuard<'a, T>;
fn poll(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<RwLockWriteGuard<'a, T>> {
let this = self.get_mut();
assert!(
!this.done,
"an Upgrade future was polled after it completed"
);
this.wait.poll(&this.lock.raw, cx.waker()).map(|()| {
this.done = true;
RwLockWriteGuard::new(this.lock)
})
}
}
impl<T: ?Sized> Drop for Upgrade<'_, T> {
fn drop(&mut self) {
if !self.done {
self.wait.cancel(&self.lock.raw);
}
}
}
impl<T: ?Sized> fmt::Debug for Upgrade<'_, T> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("Upgrade")
.field("done", &self.done)
.finish_non_exhaustive()
}
}
mod guards;
pub use guards::{
OwnedRwLockMappedWriteGuard, OwnedRwLockReadGuard, OwnedRwLockWriteGuard,
RwLockMappedWriteGuard, RwLockReadGuard, RwLockWriteGuard,
};
#[cfg(all(test, not(loom)))]
mod tests;