1
- use rand:: Rng ;
2
-
3
- use crate :: hint:: spin_loop;
4
1
use crate :: sync:: atomic:: { AtomicUsize , Ordering } ;
5
2
use crate :: sync:: mpsc:: channel;
6
3
use crate :: sync:: {
7
4
Arc , Barrier , MappedRwLockReadGuard , MappedRwLockWriteGuard , RwLock , RwLockReadGuard ,
8
5
RwLockWriteGuard , TryLockError ,
9
6
} ;
10
7
use crate :: thread;
8
+ use crate :: time;
9
+ use rand:: Rng ;
11
10
12
11
#[ derive( Eq , PartialEq , Debug ) ]
13
12
struct NonCopy ( i32 ) ;
@@ -592,12 +591,68 @@ fn test_downgrade_readers() {
592
591
}
593
592
}
594
593
595
- #[ test]
596
594
fn test_downgrade_atomic ( ) {
595
+ // The number of evil writer threads.
596
+ const W : usize = 1024 ;
597
+ let rw = Arc :: new ( RwLock :: new ( 0i32 ) ) ;
598
+
599
+ // Put the lock in write mode, making all future threads trying to access this go to sleep.
600
+ let mut main_write_guard = rw. write ( ) . unwrap ( ) ;
601
+
602
+ let mut handles = Vec :: with_capacity ( W ) ;
603
+
604
+ for _ in 0 ..W {
605
+ let w = rw. clone ( ) ;
606
+ let evil_handle = thread:: spawn ( move || {
607
+ // Will go to sleep since the main thread initially has the write lock.
608
+ let mut evil_guard = w. write ( ) . unwrap ( ) ;
609
+ * evil_guard += 1 ;
610
+ } ) ;
611
+
612
+ handles. push ( evil_handle) ;
613
+ }
614
+
615
+ // Wait for a good amount of time so that evil threads go to sleep.
616
+ // Note that this is not striclty necessary.
617
+ let eternity = time:: Duration :: from_secs ( 1 ) ;
618
+ thread:: sleep ( eternity) ;
619
+
620
+ // Once everyone is asleep, set the value to -1.
621
+ * main_write_guard = -1 ;
622
+
623
+ // Atomically downgrade the write guard into a read guard.
624
+ let main_read_guard = RwLockWriteGuard :: downgrade ( main_write_guard) ;
625
+
626
+ // If the above is not atomic, then it is possible for an evil thread to get in front of this
627
+ // read and change the value to be non-negative.
628
+ assert_eq ! ( * main_read_guard, -1 , "`downgrade` was not atomic" ) ;
629
+
630
+ drop ( main_read_guard) ;
631
+
632
+ for handle in handles {
633
+ handle. join ( ) . unwrap ( ) ;
634
+ }
635
+
636
+ let final_check = rw. read ( ) . unwrap ( ) ;
637
+ assert_eq ! ( * final_check, W as i32 - 1 ) ;
638
+ }
639
+
640
+ #[ test]
641
+ #[ ignore]
642
+ fn test_downgrade_atomic_many ( ) {
643
+ for _ in 0 ..10 {
644
+ test_downgrade_atomic ( ) ;
645
+ }
646
+ }
647
+
648
+ #[ test]
649
+ #[ ignore]
650
+ fn test_downgrade_wake_readers ( ) {
651
+ // The number of reader threads.
597
652
const R : usize = 16 ;
598
653
599
654
let r = Arc :: new ( RwLock :: new ( 0 ) ) ;
600
- // The number of reader threads that observe the correct value.
655
+ // The number of reader threads that have observed the correct value.
601
656
let observers = Arc :: new ( AtomicUsize :: new ( 0 ) ) ;
602
657
603
658
let w = r. clone ( ) ;
@@ -619,9 +674,13 @@ fn test_downgrade_atomic() {
619
674
for _ in 0 ..R {
620
675
let r = r. clone ( ) ;
621
676
let observers = observers. clone ( ) ;
677
+
678
+ // Spawn a thread that wants to observe the value that the main thread writes (not evil).
622
679
let handle = thread:: spawn ( move || {
623
680
// Will go to sleep since the main thread initially has the write lock.
624
681
let read_guard = r. read ( ) . unwrap ( ) ;
682
+ eprintln ! ( "\t Observed {}" , * read_guard) ;
683
+
625
684
if * read_guard == 1 {
626
685
observers. fetch_add ( 1 , Ordering :: Relaxed ) ;
627
686
}
@@ -630,25 +689,32 @@ fn test_downgrade_atomic() {
630
689
reader_handles. push ( handle) ;
631
690
}
632
691
692
+ // FIXME Come up with a better way to make sure everyone is sleeping.
693
+ let eternity = time:: Duration :: from_secs ( 5 ) ;
694
+
695
+ // Make sure that everyone else is actually sleeping.
696
+ thread:: sleep ( eternity) ;
697
+
698
+ // Spawn an evil thread that will try to write before the readers have read.
633
699
let evil = r. clone ( ) ;
634
700
let evil_handle = thread:: spawn ( move || {
635
701
// Will go to sleep since the main thread initially has the write lock.
636
702
let mut evil_guard = evil. write ( ) . unwrap ( ) ;
703
+ eprintln ! ( "Evil thread is writing now" ) ;
637
704
* evil_guard = 2 ;
638
705
} ) ;
639
706
640
- // FIXME Come up with a better way to make sure everyone is sleeping.
641
707
// Make sure that everyone else is actually sleeping.
642
- let spin = 1000000 ;
643
- for _ in 0 ..spin {
644
- spin_loop ( ) ;
645
- }
708
+ thread:: sleep ( eternity) ;
646
709
647
710
// Once everyone is asleep, set the value to 1.
648
711
* main_write_guard = 1 ;
649
712
713
+ // As of now, the `R` reader threads and the 1 evil thread _should_ be sleeping.
714
+ eprintln ! ( "\n \n Beginning Test\n \n " ) ;
715
+
650
716
// Atomically downgrade the write guard into a read guard.
651
- // This should wake up all of the reader threads, and allow them to also take the read lock.
717
+ // This should wake up some of the reader threads, and allow them to also take the read lock.
652
718
let main_read_guard = RwLockWriteGuard :: downgrade ( main_write_guard) ;
653
719
654
720
// If the above is not atomic, then it is possible for the evil thread to get in front of the
@@ -668,5 +734,5 @@ fn test_downgrade_atomic() {
668
734
let final_check = r. read ( ) . unwrap ( ) ;
669
735
assert_eq ! ( * final_check, 2 ) ;
670
736
671
- assert ! ( observers. load( Ordering :: Relaxed ) > 0 , "No readers observed the correct value" ) ;
737
+ assert_ne ! ( observers. load( Ordering :: Relaxed ) , 0 , "No readers observed the correct value" ) ;
672
738
}
0 commit comments