@@ -136,7 +136,11 @@ const MASK: usize = !(QUEUE_LOCKED | QUEUED | LOCKED);
136
136
#[ inline]
137
137
fn write_lock ( state : State ) -> Option < State > {
138
138
let state = state. wrapping_byte_add ( LOCKED ) ;
139
- if state. addr ( ) & LOCKED == LOCKED { Some ( state) } else { None }
139
+ if state. addr ( ) & LOCKED == LOCKED {
140
+ Some ( state)
141
+ } else {
142
+ None
143
+ }
140
144
}
141
145
142
146
/// Marks the state as read-locked, if possible.
@@ -317,13 +321,26 @@ impl RwLock {
317
321
let mut node = Node :: new ( write) ;
318
322
let mut state = self . state . load ( Relaxed ) ;
319
323
let mut count = 0 ;
324
+ let mut has_slept = false ;
320
325
loop {
321
326
if let Some ( next) = update ( state) {
322
327
// The lock is available, try locking it.
323
328
match self . state . compare_exchange_weak ( state, next, Acquire , Relaxed ) {
324
329
Ok ( _) => return ,
325
330
Err ( new) => state = new,
326
331
}
332
+ } else if !write && has_slept {
333
+ // If we are trying to read and we have already gone to sleep, first check if the
334
+ // lock is in read mode before going to sleep again.
335
+ let tail = unsafe { add_backlinks_and_find_tail ( to_node ( state) ) . as_ref ( ) } ;
336
+ let _ = tail. next . 0 . fetch_update ( Release , Acquire , |count : * mut Node | {
337
+ if count. mask ( MASK ) . addr ( ) > 0 {
338
+ Some ( without_provenance_mut ( state. addr ( ) . checked_add ( SINGLE ) ? | LOCKED ) )
339
+ } else {
340
+ None
341
+ }
342
+ } ) ;
343
+ todo ! ( "This is very wrong" ) ;
327
344
} else if state. addr ( ) & QUEUED == 0 && count < SPIN_COUNT {
328
345
// If the lock is not available and no threads are queued, spin
329
346
// for a while, using exponential backoff to decrease cache
@@ -388,6 +405,8 @@ impl RwLock {
388
405
node. wait ( ) ;
389
406
}
390
407
408
+ has_slept = true ;
409
+
391
410
// The node was removed from the queue, disarm the guard.
392
411
mem:: forget ( guard) ;
393
412
@@ -453,45 +472,44 @@ impl RwLock {
453
472
454
473
#[ inline]
455
474
pub unsafe fn downgrade ( & self ) {
456
- // Atomically set to read-locked with a single reader, without any waiting threads.
457
- if let Err ( mut state) = self . state . compare_exchange (
475
+ // Atomically attempt to go from a single writer without any waiting threads to a single
476
+ // reader without any waiting threads.
477
+ if let Err ( state) = self . state . compare_exchange (
458
478
without_provenance_mut ( LOCKED ) ,
459
479
without_provenance_mut ( LOCKED | SINGLE ) ,
460
480
Release ,
461
481
Relaxed ,
462
482
) {
463
- // Attempt to grab the queue lock.
464
- loop {
465
- let next = state. map_addr ( |addr| addr | QUEUE_LOCKED ) ;
466
- match self . state . compare_exchange ( state, next, AcqRel , Relaxed ) {
467
- Err ( new_state) => state = new_state,
468
- Ok ( new_state) => {
469
- assert_eq ! (
470
- new_state. mask( !MASK ) . addr( ) ,
471
- LOCKED | QUEUED | QUEUE_LOCKED ,
472
- "{:p}" ,
473
- new_state
474
- ) ;
475
- state = new_state;
476
- break ;
477
- }
478
- }
479
- }
480
-
481
- assert_eq ! ( state. mask( !MASK ) . addr( ) , LOCKED | QUEUED | QUEUE_LOCKED ) ;
483
+ debug_assert ! (
484
+ state. mask( LOCKED ) . addr( ) != 0 && state. mask( QUEUED ) . addr( ) != 0 ,
485
+ "RwLock should be LOCKED and QUEUED"
486
+ ) ;
482
487
483
- // SAFETY: We have the queue lock so all safety contracts are fulfilled.
484
- let tail = unsafe { add_backlinks_and_find_tail ( to_node ( state) ) . as_ref ( ) } ;
488
+ // FIXME Is this correct?
489
+ // SAFETY: Since we have the write lock, nobody else can be modifying state, and since
490
+ // we got `state` from the `compare_exchange`, we know it is a valid head of the queue.
491
+ let tail = unsafe { add_backlinks_and_find_tail ( to_node ( state) ) } ;
485
492
493
+ // FIXME Is this safe to modify? There shouldn't be other threads modifying this since
494
+ // we have the write lock and only we should be able to modify the nodes in the queue...
486
495
// Increment the reader count from 0 to 1.
487
- assert_eq ! (
488
- tail. next. 0 . fetch_byte_add( SINGLE , AcqRel ) . addr( ) ,
489
- 0 ,
490
- "Reader count was not zero while we had the write lock"
491
- ) ;
492
-
493
- // Release the queue lock.
494
- self . state . fetch_byte_sub ( QUEUE_LOCKED , Release ) ;
496
+ let old = unsafe { tail. as_ref ( ) } . next . 0 . fetch_byte_add ( SINGLE , AcqRel ) . addr ( ) ;
497
+ debug_assert_eq ! ( old, 0 , "Reader count was not zero while we had the write lock" ) ;
498
+
499
+ // Now that we are in read mode, traverse the queue and wake up readers until we find a
500
+ // writer node.
501
+ let mut current = tail;
502
+ while unsafe { !current. as_ref ( ) . write } {
503
+ let prev = unsafe { current. as_ref ( ) . prev . get ( ) } ;
504
+ unsafe {
505
+ // There must be threads waiting.
506
+ Node :: complete ( current) ;
507
+ }
508
+ match prev {
509
+ Some ( prev) => current = prev,
510
+ None => return ,
511
+ }
512
+ }
495
513
}
496
514
}
497
515
0 commit comments