@@ -476,16 +476,22 @@ impl RwLock {
476
476
}
477
477
}
478
478
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.
479
484
#[ cold]
480
485
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.
489
495
let tail = unsafe { find_tail_and_add_backlinks ( to_node ( state) ) . as_ref ( ) } ;
490
496
491
497
// The lock count is stored in the `next` field of `tail`.
@@ -515,10 +521,11 @@ impl RwLock {
515
521
///
516
522
/// * The lock must be exclusively owned by this thread.
517
523
/// * There must be threads queued on the lock.
524
+ /// * `state` must be a pointer to a node in a valid queue.
518
525
/// * There cannot be a `downgrade` in progress.
519
526
#[ cold]
520
527
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 ) ;
522
529
523
530
let mut current = state;
524
531
@@ -540,9 +547,10 @@ impl RwLock {
540
547
// Atomically release the lock and try to acquire the queue lock.
541
548
let next = current. map_addr ( |addr| ( addr & !LOCKED ) | QUEUE_LOCKED ) ;
542
549
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.
543
551
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 .
546
554
unsafe { self . unlock_queue ( next) } ;
547
555
return ;
548
556
}
@@ -580,10 +588,11 @@ impl RwLock {
580
588
/// # Safety
581
589
///
582
590
/// * The lock must be write-locked by this thread.
591
+ /// * `state` must be a pointer to a node in a valid queue.
583
592
/// * There must be threads queued on the lock.
584
593
#[ cold]
585
594
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 ) ;
587
596
588
597
// Attempt to wake up all waiters by taking ownership of the entire waiter queue.
589
598
loop {
@@ -612,7 +621,8 @@ impl RwLock {
612
621
let tail = unsafe { find_tail_and_add_backlinks ( to_node ( state) ) } ;
613
622
614
623
// 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.
616
626
unsafe { complete_all ( tail) } ;
617
627
618
628
return ;
@@ -626,11 +636,13 @@ impl RwLock {
626
636
/// # Safety
627
637
///
628
638
/// * The queue lock must be held by the current thread.
639
+ /// * `state` must be a pointer to a node in a valid queue.
629
640
/// * There must be threads queued on the lock.
630
641
unsafe fn unlock_queue ( & self , mut state : State ) {
631
642
debug_assert_eq ! ( state. addr( ) & ( QUEUED | QUEUE_LOCKED ) , QUEUED | QUEUE_LOCKED ) ;
632
643
633
644
loop {
645
+ // SAFETY: Since we have the queue lock, nobody else can be modifying the queue.
634
646
let tail = unsafe { find_tail_and_add_backlinks ( to_node ( state) ) } ;
635
647
636
648
if state. addr ( ) & ( DOWNGRADED | LOCKED ) == LOCKED {
0 commit comments