about summary refs log tree commit diff
diff options
context:
space:
mode:
authorBen Blum <bblum@andrew.cmu.edu>2013-06-12 17:46:28 -0400
committerBen Blum <bblum@andrew.cmu.edu>2013-06-13 14:41:22 -0400
commit68e8fe9b6e8cd1abf88822c12738a8cd8fc950a5 (patch)
tree6bb0edc66ab403554abe1ac0fd5dcaa8f4b00f87
parentbd019c4c26d49c9481ebc4b57134bfdc5c5e5a97 (diff)
downloadrust-68e8fe9b6e8cd1abf88822c12738a8cd8fc950a5.tar.gz
rust-68e8fe9b6e8cd1abf88822c12738a8cd8fc950a5.zip
Add a test case for #7065.
-rw-r--r--src/libextra/arc.rs62
1 files changed, 62 insertions, 0 deletions
diff --git a/src/libextra/arc.rs b/src/libextra/arc.rs
index 2d9068be6cf..197e3e44105 100644
--- a/src/libextra/arc.rs
+++ b/src/libextra/arc.rs
@@ -813,4 +813,66 @@ mod tests {
 
         wp2.recv(); // complete handshake with writer
     }
+    #[cfg(test)]
+    fn test_rw_write_cond_downgrade_read_race_helper() {
+        // Tests that when a downgrader hands off the "reader cloud" lock
+        // because of a contending reader, a writer can't race to get it
+        // instead, which would result in readers_and_writers. This tests
+        // the sync module rather than this one, but it's here because an
+        // rwarc gives us extra shared state to help check for the race.
+        // If you want to see this test fail, go to sync.rs and replace the
+        // line in RWlock::write_cond() that looks like:
+        //     "blk(&Condvar { order: opt_lock, ..*cond })"
+        // with just "blk(cond)".
+        let x = ~RWARC(true);
+        let (wp, wc) = comm::stream();
+
+        // writer task
+        let xw = (*x).clone();
+        do task::spawn {
+            do xw.write_cond |state, c| {
+                wc.send(()); // tell downgrader it's ok to go
+                c.wait();
+                // The core of the test is here: the condvar reacquire path
+                // must involve order_lock, so that it cannot race with a reader
+                // trying to receive the "reader cloud lock hand-off".
+                *state = false;
+            }
+        }
+
+        wp.recv(); // wait for writer to get in
+
+        do x.write_downgrade |mut write_mode| {
+            do write_mode.write_cond |state, c| {
+                assert!(*state);
+                // make writer contend in the cond-reacquire path
+                c.signal();
+            }
+            // make a reader task to trigger the "reader cloud lock" handoff
+            let xr = (*x).clone();
+            let (rp, rc) = comm::stream();
+            do task::spawn {
+                rc.send(());
+                do xr.read |_state| { }
+            }
+            rp.recv(); // wait for reader task to exist
+
+            let read_mode = x.downgrade(write_mode);
+            do read_mode.read |state| {
+                // if writer mistakenly got in, make sure it mutates state
+                // before we assert on it
+                for 5.times { task::yield(); }
+                // make sure writer didn't get in.
+                assert!(*state);
+            }
+        }
+    }
+    #[test]
+    fn test_rw_write_cond_downgrade_read_race() {
+        // Ideally the above test case would have yield statements in it that
+        // helped to expose the race nearly 100% of the time... but adding
+        // yields in the intuitively-right locations made it even less likely,
+        // and I wasn't sure why :( . This is a mediocre "next best" option.
+        for 8.times { test_rw_write_cond_downgrade_read_race_helper() }
+    }
 }