Skip to content
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit 0604b8f

Browse files
committedNov 16, 2024
add safety comments for queue implementation
1 parent 00255e6 commit 0604b8f

File tree

1 file changed

+25
-13
lines changed

1 file changed

+25
-13
lines changed
 

‎std/src/sys/sync/rwlock/queue.rs

+25-13
Original file line numberDiff line numberDiff line change
@@ -476,16 +476,22 @@ impl RwLock {
476476
}
477477
}
478478

479+
/// # Safety
480+
///
481+
/// * There must be threads queued on the lock.
482+
/// * `state` must be a pointer to a node in a valid queue.
483+
/// * There cannot be a `downgrade` in progress.
479484
#[cold]
480485
unsafe fn read_unlock_contended(&self, state: State) {
481-
// The state was observed with acquire ordering above, so the current
482-
// thread will observe all node initializations.
483-
484-
// FIXME this is a bit confusing
485-
// SAFETY: Because new read-locks cannot be acquired while threads are queued, all
486-
// queue-lock owners will observe the set `LOCKED` bit. And because no downgrade can be in
487-
// progress (we checked above), they hence do not modify the queue, so the queue will not be
488-
// removed from here.
486+
// SAFETY:
487+
// The state was observed with acquire ordering above, so the current thread will have
488+
// observed all node initializations.
489+
// We also know that no threads can be modifying the queue starting at `state`: because new
490+
// read-locks cannot be acquired while there are any threads queued on the lock, all
491+
// queue-lock owners will observe a set `LOCKED` bit in `self.state` and will not modify
492+
// the queue. The other case that a thread could modify the queue is if a downgrade is in
493+
// progress (removal of the entire queue), but since that is part of this function's safety
494+
// contract, we can guarantee that no other threads can modify the queue.
489495
let tail = unsafe { find_tail_and_add_backlinks(to_node(state)).as_ref() };
490496

491497
// The lock count is stored in the `next` field of `tail`.
@@ -515,10 +521,11 @@ impl RwLock {
515521
///
516522
/// * The lock must be exclusively owned by this thread.
517523
/// * There must be threads queued on the lock.
524+
/// * `state` must be a pointer to a node in a valid queue.
518525
/// * There cannot be a `downgrade` in progress.
519526
#[cold]
520527
unsafe fn unlock_contended(&self, state: State) {
521-
debug_assert!(state.addr() & STATE == (QUEUED | LOCKED));
528+
debug_assert_eq!(state.addr() & (DOWNGRADED | QUEUED | LOCKED), QUEUED | LOCKED);
522529

523530
let mut current = state;
524531

@@ -540,9 +547,10 @@ impl RwLock {
540547
// Atomically release the lock and try to acquire the queue lock.
541548
let next = current.map_addr(|addr| (addr & !LOCKED) | QUEUE_LOCKED);
542549
match self.state.compare_exchange_weak(current, next, AcqRel, Relaxed) {
550+
// Now that we have the queue lock, we can wake up the next waiter.
543551
Ok(_) => {
544-
// Now that we have the queue lock, we can wake up the next waiter.
545-
// SAFETY: This thread is exclusively owned by this thread.
552+
// SAFETY: This thread just acquired the queue lock, and this function's safety
553+
// contract requires that there are threads already queued on the lock.
546554
unsafe { self.unlock_queue(next) };
547555
return;
548556
}
@@ -580,10 +588,11 @@ impl RwLock {
580588
/// # Safety
581589
///
582590
/// * The lock must be write-locked by this thread.
591+
/// * `state` must be a pointer to a node in a valid queue.
583592
/// * There must be threads queued on the lock.
584593
#[cold]
585594
unsafe fn downgrade_slow(&self, mut state: State) {
586-
debug_assert!(state.addr() & (DOWNGRADED | QUEUED | LOCKED) == (QUEUED | LOCKED));
595+
debug_assert_eq!(state.addr() & (DOWNGRADED | QUEUED | LOCKED), QUEUED | LOCKED);
587596

588597
// Attempt to wake up all waiters by taking ownership of the entire waiter queue.
589598
loop {
@@ -612,7 +621,8 @@ impl RwLock {
612621
let tail = unsafe { find_tail_and_add_backlinks(to_node(state)) };
613622

614623
// Wake up all waiters.
615-
// SAFETY: `tail` was just computed, meaning the whole queue is linked.
624+
// SAFETY: `tail` was just computed, meaning the whole queue is linked, and we have
625+
// full ownership of the queue, so we have exclusive access.
616626
unsafe { complete_all(tail) };
617627

618628
return;
@@ -626,11 +636,13 @@ impl RwLock {
626636
/// # Safety
627637
///
628638
/// * The queue lock must be held by the current thread.
639+
/// * `state` must be a pointer to a node in a valid queue.
629640
/// * There must be threads queued on the lock.
630641
unsafe fn unlock_queue(&self, mut state: State) {
631642
debug_assert_eq!(state.addr() & (QUEUED | QUEUE_LOCKED), QUEUED | QUEUE_LOCKED);
632643

633644
loop {
645+
// SAFETY: Since we have the queue lock, nobody else can be modifying the queue.
634646
let tail = unsafe { find_tail_and_add_backlinks(to_node(state)) };
635647

636648
if state.addr() & (DOWNGRADED | LOCKED) == LOCKED {

0 commit comments

Comments
 (0)
Failed to load comments.