Fix linked failure with root taskgroup to kill the runtime too.

This commit is contained in:
Ben Blum
2012-07-12 02:42:56 -04:00
parent 343e9de8ef
commit 200a2ded32
6 changed files with 59 additions and 23 deletions

View File

@@ -591,16 +591,21 @@ class taskgroup {
let my_pos: uint;
// let parent_group: taskgroup_arc; // TODO(bblum)
// TODO XXX bblum: add a list of empty slots to get runtime back
let mut failed: bool;
new(-tasks: taskgroup_arc, me: *rust_task, my_pos: uint) {
self.tasks = tasks; self.me = me; self.my_pos = my_pos;
self.failed = true; // This will get un-set on successful exit.
// Indicates whether this is the main (root) taskgroup. If so, failure
// here should take down the entire runtime.
let is_main: bool;
new(-tasks: taskgroup_arc, me: *rust_task, my_pos: uint, is_main: bool) {
self.tasks = tasks;
self.me = me;
self.my_pos = my_pos;
self.is_main = is_main;
}
// Runs on task exit.
drop {
if self.failed {
// If we are failing, the whole taskgroup needs to die.
if rustrt::rust_task_is_unwinding(self.me) {
// Take everybody down with us.
kill_taskgroup(self.tasks, self.me, self.my_pos);
kill_taskgroup(self.tasks, self.me, self.my_pos, self.is_main);
} else {
// Remove ourselves from the group.
leave_taskgroup(self.tasks, self.me, self.my_pos);
@@ -642,7 +647,8 @@ fn leave_taskgroup(group_arc: taskgroup_arc, me: *rust_task, index: uint) {
}
// NB: Runs in destructor/post-exit context. Can't 'fail'.
fn kill_taskgroup(group_arc: taskgroup_arc, me: *rust_task, index: uint) {
fn kill_taskgroup(group_arc: taskgroup_arc, me: *rust_task, index: uint,
is_main: bool) {
// NB: We could do the killing iteration outside of the group arc, by
// having "let mut newstate" here, swapping inside, and iterating after.
// But that would let other exiting tasks fall-through and exit while we
@@ -667,32 +673,40 @@ fn kill_taskgroup(group_arc: taskgroup_arc, me: *rust_task, index: uint) {
rustrt::rust_task_kill_other(task);
};
}
// Only one task should ever do this.
if is_main {
rustrt::rust_task_kill_all(me);
}
};
// (note: multiple tasks may reach this point)
};
}
fn share_parent_taskgroup() -> taskgroup_arc {
fn share_parent_taskgroup() -> (taskgroup_arc, bool) {
let me = rustrt::rust_get_task();
alt unsafe { local_get(me, taskgroup_key) } {
some(group) {
group.tasks.clone()
// Clone the shared state for the child; propagate main-ness.
(group.tasks.clone(), group.is_main)
}
none {
/* Main task, doing first spawn ever. */
// Main task, doing first spawn ever.
let tasks = arc::exclusive(some(dvec::from_elem(some(me))));
let group = @taskgroup(tasks.clone(), me, 0);
let group = @taskgroup(tasks.clone(), me, 0, true);
unsafe { local_set(me, taskgroup_key, group); }
tasks
// Tell child task it's also in the main group.
(tasks, true)
}
}
}
fn spawn_raw(opts: task_opts, +f: fn~()) {
// Decide whether the child needs to be in a new linked failure group.
let child_tg: taskgroup_arc = if opts.supervise {
let (child_tg, is_main) = if opts.supervise {
share_parent_taskgroup()
} else {
arc::exclusive(some(dvec::from_elem(none)))
// Detached from the parent group; create a new (non-main) one.
(arc::exclusive(some(dvec::from_elem(none))), false)
};
unsafe {
@@ -712,7 +726,8 @@ fn spawn_raw(opts: task_opts, +f: fn~()) {
// Getting killed after here would leak the task.
let child_wrapper =
make_child_wrapper(new_task, child_tg, opts.supervise, f);
make_child_wrapper(new_task, child_tg,
opts.supervise, is_main, f);
let fptr = ptr::addr_of(child_wrapper);
let closure: *rust_closure = unsafe::reinterpret_cast(fptr);
@@ -730,7 +745,8 @@ fn spawn_raw(opts: task_opts, +f: fn~()) {
}
fn make_child_wrapper(child_task: *rust_task, -child_tg: taskgroup_arc,
supervise: bool, -f: fn~()) -> fn~() {
supervise: bool, is_main: bool,
-f: fn~()) -> fn~() {
let child_tg_ptr = ~mut some(child_tg);
fn~() {
// Agh. Get move-mode items into the closure. FIXME (#2829)
@@ -746,13 +762,12 @@ fn spawn_raw(opts: task_opts, +f: fn~()) {
// parent was already failing, so don't bother doing anything.
alt enlist_in_taskgroup(child_tg, child_task) {
some(my_index) {
let group = @taskgroup(child_tg, child_task, my_index);
let group =
@taskgroup(child_tg, child_task, my_index, is_main);
unsafe { local_set(child_task, taskgroup_key, group); }
// Run the child's body.
f();
// Report successful exit. (TLS cleanup code will tear
// down the group.)
group.failed = false;
// TLS cleanup code will exit the taskgroup.
}
none { }
}
@@ -1006,6 +1021,7 @@ extern mod rustrt {
fn rust_task_inhibit_kill();
fn rust_task_allow_kill();
fn rust_task_kill_other(task: *rust_task);
fn rust_task_kill_all(task: *rust_task);
#[rust_stack]
fn rust_get_task_local_data(task: *rust_task) -> *libc::c_void;