Skip to content

Commit d30cca4

Browse files
committed
Fix embarrassing bug where 'unkillable' would unwind improperly when it receives a kill signal.
1 parent cde6ad3 commit d30cca4

File tree

2 files changed

+44
-2
lines changed

2 files changed

+44
-2
lines changed

src/libstd/rt/kill.rs

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -548,11 +548,12 @@ impl Death {
548548
/// All calls must be paired with a subsequent call to allow_kill.
549549
#[inline]
550550
pub fn inhibit_kill(&mut self, already_failing: bool) {
551-
if self.unkillable == 0 {
551+
self.unkillable += 1;
552+
// May fail, hence must happen *after* incrementing the counter
553+
if self.unkillable == 1 {
552554
rtassert!(self.kill_handle.is_some());
553555
self.kill_handle.get_mut_ref().inhibit_kill(already_failing);
554556
}
555-
self.unkillable += 1;
556557
}
557558

558559
/// Exit a possibly-nested unkillable section of code.

src/libstd/task/mod.rs

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -655,6 +655,47 @@ pub unsafe fn rekillable<U>(f: &fn() -> U) -> U {
655655
}
656656
}
657657

658+
#[test] #[ignore(cfg(windows))]
659+
fn test_kill_unkillable_task() {
660+
use rt::test::*;
661+
662+
// Attempt to test that when a kill signal is received at the start of an
663+
// unkillable section, 'unkillable' unwinds correctly. This is actually
664+
// quite a difficult race to expose, as the kill has to happen on a second
665+
// CPU, *after* the spawner is already switched-back-to (and passes the
666+
// killed check at the start of its timeslice). As far as I know, it's not
667+
// possible to make this race deterministic, or even more likely to happen.
668+
do run_in_newsched_task {
669+
do task::try {
670+
do task::spawn {
671+
fail!();
672+
}
673+
do task::unkillable { }
674+
};
675+
}
676+
}
677+
678+
#[test] #[ignore(cfg(windows))]
679+
fn test_kill_rekillable_task() {
680+
use rt::test::*;
681+
682+
// Tests that when a kill signal is received, 'rekillable' and
683+
// 'unkillable' unwind correctly in conjunction with each other.
684+
do run_in_newsched_task {
685+
do task::try {
686+
do task::unkillable {
687+
unsafe {
688+
do task::rekillable {
689+
do task::spawn {
690+
fail!();
691+
}
692+
}
693+
}
694+
}
695+
};
696+
}
697+
}
698+
658699
#[test] #[should_fail] #[ignore(cfg(windows))]
659700
fn test_cant_dup_task_builder() {
660701
let mut builder = task();

0 commit comments

Comments
 (0)