Skip to content

Commit 6e0edf1

Browse files
committed
---
yaml --- r: 63352 b: refs/heads/snap-stage3 c: 998e41a h: refs/heads/master v: v3
1 parent f309fdb commit 6e0edf1

File tree

38 files changed

+1498
-652
lines changed

38 files changed

+1498
-652
lines changed

[refs]

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
---
22
refs/heads/master: 2d28d645422c1617be58c8ca7ad9a457264ca850
33
refs/heads/snap-stage1: e33de59e47c5076a89eadeb38f4934f58a3618a6
4-
refs/heads/snap-stage3: f22580dd294fde86c399741cdd1d3519a74004d6
4+
refs/heads/snap-stage3: 998e41a11a977413dcfc6716ab936ab9dcecb76d
55
refs/heads/try: 7b78b52e602bb3ea8174f9b2006bff3315f03ef9
66
refs/tags/release-0.1: 1f5c5126e96c79d22cb7862f75304136e204f105
77
refs/heads/ndm: f3868061cd7988080c30d6d5bf352a5a5fe2460b

branches/snap-stage3/src/libextra/arc.rs

Lines changed: 67 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -281,7 +281,6 @@ struct RWARCInner<T> { lock: RWlock, failed: bool, data: T }
281281
#[mutable]
282282
struct RWARC<T> {
283283
x: UnsafeAtomicRcBox<RWARCInner<T>>,
284-
cant_nest: ()
285284
}
286285

287286
/// Create a reader/writer ARC with the supplied data.
@@ -299,15 +298,14 @@ pub fn rw_arc_with_condvars<T:Const + Owned>(
299298
let data =
300299
RWARCInner { lock: rwlock_with_condvars(num_condvars),
301300
failed: false, data: user_data };
302-
RWARC { x: UnsafeAtomicRcBox::new(data), cant_nest: () }
301+
RWARC { x: UnsafeAtomicRcBox::new(data), }
303302
}
304303

305304
impl<T:Const + Owned> RWARC<T> {
306305
/// Duplicate a rwlock-protected ARC, as arc::clone.
307306
pub fn clone(&self) -> RWARC<T> {
308307
RWARC {
309308
x: self.x.clone(),
310-
cant_nest: (),
311309
}
312310
}
313311

@@ -382,12 +380,12 @@ impl<T:Const + Owned> RWARC<T> {
382380
* # Example
383381
*
384382
* ~~~ {.rust}
385-
* do arc.write_downgrade |write_mode| {
386-
* do (&write_mode).write_cond |state, condvar| {
383+
* do arc.write_downgrade |mut write_token| {
384+
* do write_token.write_cond |state, condvar| {
387385
* ... exclusive access with mutable state ...
388386
* }
389-
* let read_mode = arc.downgrade(write_mode);
390-
* do (&read_mode).read |state| {
387+
* let read_token = arc.downgrade(write_token);
388+
* do read_token.read |state| {
391389
* ... shared access with immutable state ...
392390
* }
393391
* }
@@ -815,4 +813,66 @@ mod tests {
815813

816814
wp2.recv(); // complete handshake with writer
817815
}
816+
#[cfg(test)]
817+
fn test_rw_write_cond_downgrade_read_race_helper() {
818+
// Tests that when a downgrader hands off the "reader cloud" lock
819+
// because of a contending reader, a writer can't race to get it
820+
// instead, which would result in readers_and_writers. This tests
821+
// the sync module rather than this one, but it's here because an
822+
// rwarc gives us extra shared state to help check for the race.
823+
// If you want to see this test fail, go to sync.rs and replace the
824+
// line in RWlock::write_cond() that looks like:
825+
// "blk(&Condvar { order: opt_lock, ..*cond })"
826+
// with just "blk(cond)".
827+
let x = ~RWARC(true);
828+
let (wp, wc) = comm::stream();
829+
830+
// writer task
831+
let xw = (*x).clone();
832+
do task::spawn {
833+
do xw.write_cond |state, c| {
834+
wc.send(()); // tell downgrader it's ok to go
835+
c.wait();
836+
// The core of the test is here: the condvar reacquire path
837+
// must involve order_lock, so that it cannot race with a reader
838+
// trying to receive the "reader cloud lock hand-off".
839+
*state = false;
840+
}
841+
}
842+
843+
wp.recv(); // wait for writer to get in
844+
845+
do x.write_downgrade |mut write_mode| {
846+
do write_mode.write_cond |state, c| {
847+
assert!(*state);
848+
// make writer contend in the cond-reacquire path
849+
c.signal();
850+
}
851+
// make a reader task to trigger the "reader cloud lock" handoff
852+
let xr = (*x).clone();
853+
let (rp, rc) = comm::stream();
854+
do task::spawn {
855+
rc.send(());
856+
do xr.read |_state| { }
857+
}
858+
rp.recv(); // wait for reader task to exist
859+
860+
let read_mode = x.downgrade(write_mode);
861+
do read_mode.read |state| {
862+
// if writer mistakenly got in, make sure it mutates state
863+
// before we assert on it
864+
for 5.times { task::yield(); }
865+
// make sure writer didn't get in.
866+
assert!(*state);
867+
}
868+
}
869+
}
870+
#[test]
871+
fn test_rw_write_cond_downgrade_read_race() {
872+
// Ideally the above test case would have yield statements in it that
873+
// helped to expose the race nearly 100% of the time... but adding
874+
// yields in the intuitively-right locations made it even less likely,
875+
// and I wasn't sure why :( . This is a mediocre "next best" option.
876+
for 8.times { test_rw_write_cond_downgrade_read_race_helper() }
877+
}
818878
}

0 commit comments

Comments
 (0)