Allow failing in arc::exclusive; poison to fail subsequent accesses (fix #3092)

This commit is contained in:
Ben Blum 2012-08-09 14:55:54 -04:00
parent 190ecc220a
commit be2e4ef624

View file

@ -81,11 +81,12 @@ fn clone<T: const send>(rc: &arc<T>) -> arc<T> {
} }
// An arc over mutable data that is protected by a lock. // An arc over mutable data that is protected by a lock.
type ex_data<T: send> = {lock: sys::little_lock, mut data: T}; type ex_data<T: send> =
{lock: sys::little_lock, mut failed: bool, mut data: T};
type exclusive<T: send> = arc_destruct<ex_data<T>>; type exclusive<T: send> = arc_destruct<ex_data<T>>;
fn exclusive<T:send >(-data: T) -> exclusive<T> { fn exclusive<T:send >(-data: T) -> exclusive<T> {
let data = ~{mut count: 1, data: {lock: sys::little_lock(), let data = ~{mut count: 1, data: {lock: sys::little_lock(), failed: false,
data: data}}; data: data}};
unsafe { unsafe {
let ptr = unsafe::reinterpret_cast(data); let ptr = unsafe::reinterpret_cast(data);
@ -128,12 +129,18 @@ impl<T: send> exclusive<T> {
let ptr: ~arc_data<ex_data<T>> = let ptr: ~arc_data<ex_data<T>> =
unsafe::reinterpret_cast(self.data); unsafe::reinterpret_cast(self.data);
assert ptr.count > 0; assert ptr.count > 0;
let r = { let ptr2: &arc_data<ex_data<T>> = unsafe::reinterpret_cast(&*ptr);
let rec: &ex_data<T> = &(*ptr).data;
do rec.lock.lock { f(&mut rec.data) }
};
unsafe::forget(ptr); unsafe::forget(ptr);
r let rec: &ex_data<T> = &(*ptr2).data;
do rec.lock.lock {
if rec.failed {
fail ~"Poisoned arc::exclusive - another task failed inside!";
}
rec.failed = true;
let result = f(&mut rec.data);
rec.failed = false;
result
}
} }
} }
@ -168,12 +175,11 @@ mod tests {
} }
#[test] #[test]
#[ignore] // this can probably infinite loop too.
fn exclusive_arc() { fn exclusive_arc() {
let mut futures = ~[]; let mut futures = ~[];
let num_tasks = 10u; let num_tasks = 10u;
let count = 1000u; let count = 10u;
let total = exclusive(~mut 0u); let total = exclusive(~mut 0u);
@ -194,4 +200,20 @@ mod tests {
assert **total == num_tasks * count assert **total == num_tasks * count
}; };
} }
#[test] #[should_fail] #[ignore(cfg(windows))]
fn exclusive_poison() {
// Tests that if one task fails inside of an exclusive, subsequent
// accesses will also fail.
let x = arc::exclusive(1);
let x2 = x.clone();
do task::try {
do x2.with |one| {
assert *one == 2;
}
};
do x.with |one| {
assert *one == 1;
}
}
} }