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() } + } }