1- use rand:: Rng ;
2-
3- use crate :: hint:: spin_loop;
41use crate :: sync:: atomic:: { AtomicUsize , Ordering } ;
52use crate :: sync:: mpsc:: channel;
63use crate :: sync:: {
74 Arc , Barrier , MappedRwLockReadGuard , MappedRwLockWriteGuard , RwLock , RwLockReadGuard ,
85 RwLockWriteGuard , TryLockError ,
96} ;
107use crate :: thread;
8+ use crate :: time;
9+ use rand:: Rng ;
1110
1211#[ derive( Eq , PartialEq , Debug ) ]
1312struct NonCopy ( i32 ) ;
@@ -592,12 +591,68 @@ fn test_downgrade_readers() {
592591 }
593592}
594593
595- #[ test]
596594fn 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.
597652 const R : usize = 16 ;
598653
599654 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.
601656 let observers = Arc :: new ( AtomicUsize :: new ( 0 ) ) ;
602657
603658 let w = r. clone ( ) ;
@@ -619,9 +674,13 @@ fn test_downgrade_atomic() {
619674 for _ in 0 ..R {
620675 let r = r. clone ( ) ;
621676 let observers = observers. clone ( ) ;
677+
678+ // Spawn a thread that wants to observe the value that the main thread writes (not evil).
622679 let handle = thread:: spawn ( move || {
623680 // Will go to sleep since the main thread initially has the write lock.
624681 let read_guard = r. read ( ) . unwrap ( ) ;
682+ eprintln ! ( "\t Observed {}" , * read_guard) ;
683+
625684 if * read_guard == 1 {
626685 observers. fetch_add ( 1 , Ordering :: Relaxed ) ;
627686 }
@@ -630,25 +689,32 @@ fn test_downgrade_atomic() {
630689 reader_handles. push ( handle) ;
631690 }
632691
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.
633699 let evil = r. clone ( ) ;
634700 let evil_handle = thread:: spawn ( move || {
635701 // Will go to sleep since the main thread initially has the write lock.
636702 let mut evil_guard = evil. write ( ) . unwrap ( ) ;
703+ eprintln ! ( "Evil thread is writing now" ) ;
637704 * evil_guard = 2 ;
638705 } ) ;
639706
640- // FIXME Come up with a better way to make sure everyone is sleeping.
641707 // 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) ;
646709
647710 // Once everyone is asleep, set the value to 1.
648711 * main_write_guard = 1 ;
649712
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+
650716 // 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.
652718 let main_read_guard = RwLockWriteGuard :: downgrade ( main_write_guard) ;
653719
654720 // 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() {
668734 let final_check = r. read ( ) . unwrap ( ) ;
669735 assert_eq ! ( * final_check, 2 ) ;
670736
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" ) ;
672738}
0 commit comments