Skip to content

Don't poison ARCs that are used while unwinding #11109

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Dec 23, 2013
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
73 changes: 58 additions & 15 deletions src/libextra/arc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ use std::borrow;
/// As sync::condvar, a mechanism for unlock-and-descheduling and signaling.
pub struct Condvar<'a> {
priv is_mutex: bool,
priv failed: &'a mut bool,
priv failed: &'a bool,
priv cond: &'a sync::Condvar<'a>
}

Expand Down Expand Up @@ -226,7 +226,7 @@ impl<T:Send> MutexArc<T> {
// not already unsafe. See borrow_rwlock, far below.
(&(*state).lock).lock(|| {
check_poison(true, (*state).failed);
let _z = PoisonOnFail(&mut (*state).failed);
let _z = PoisonOnFail::new(&mut (*state).failed);
blk(&mut (*state).data)
})
}
Expand All @@ -239,10 +239,10 @@ impl<T:Send> MutexArc<T> {
let state = self.x.get();
(&(*state).lock).lock_cond(|cond| {
check_poison(true, (*state).failed);
let _z = PoisonOnFail(&mut (*state).failed);
let _z = PoisonOnFail::new(&mut (*state).failed);
blk(&mut (*state).data,
&Condvar {is_mutex: true,
failed: &mut (*state).failed,
failed: &(*state).failed,
cond: cond })
})
}
Expand Down Expand Up @@ -311,24 +311,28 @@ fn check_poison(is_mutex: bool, failed: bool) {

#[doc(hidden)]
struct PoisonOnFail {
failed: *mut bool,
flag: *mut bool,
failed: bool,
}

impl Drop for PoisonOnFail {
fn drop(&mut self) {
unsafe {
/* assert!(!*self.failed);
-- might be false in case of cond.wait() */
if task::failing() {
*self.failed = true;
if !self.failed && task::failing() {
*self.flag = true;
}
}
}
}

fn PoisonOnFail<'r>(failed: &'r mut bool) -> PoisonOnFail {
PoisonOnFail {
failed: failed
impl PoisonOnFail {
fn new<'a>(flag: &'a mut bool) -> PoisonOnFail {
PoisonOnFail {
flag: flag,
failed: task::failing()
}
}
}

Expand Down Expand Up @@ -392,7 +396,7 @@ impl<T:Freeze + Send> RWArc<T> {
let state = self.x.get();
(*borrow_rwlock(state)).write(|| {
check_poison(false, (*state).failed);
let _z = PoisonOnFail(&mut (*state).failed);
let _z = PoisonOnFail::new(&mut (*state).failed);
blk(&mut (*state).data)
})
}
Expand All @@ -407,10 +411,10 @@ impl<T:Freeze + Send> RWArc<T> {
let state = self.x.get();
(*borrow_rwlock(state)).write_cond(|cond| {
check_poison(false, (*state).failed);
let _z = PoisonOnFail(&mut (*state).failed);
let _z = PoisonOnFail::new(&mut (*state).failed);
blk(&mut (*state).data,
&Condvar {is_mutex: false,
failed: &mut (*state).failed,
failed: &(*state).failed,
cond: cond})
})
}
Expand Down Expand Up @@ -463,7 +467,7 @@ impl<T:Freeze + Send> RWArc<T> {
blk(RWWriteMode {
data: &mut (*state).data,
token: write_mode,
poison: PoisonOnFail(&mut (*state).failed)
poison: PoisonOnFail::new(&mut (*state).failed)
})
})
}
Expand Down Expand Up @@ -563,7 +567,7 @@ impl<'a, T:Freeze + Send> RWWriteMode<'a, T> {
unsafe {
let cvar = Condvar {
is_mutex: false,
failed: &mut *poison.failed,
failed: &*poison.flag,
cond: cond
};
blk(data, &cvar)
Expand Down Expand Up @@ -714,6 +718,25 @@ mod tests {
}
}

#[test]
fn test_mutex_arc_access_in_unwind() {
let arc = MutexArc::new(1i);
let arc2 = arc.clone();
task::try::<()>(proc() {
struct Unwinder {
i: MutexArc<int>
}
impl Drop for Unwinder {
fn drop(&mut self) {
self.i.access(|num| *num += 1);
}
}
let _u = Unwinder { i: arc2 };
fail!();
});
assert_eq!(2, arc.access(|n| *n));
}

#[test] #[should_fail]
fn test_rw_arc_poison_wr() {
let arc = RWArc::new(1);
Expand Down Expand Up @@ -840,6 +863,26 @@ mod tests {
assert_eq!(*num, 10);
})
}

#[test]
fn test_rw_arc_access_in_unwind() {
let arc = RWArc::new(1i);
let arc2 = arc.clone();
task::try::<()>(proc() {
struct Unwinder {
i: RWArc<int>
}
impl Drop for Unwinder {
fn drop(&mut self) {
self.i.write(|num| *num += 1);
}
}
let _u = Unwinder { i: arc2 };
fail!();
});
assert_eq!(2, arc.read(|n| *n));
}

#[test]
fn test_rw_downgrade() {
// (1) A downgrader gets in write mode and does cond.wait.
Expand Down