Fix linked failure with root taskgroup to kill the runtime too.
This commit is contained in:
@@ -591,16 +591,21 @@ class taskgroup {
|
|||||||
let my_pos: uint;
|
let my_pos: uint;
|
||||||
// let parent_group: taskgroup_arc; // TODO(bblum)
|
// let parent_group: taskgroup_arc; // TODO(bblum)
|
||||||
// TODO XXX bblum: add a list of empty slots to get runtime back
|
// TODO XXX bblum: add a list of empty slots to get runtime back
|
||||||
let mut failed: bool;
|
// Indicates whether this is the main (root) taskgroup. If so, failure
|
||||||
new(-tasks: taskgroup_arc, me: *rust_task, my_pos: uint) {
|
// here should take down the entire runtime.
|
||||||
self.tasks = tasks; self.me = me; self.my_pos = my_pos;
|
let is_main: bool;
|
||||||
self.failed = true; // This will get un-set on successful exit.
|
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.
|
// Runs on task exit.
|
||||||
drop {
|
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.
|
// 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 {
|
} else {
|
||||||
// Remove ourselves from the group.
|
// Remove ourselves from the group.
|
||||||
leave_taskgroup(self.tasks, self.me, self.my_pos);
|
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'.
|
// 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
|
// NB: We could do the killing iteration outside of the group arc, by
|
||||||
// having "let mut newstate" here, swapping inside, and iterating after.
|
// having "let mut newstate" here, swapping inside, and iterating after.
|
||||||
// But that would let other exiting tasks fall-through and exit while we
|
// 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);
|
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();
|
let me = rustrt::rust_get_task();
|
||||||
alt unsafe { local_get(me, taskgroup_key) } {
|
alt unsafe { local_get(me, taskgroup_key) } {
|
||||||
some(group) {
|
some(group) {
|
||||||
group.tasks.clone()
|
// Clone the shared state for the child; propagate main-ness.
|
||||||
|
(group.tasks.clone(), group.is_main)
|
||||||
}
|
}
|
||||||
none {
|
none {
|
||||||
/* Main task, doing first spawn ever. */
|
// Main task, doing first spawn ever.
|
||||||
let tasks = arc::exclusive(some(dvec::from_elem(some(me))));
|
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); }
|
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~()) {
|
fn spawn_raw(opts: task_opts, +f: fn~()) {
|
||||||
// Decide whether the child needs to be in a new linked failure group.
|
// 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()
|
share_parent_taskgroup()
|
||||||
} else {
|
} 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 {
|
unsafe {
|
||||||
@@ -712,7 +726,8 @@ fn spawn_raw(opts: task_opts, +f: fn~()) {
|
|||||||
// Getting killed after here would leak the task.
|
// Getting killed after here would leak the task.
|
||||||
|
|
||||||
let child_wrapper =
|
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 fptr = ptr::addr_of(child_wrapper);
|
||||||
let closure: *rust_closure = unsafe::reinterpret_cast(fptr);
|
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,
|
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);
|
let child_tg_ptr = ~mut some(child_tg);
|
||||||
fn~() {
|
fn~() {
|
||||||
// Agh. Get move-mode items into the closure. FIXME (#2829)
|
// 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.
|
// parent was already failing, so don't bother doing anything.
|
||||||
alt enlist_in_taskgroup(child_tg, child_task) {
|
alt enlist_in_taskgroup(child_tg, child_task) {
|
||||||
some(my_index) {
|
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); }
|
unsafe { local_set(child_task, taskgroup_key, group); }
|
||||||
// Run the child's body.
|
// Run the child's body.
|
||||||
f();
|
f();
|
||||||
// Report successful exit. (TLS cleanup code will tear
|
// TLS cleanup code will exit the taskgroup.
|
||||||
// down the group.)
|
|
||||||
group.failed = false;
|
|
||||||
}
|
}
|
||||||
none { }
|
none { }
|
||||||
}
|
}
|
||||||
@@ -1006,6 +1021,7 @@ extern mod rustrt {
|
|||||||
fn rust_task_inhibit_kill();
|
fn rust_task_inhibit_kill();
|
||||||
fn rust_task_allow_kill();
|
fn rust_task_allow_kill();
|
||||||
fn rust_task_kill_other(task: *rust_task);
|
fn rust_task_kill_other(task: *rust_task);
|
||||||
|
fn rust_task_kill_all(task: *rust_task);
|
||||||
|
|
||||||
#[rust_stack]
|
#[rust_stack]
|
||||||
fn rust_get_task_local_data(task: *rust_task) -> *libc::c_void;
|
fn rust_get_task_local_data(task: *rust_task) -> *libc::c_void;
|
||||||
|
|||||||
@@ -863,6 +863,11 @@ rust_task_kill_other(rust_task *task) { /* Used for linked failure */
|
|||||||
task->kill();
|
task->kill();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
extern "C" void
|
||||||
|
rust_task_kill_all(rust_task *task) {
|
||||||
|
task->fail_sched_loop();
|
||||||
|
}
|
||||||
|
|
||||||
extern "C" rust_cond_lock*
|
extern "C" rust_cond_lock*
|
||||||
rust_create_cond_lock() {
|
rust_create_cond_lock() {
|
||||||
return new rust_cond_lock();
|
return new rust_cond_lock();
|
||||||
|
|||||||
@@ -260,8 +260,8 @@ rust_task *
|
|||||||
rust_sched_loop::create_task(rust_task *spawner, const char *name) {
|
rust_sched_loop::create_task(rust_task *spawner, const char *name) {
|
||||||
rust_task *task =
|
rust_task *task =
|
||||||
new (this->kernel, "rust_task")
|
new (this->kernel, "rust_task")
|
||||||
rust_task (this, task_state_newborn,
|
rust_task(this, task_state_newborn,
|
||||||
spawner, name, kernel->env->min_stack_size);
|
spawner, name, kernel->env->min_stack_size);
|
||||||
DLOG(this, task, "created task: " PTR ", spawner: %s, name: %s",
|
DLOG(this, task, "created task: " PTR ", spawner: %s, name: %s",
|
||||||
task, spawner ? spawner->name : "null", name);
|
task, spawner ? spawner->name : "null", name);
|
||||||
|
|
||||||
|
|||||||
@@ -129,6 +129,11 @@ cleanup_task(cleanup_args *args) {
|
|||||||
// assert(task->task_local_data != NULL);
|
// assert(task->task_local_data != NULL);
|
||||||
task->task_local_data_cleanup(task->task_local_data);
|
task->task_local_data_cleanup(task->task_local_data);
|
||||||
task->task_local_data = NULL;
|
task->task_local_data = NULL;
|
||||||
|
} else if (threw_exception) {
|
||||||
|
// Edge case: If main never spawns any tasks, but fails anyway, TLS
|
||||||
|
// won't be around to take down the kernel (task.rs:kill_taskgroup,
|
||||||
|
// rust_task_kill_all). Do it here instead.
|
||||||
|
task->fail_sched_loop();
|
||||||
}
|
}
|
||||||
|
|
||||||
// FIXME (#2676): For performance we should do the annihilator
|
// FIXME (#2676): For performance we should do the annihilator
|
||||||
@@ -282,6 +287,7 @@ rust_task::kill() {
|
|||||||
LOG(this, task, "preparing to unwind task: 0x%" PRIxPTR, this);
|
LOG(this, task, "preparing to unwind task: 0x%" PRIxPTR, this);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO(bblum): Move this to rust_builtin.cpp (cleanup)
|
||||||
extern "C" CDECL
|
extern "C" CDECL
|
||||||
bool rust_task_is_unwinding(rust_task *rt) {
|
bool rust_task_is_unwinding(rust_task *rt) {
|
||||||
return rt->unwinding;
|
return rt->unwinding;
|
||||||
@@ -315,10 +321,14 @@ rust_task::begin_failure(char const *expr, char const *file, size_t line) {
|
|||||||
#else
|
#else
|
||||||
die();
|
die();
|
||||||
// FIXME (#908): Need unwinding on windows. This will end up aborting
|
// FIXME (#908): Need unwinding on windows. This will end up aborting
|
||||||
sched_loop->fail();
|
fail_sched_loop();
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void rust_task::fail_sched_loop() {
|
||||||
|
sched_loop->fail();
|
||||||
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
rust_task::unsupervise()
|
rust_task::unsupervise()
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -275,6 +275,10 @@ public:
|
|||||||
void fail();
|
void fail();
|
||||||
void fail(char const *expr, char const *file, size_t line);
|
void fail(char const *expr, char const *file, size_t line);
|
||||||
|
|
||||||
|
// Propagate failure to the entire rust runtime.
|
||||||
|
// TODO(bblum): maybe this can be done at rust-level?
|
||||||
|
void fail_sched_loop();
|
||||||
|
|
||||||
// Disconnect from our supervisor.
|
// Disconnect from our supervisor.
|
||||||
void unsupervise();
|
void unsupervise();
|
||||||
|
|
||||||
|
|||||||
@@ -178,6 +178,7 @@ rust_port_task
|
|||||||
rust_task_inhibit_kill
|
rust_task_inhibit_kill
|
||||||
rust_task_allow_kill
|
rust_task_allow_kill
|
||||||
rust_task_kill_other
|
rust_task_kill_other
|
||||||
|
rust_task_kill_all
|
||||||
rust_create_cond_lock
|
rust_create_cond_lock
|
||||||
rust_destroy_cond_lock
|
rust_destroy_cond_lock
|
||||||
rust_lock_cond_lock
|
rust_lock_cond_lock
|
||||||
|
|||||||
Reference in New Issue
Block a user