Fix embarrassing bug where 'unkillable' would unwind improperly when it receives a kill signal.

This commit is contained in:
Ben Blum 2013-08-02 16:06:13 -04:00
parent cde6ad3992
commit d30cca46e6
2 changed files with 44 additions and 2 deletions

View File

@ -548,11 +548,12 @@ impl Death {
/// All calls must be paired with a subsequent call to allow_kill.
#[inline]
pub fn inhibit_kill(&mut self, already_failing: bool) {
if self.unkillable == 0 {
self.unkillable += 1;
// May fail, hence must happen *after* incrementing the counter
if self.unkillable == 1 {
rtassert!(self.kill_handle.is_some());
self.kill_handle.get_mut_ref().inhibit_kill(already_failing);
}
self.unkillable += 1;
}
/// Exit a possibly-nested unkillable section of code.

View File

@ -655,6 +655,47 @@ pub unsafe fn rekillable<U>(f: &fn() -> U) -> U {
}
}
#[test] #[ignore(cfg(windows))]
fn test_kill_unkillable_task() {
use rt::test::*;
// Attempt to test that when a kill signal is received at the start of an
// unkillable section, 'unkillable' unwinds correctly. This is actually
// quite a difficult race to expose, as the kill has to happen on a second
// CPU, *after* the spawner is already switched-back-to (and passes the
// killed check at the start of its timeslice). As far as I know, it's not
// possible to make this race deterministic, or even more likely to happen.
do run_in_newsched_task {
do task::try {
do task::spawn {
fail!();
}
do task::unkillable { }
};
}
}
#[test] #[ignore(cfg(windows))]
fn test_kill_rekillable_task() {
use rt::test::*;
// Tests that when a kill signal is received, 'rekillable' and
// 'unkillable' unwind correctly in conjunction with each other.
do run_in_newsched_task {
do task::try {
do task::unkillable {
unsafe {
do task::rekillable {
do task::spawn {
fail!();
}
}
}
}
};
}
}
#[test] #[should_fail] #[ignore(cfg(windows))]
fn test_cant_dup_task_builder() {
let mut builder = task();