Skip to content

Commit f163edd

Browse files
committed
clean up downgrade test
1 parent c16dbc3 commit f163edd

File tree

1 file changed

+19
-114
lines changed

1 file changed

+19
-114
lines changed

library/std/src/sync/rwlock/tests.rs

Lines changed: 19 additions & 114 deletions
Original file line numberDiff line numberDiff line change
@@ -591,29 +591,34 @@ fn test_downgrade_readers() {
591591
}
592592
}
593593

594+
#[test]
594595
fn 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!("\tObserved {}", *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\nBeginning 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

Comments
 (0)