|
| 1 | +use crate::hint::spin_loop; |
1 | 2 | use crate::sync::atomic::{AtomicUsize, Ordering};
|
2 | 3 | use crate::sync::mpsc::channel;
|
3 | 4 | use crate::sync::{
|
@@ -537,6 +538,9 @@ fn test_downgrade_readers() {
|
537 | 538 | const R: usize = 16;
|
538 | 539 | const N: usize = 1000;
|
539 | 540 |
|
| 541 | + // Starts up 1 writing thread and `R` reader threads. |
| 542 | + // The writer thread will constantly update the value inside the `RwLock`, and this test will |
| 543 | + // only pass if every reader observes all values between 0 and `N`. |
540 | 544 | let r = Arc::new(RwLock::new(0));
|
541 | 545 | let b = Arc::new(Barrier::new(R + 1));
|
542 | 546 |
|
@@ -582,3 +586,82 @@ fn test_downgrade_readers() {
|
582 | 586 | });
|
583 | 587 | }
|
584 | 588 | }
|
| 589 | + |
| 590 | +#[test] |
| 591 | +fn test_downgrade_atomic() { |
| 592 | + const R: usize = 16; |
| 593 | + |
| 594 | + let r = Arc::new(RwLock::new(0)); |
| 595 | + // The number of reader threads that observe the correct value. |
| 596 | + let observers = Arc::new(AtomicUsize::new(0)); |
| 597 | + |
| 598 | + let w = r.clone(); |
| 599 | + let mut main_write_guard = w.write().unwrap(); |
| 600 | + |
| 601 | + // While the current thread is holding the write lock, spawn several reader threads and an evil |
| 602 | + // writer thread. |
| 603 | + // Each of the threads will attempt to read the `RwLock` and go to sleep because we have the |
| 604 | + // write lock. |
| 605 | + // We need at least 1 reader thread to observe what the main thread writes, otherwise that means |
| 606 | + // the evil writer thread got in front of every single reader. |
| 607 | + |
| 608 | + // FIXME |
| 609 | + // Should we actually require that every reader observe the first change? |
| 610 | + // This is a matter of protocol rather than correctness... |
| 611 | + |
| 612 | + let mut reader_handles = Vec::with_capacity(R); |
| 613 | + |
| 614 | + for _ in 0..R { |
| 615 | + let r = r.clone(); |
| 616 | + let observers = observers.clone(); |
| 617 | + let handle = thread::spawn(move || { |
| 618 | + // Will go to sleep since the main thread initially has the write lock. |
| 619 | + let read_guard = r.read().unwrap(); |
| 620 | + if *read_guard == 1 { |
| 621 | + observers.fetch_add(1, Ordering::Relaxed); |
| 622 | + } |
| 623 | + }); |
| 624 | + |
| 625 | + reader_handles.push(handle); |
| 626 | + } |
| 627 | + |
| 628 | + let evil = r.clone(); |
| 629 | + let evil_handle = thread::spawn(move || { |
| 630 | + // Will go to sleep since the main thread initially has the write lock. |
| 631 | + let mut evil_guard = evil.write().unwrap(); |
| 632 | + *evil_guard = 2; |
| 633 | + }); |
| 634 | + |
| 635 | + // FIXME Come up with a better way to make sure everyone is sleeping. |
| 636 | + // Make sure that everyone else is actually sleeping. |
| 637 | + let spin = 1000000; |
| 638 | + for _ in 0..spin { |
| 639 | + spin_loop(); |
| 640 | + } |
| 641 | + |
| 642 | + // Once everyone is asleep, set the value to 1. |
| 643 | + *main_write_guard = 1; |
| 644 | + |
| 645 | + // Atomically downgrade the write guard into a read guard. |
| 646 | + // This should wake up all of the reader threads, and allow them to also take the read lock. |
| 647 | + let main_read_guard = RwLockWriteGuard::downgrade(main_write_guard); |
| 648 | + |
| 649 | + // If the above is not atomic, then it is possible for the evil thread to get in front of the |
| 650 | + // readers and change the value to 2 instead. |
| 651 | + assert_eq!(*main_read_guard, 1, "`downgrade` was not atomic"); |
| 652 | + |
| 653 | + // By dropping all of the read guards, we allow the evil thread to make the change. |
| 654 | + drop(main_read_guard); |
| 655 | + |
| 656 | + for handle in reader_handles { |
| 657 | + handle.join().unwrap(); |
| 658 | + } |
| 659 | + |
| 660 | + // Wait for the evil thread to set the value to 2. |
| 661 | + evil_handle.join().unwrap(); |
| 662 | + |
| 663 | + let final_check = r.read().unwrap(); |
| 664 | + assert_eq!(*final_check, 2); |
| 665 | + |
| 666 | + assert!(observers.load(Ordering::Relaxed) > 0, "No readers observed the correct value"); |
| 667 | +} |
0 commit comments