#![allow(unsafe_code)]
use core::ptr::NonNull;
use core::task::{Poll, Waker};
use super::contend::{Contend, Lock};
use super::internal::{Ordering, UnsafeCell};
use super::list::List;
use super::queue::{
Arrivals, FIRST_BIT, HANDOFF, Nudges, Policy, QUEUED, Step, WAKE_FRONT, WaitQueue, wants_drain,
};
use super::waiter::{Cancel, Park, Wait, Waiter, WakeList, release_queued};
const WRITER: usize = 1 << FIRST_BIT;
const UPGRADABLE: usize = 1 << (FIRST_BIT + 1);
const UPGRADING: usize = 1 << (FIRST_BIT + 2);
const READER_SHIFT: u32 = FIRST_BIT + 3;
const ONE_READER: usize = 1 << READER_SHIFT;
pub(super) const MAX_READERS: usize = usize::MAX >> READER_SHIFT;
pub(super) const READ: usize = 0;
pub(super) const WRITE: usize = 1;
pub(super) const UPGRADABLE_READ: usize = 2;
const UPGRADE: usize = 3;
fn readers(state: usize) -> usize {
state >> READER_SHIFT
}
fn enter(kind: usize, state: usize) -> usize {
match kind {
READ => state + ONE_READER,
WRITE => state | WRITER,
_ => state | UPGRADABLE,
}
}
struct Waiting {
list: List,
upgrade: Option<NonNull<Waiter>>,
}
pub(super) struct RawRwLock {
queue: WaitQueue,
waiting: UnsafeCell<Waiting>,
max_readers: usize,
}
unsafe impl Send for RawRwLock {}
unsafe impl Sync for RawRwLock {}
impl RawRwLock {
loom_const_fn! {
pub(super) fn new() -> Self {
Self::with_max_readers(MAX_READERS)
}
}
loom_const_fn! {
pub(super) fn with_max_readers(max_readers: usize) -> Self {
Self {
queue: WaitQueue::new(0),
waiting: UnsafeCell::new(Waiting { list: List::new(), upgrade: None }),
max_readers,
}
}
}
loom_const_fn! {
#[cfg(test)]
pub(super) fn without_spin() -> Self {
Self {
queue: WaitQueue::without_spin(0),
waiting: UnsafeCell::new(Waiting { list: List::new(), upgrade: None }),
max_readers: MAX_READERS,
}
}
}
fn admits(&self, kind: usize, state: usize) -> bool {
match kind {
READ => state & (WRITER | UPGRADING) == 0 && readers(state) < self.max_readers,
WRITE => state & (WRITER | UPGRADABLE) == 0 && readers(state) == 0,
_ => state & (WRITER | UPGRADABLE) == 0,
}
}
pub(super) fn try_kind(&self, kind: usize) -> bool {
self.try_take(kind).is_ok()
}
pub(super) fn read_unlock(&self) {
let before = self.queue.state.fetch_sub(ONE_READER, Ordering::AcqRel);
let upgrade_ready = readers(before) == 1 && before & UPGRADING != 0;
if wants_drain(before) || upgrade_ready {
self.released();
}
}
pub(super) fn write_unlock(&self) {
let before = self.queue.state.fetch_and(!WRITER, Ordering::AcqRel);
if wants_drain(before) {
self.released();
}
}
pub(super) fn upgradable_unlock(&self) {
let before = self
.queue
.state
.fetch_and(!(UPGRADABLE | UPGRADING), Ordering::AcqRel);
if wants_drain(before) || before & UPGRADING != 0 {
self.released();
}
}
pub(super) fn downgrade(&self) {
self.transition(|state| {
debug_assert!(
state & WRITER != 0 && readers(state) == 0,
"a downgrade without the write"
);
let next = (state & !WRITER) + ONE_READER;
if state & QUEUED != 0 {
Step::Wake(next | WAKE_FRONT)
} else {
Step::Set(next)
}
});
}
pub(super) fn unlock_kind(&self, kind: usize) {
match kind {
READ => self.read_unlock(),
WRITE => self.write_unlock(),
_ => self.upgradable_unlock(),
}
}
pub(super) fn try_upgrade(&self) -> bool {
let mut state = self.queue.state.load(Ordering::Relaxed);
loop {
if readers(state) != 0 {
return false;
}
match self.queue.state.compare_exchange_weak(
state,
(state & !(UPGRADABLE | UPGRADING)) | WRITER,
Ordering::Acquire,
Ordering::Relaxed,
) {
Ok(_) => return true,
Err(actual) => state = actual,
}
}
}
fn finish_upgrade(&self) {
let before = self
.queue
.state
.fetch_xor(UPGRADABLE | UPGRADING | WRITER, Ordering::AcqRel);
debug_assert!(
before & (UPGRADABLE | UPGRADING | WRITER) == UPGRADABLE | UPGRADING
&& readers(before) == 0,
"an upgrade was granted on a lock its holder does not hold alone"
);
}
fn grant_upgrade(&self, node: NonNull<Waiter>, woken: &mut WakeList<'_>) -> bool {
let state = self.queue.state.load(Ordering::Acquire);
if state & UPGRADING == 0 || readers(state) != 0 {
return false;
}
woken.grant(node);
true
}
}
#[cfg(all(test, not(loom)))]
impl RawRwLock {
pub(super) fn spin_rounds(&self) -> u32 {
self.queue.spin.rounds()
}
}
impl Lock for RawRwLock {
fn admit(&self, state: usize, kind: usize) -> Option<usize> {
self.admits(kind, state).then(|| enter(kind, state))
}
fn give_back(&self, kind: usize) {
self.unlock_kind(kind);
}
}
impl Policy for RawRwLock {
fn queue(&self) -> &WaitQueue {
&self.queue
}
fn pass(&self, arrivals: Arrivals, woken: &mut WakeList<'_>, wake_front: bool) -> usize {
let queue = &self.queue;
self.waiting.with_mut(|waiting| unsafe {
let waiting = &mut *waiting;
if let Some(node) = waiting.upgrade
&& node.as_ref().is_cancelled()
{
release_queued(node, queue);
waiting.upgrade = None;
}
for node in arrivals {
if node.as_ref().is_cancelled() {
release_queued(node, queue);
} else if node.as_ref().payload() == UPGRADE {
debug_assert!(waiting.upgrade.is_none(), "two upgrades at once");
waiting.upgrade = Some(node);
} else {
waiting.list.push_back(node);
}
}
waiting.list.tidy(queue);
if let Some(node) = waiting.upgrade
&& self.grant_upgrade(node, woken)
{
waiting.upgrade = None;
}
let list = &mut waiting.list;
if queue.state.load(Ordering::Acquire) & HANDOFF != 0 {
while let Some(node) = list.front(queue) {
let kind = node.as_ref().payload();
if !self.take_for_waiter(kind) {
break;
}
list.pop_front();
if !woken.grant(node) {
self.undo(kind);
} else if kind == WRITE {
break;
}
}
}
if !queue.hands_off()
&& let Some(front) = list.front(queue)
{
let mut sharing = queue.state.load(Ordering::Acquire);
let mut nudges = Nudges::new(woken, queue);
for (at, node) in list.iter().enumerate() {
let kind = node.as_ref().payload();
let fits = self.admits(kind, sharing);
if at == 0 && (fits || wake_front) {
nudges.nudge(node);
}
if !fits || kind == WRITE || (at > 0 && front.as_ref().payload() == WRITE) {
break;
}
if at > 0 {
nudges.nudge(node);
}
sharing = enter(kind, sharing);
}
nudges.finish();
}
if list.front(queue).is_none() && waiting.upgrade.is_none() {
QUEUED
} else {
0
}
})
}
}
impl RawRwLock {
fn undo(&self, kind: usize) {
let gone = match kind {
READ => ONE_READER,
WRITE => WRITER,
_ => UPGRADABLE,
};
if kind == READ {
self.queue.state.fetch_sub(gone, Ordering::AcqRel);
} else {
self.queue.state.fetch_and(!gone, Ordering::AcqRel);
}
}
}
impl Drop for RawRwLock {
fn drop(&mut self) {
let queue = &self.queue;
self.waiting.with_mut(|waiting| unsafe {
let waiting = &mut *waiting;
waiting.list.clear(queue);
if let Some(node) = waiting.upgrade.take() {
release_queued(node, queue);
}
});
}
}
pub(super) struct RwWait {
contend: Contend,
}
impl RwWait {
pub(super) const fn new() -> Self {
Self {
contend: Contend::new(),
}
}
pub(super) fn is_queued(&self) -> bool {
self.contend.is_queued()
}
pub(super) fn poll(&mut self, lock: &RawRwLock, kind: usize, waker: &Waker) -> Poll<()> {
self.contend.poll(lock, kind, waker)
}
pub(super) fn cancel(&mut self, lock: &RawRwLock, kind: usize) {
self.contend.cancel(lock, kind);
}
}
pub(super) struct UpgradeWait {
wait: Wait,
started: bool,
}
impl UpgradeWait {
pub(super) const fn new() -> Self {
Self {
wait: Wait::new(),
started: false,
}
}
pub(super) fn poll(&mut self, lock: &RawRwLock, waker: &Waker) -> Poll<()> {
let park = if self.wait.is_queued() {
self.wait.park(&lock.queue, Some(waker))
} else {
if !self.started {
self.started = true;
lock.queue.state.fetch_or(UPGRADING, Ordering::AcqRel);
}
if lock.try_upgrade() {
return Poll::Ready(());
}
lock.enqueue(&mut self.wait, UPGRADE, waker);
self.wait.park(&lock.queue, None)
};
match park {
Park::Granted(_) => {
lock.finish_upgrade();
Poll::Ready(())
}
Park::Nudged | Park::Pending => Poll::Pending,
}
}
pub(super) fn cancel(&mut self, lock: &RawRwLock) {
let outcome = self.wait.cancel(&lock.queue);
lock.upgradable_unlock();
if let Cancel::Withdrawn(_) = outcome {
lock.withdrawn(false);
}
}
}
#[cfg(all(test, not(loom)))]
mod tests {
use core::cell::{Cell, RefCell};
use std::sync::Arc;
use std::sync::atomic::{AtomicBool, Ordering as StdOrdering};
use std::task::Wake;
use super::*;
struct Raced<'a> {
lock: &'a RawRwLock,
passes: Cell<u32>,
pass_begins: Box<dyn Fn(u32) + 'a>,
}
impl Policy for Raced<'_> {
fn queue(&self) -> &WaitQueue {
self.lock.queue()
}
fn pass(&self, arrivals: Arrivals, woken: &mut WakeList<'_>, wake_front: bool) -> usize {
let pass = self.passes.get() + 1;
self.passes.set(pass);
(self.pass_begins)(pass);
self.lock.pass(arrivals, woken, wake_front)
}
}
impl Lock for Raced<'_> {
fn admit(&self, state: usize, kind: usize) -> Option<usize> {
self.lock.admit(state, kind)
}
fn give_back(&self, kind: usize) {
self.lock.give_back(kind);
}
}
#[test]
fn an_upgrade_dropped_while_another_thread_drains_does_not_cost_the_next_upgrade() {
let lock = RawRwLock::new();
let waker = Waker::noop();
assert!(lock.try_kind(READ), "a reader is inside");
assert!(lock.try_kind(UPGRADABLE_READ), "the first upgradable read");
let first = RefCell::new(UpgradeWait::new());
assert!(
first.borrow_mut().poll(&lock, waker).is_pending(),
"the first upgrade waits for the reader"
);
let second = RefCell::new(UpgradeWait::new());
let raced = Raced {
lock: &lock,
passes: Cell::new(0),
pass_begins: Box::new(|pass| {
if pass == 1 {
first.borrow_mut().cancel(&lock);
assert!(lock.try_kind(UPGRADABLE_READ), "the second upgradable read");
assert!(
second.borrow_mut().poll(&lock, waker).is_pending(),
"the second upgrade waits for the reader"
);
lock.read_unlock();
}
}),
};
let mut writer = Contend::new();
let wrote = writer.poll(&raced, WRITE, waker);
assert!(
wrote.is_pending(),
"a writer got in while the upgradable read was held"
);
assert!(
second.borrow_mut().poll(&lock, waker).is_ready(),
"the upgrade never became the write"
);
writer.cancel(&raced, WRITE);
lock.write_unlock();
}
struct RaceAtDrop {
lock: Arc<RawRwLock>,
second_took_it: Arc<AtomicBool>,
}
impl Wake for RaceAtDrop {
fn wake(self: Arc<Self>) {
drop(self);
}
}
impl Drop for RaceAtDrop {
fn drop(&mut self) {
self.lock.read_unlock();
let took = self.lock.try_kind(UPGRADABLE_READ);
self.second_took_it.store(took, StdOrdering::Relaxed);
}
}
#[test]
fn a_pass_between_a_dropped_upgrades_withdraw_and_its_release_frees_nothing_twice() {
let lock = Arc::new(RawRwLock::new());
assert!(lock.try_kind(READ), "a reader is inside");
assert!(lock.try_kind(UPGRADABLE_READ), "the first upgradable read");
let second_took_it = Arc::new(AtomicBool::new(false));
let waker = Waker::from(Arc::new(RaceAtDrop {
lock: Arc::clone(&lock),
second_took_it: Arc::clone(&second_took_it),
}));
let mut first = UpgradeWait::new();
assert!(
first.poll(&lock, &waker).is_pending(),
"the first upgrade waits for the reader"
);
drop(waker);
first.cancel(&lock);
if !second_took_it.load(StdOrdering::Relaxed) {
assert!(
lock.try_kind(UPGRADABLE_READ),
"the upgradable read is free once its holder let it go"
);
}
assert!(
!lock.try_kind(WRITE),
"a writer got in while the upgradable read was held"
);
lock.upgradable_unlock();
assert!(lock.try_kind(WRITE), "the lock is free at the end");
}
}