@@ -591,29 +591,34 @@ fn test_downgrade_readers() {
591591 }
592592}
593593
594+ #[ test]
594595fn test_downgrade_atomic ( ) {
596+ // Spawns many evil writer threads that will try and write to the locked value before the
597+ // intial writer who has the exlusive lock can read after it downgrades.
598+ // If the `RwLock` behaves correctly, then the initial writer should read the value it wrote
599+ // itself as no other thread should get in front of it.
600+
595601 // The number of evil writer threads.
596602 const W : usize = 1024 ;
597603 let rw = Arc :: new ( RwLock :: new ( 0i32 ) ) ;
598604
599605 // Put the lock in write mode, making all future threads trying to access this go to sleep.
600606 let mut main_write_guard = rw. write ( ) . unwrap ( ) ;
601607
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- }
608+ // Spawn all of the evil writer threads.
609+ let handles: Vec < _ > = ( 0 ..W )
610+ . map ( |_| {
611+ let w = rw. clone ( ) ;
612+ thread:: spawn ( move || {
613+ // Will go to sleep since the main thread initially has the write lock.
614+ let mut evil_guard = w. write ( ) . unwrap ( ) ;
615+ * evil_guard += 1 ;
616+ } )
617+ } )
618+ . collect ( ) ;
614619
615620 // Wait for a good amount of time so that evil threads go to sleep.
616- // Note that this is not striclty necessary.
621+ // ( Note that this is not striclty necessary...)
617622 let eternity = time:: Duration :: from_secs ( 1 ) ;
618623 thread:: sleep ( eternity) ;
619624
@@ -627,112 +632,12 @@ fn test_downgrade_atomic() {
627632 // read and change the value to be non-negative.
628633 assert_eq ! ( * main_read_guard, -1 , "`downgrade` was not atomic" ) ;
629634
635+ // Clean up everything now
630636 drop ( main_read_guard) ;
631-
632637 for handle in handles {
633638 handle. join ( ) . unwrap ( ) ;
634639 }
635640
636641 let final_check = rw. read ( ) . unwrap ( ) ;
637642 assert_eq ! ( * final_check, W as i32 - 1 ) ;
638643}
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.
652- const R : usize = 16 ;
653-
654- let r = Arc :: new ( RwLock :: new ( 0 ) ) ;
655- // The number of reader threads that have observed the correct value.
656- let observers = Arc :: new ( AtomicUsize :: new ( 0 ) ) ;
657-
658- let w = r. clone ( ) ;
659- let mut main_write_guard = w. write ( ) . unwrap ( ) ;
660-
661- // While the current thread is holding the write lock, spawn several reader threads and an evil
662- // writer thread.
663- // Each of the threads will attempt to read the `RwLock` and go to sleep because we have the
664- // write lock.
665- // We need at least 1 reader thread to observe what the main thread writes, otherwise that means
666- // the evil writer thread got in front of every single reader.
667-
668- // FIXME
669- // Should we actually require that every reader observe the first change?
670- // This is a matter of protocol rather than correctness...
671-
672- let mut reader_handles = Vec :: with_capacity ( R ) ;
673-
674- for _ in 0 ..R {
675- let r = r. clone ( ) ;
676- let observers = observers. clone ( ) ;
677-
678- // Spawn a thread that wants to observe the value that the main thread writes (not evil).
679- let handle = thread:: spawn ( move || {
680- // Will go to sleep since the main thread initially has the write lock.
681- let read_guard = r. read ( ) . unwrap ( ) ;
682- eprintln ! ( "\t Observed {}" , * read_guard) ;
683-
684- if * read_guard == 1 {
685- observers. fetch_add ( 1 , Ordering :: Relaxed ) ;
686- }
687- } ) ;
688-
689- reader_handles. push ( handle) ;
690- }
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.
699- let evil = r. clone ( ) ;
700- let evil_handle = thread:: spawn ( move || {
701- // Will go to sleep since the main thread initially has the write lock.
702- let mut evil_guard = evil. write ( ) . unwrap ( ) ;
703- eprintln ! ( "Evil thread is writing now" ) ;
704- * evil_guard = 2 ;
705- } ) ;
706-
707- // Make sure that everyone else is actually sleeping.
708- thread:: sleep ( eternity) ;
709-
710- // Once everyone is asleep, set the value to 1.
711- * main_write_guard = 1 ;
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-
716- // Atomically downgrade the write guard into a read guard.
717- // This should wake up some of the reader threads, and allow them to also take the read lock.
718- let main_read_guard = RwLockWriteGuard :: downgrade ( main_write_guard) ;
719-
720- // If the above is not atomic, then it is possible for the evil thread to get in front of the
721- // readers and change the value to 2 instead.
722- assert_eq ! ( * main_read_guard, 1 , "`downgrade` was not atomic" ) ;
723-
724- // By dropping all of the read guards, we allow the evil thread to make the change.
725- drop ( main_read_guard) ;
726-
727- for handle in reader_handles {
728- handle. join ( ) . unwrap ( ) ;
729- }
730-
731- // Wait for the evil thread to set the value to 2.
732- evil_handle. join ( ) . unwrap ( ) ;
733-
734- let final_check = r. read ( ) . unwrap ( ) ;
735- assert_eq ! ( * final_check, 2 ) ;
736-
737- assert_ne ! ( observers. load( Ordering :: Relaxed ) , 0 , "No readers observed the correct value" ) ;
738- }
0 commit comments