Skip to content

Commit d338879

Browse files
committed
Reintroduce linked failure (killing runtime)
This reverts commit 5724c64.
1 parent 62575d9 commit d338879

File tree

6 files changed

+59
-23
lines changed

6 files changed

+59
-23
lines changed

src/libcore/task.rs

Lines changed: 36 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -591,16 +591,21 @@ class taskgroup {
591591
let my_pos: uint;
592592
// let parent_group: taskgroup_arc; // TODO(bblum)
593593
// TODO XXX bblum: add a list of empty slots to get runtime back
594-
let mut failed: bool;
595-
new(-tasks: taskgroup_arc, me: *rust_task, my_pos: uint) {
596-
self.tasks = tasks; self.me = me; self.my_pos = my_pos;
597-
self.failed = true; // This will get un-set on successful exit.
594+
// Indicates whether this is the main (root) taskgroup. If so, failure
595+
// here should take down the entire runtime.
596+
let is_main: bool;
597+
new(-tasks: taskgroup_arc, me: *rust_task, my_pos: uint, is_main: bool) {
598+
self.tasks = tasks;
599+
self.me = me;
600+
self.my_pos = my_pos;
601+
self.is_main = is_main;
598602
}
599603
// Runs on task exit.
600604
drop {
601-
if self.failed {
605+
// If we are failing, the whole taskgroup needs to die.
606+
if rustrt::rust_task_is_unwinding(self.me) {
602607
// Take everybody down with us.
603-
kill_taskgroup(self.tasks, self.me, self.my_pos);
608+
kill_taskgroup(self.tasks, self.me, self.my_pos, self.is_main);
604609
} else {
605610
// Remove ourselves from the group.
606611
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) {
642647
}
643648

644649
// NB: Runs in destructor/post-exit context. Can't 'fail'.
645-
fn kill_taskgroup(group_arc: taskgroup_arc, me: *rust_task, index: uint) {
650+
fn kill_taskgroup(group_arc: taskgroup_arc, me: *rust_task, index: uint,
651+
is_main: bool) {
646652
// NB: We could do the killing iteration outside of the group arc, by
647653
// having "let mut newstate" here, swapping inside, and iterating after.
648654
// 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) {
667673
rustrt::rust_task_kill_other(task);
668674
};
669675
}
676+
// Only one task should ever do this.
677+
if is_main {
678+
rustrt::rust_task_kill_all(me);
679+
}
670680
};
681+
// (note: multiple tasks may reach this point)
671682
};
672683
}
673684

674-
fn share_parent_taskgroup() -> taskgroup_arc {
685+
fn share_parent_taskgroup() -> (taskgroup_arc, bool) {
675686
let me = rustrt::rust_get_task();
676687
alt unsafe { local_get(me, taskgroup_key) } {
677688
some(group) {
678-
group.tasks.clone()
689+
// Clone the shared state for the child; propagate main-ness.
690+
(group.tasks.clone(), group.is_main)
679691
}
680692
none {
681-
/* Main task, doing first spawn ever. */
693+
// Main task, doing first spawn ever.
682694
let tasks = arc::exclusive(some(dvec::from_elem(some(me))));
683-
let group = @taskgroup(tasks.clone(), me, 0);
695+
let group = @taskgroup(tasks.clone(), me, 0, true);
684696
unsafe { local_set(me, taskgroup_key, group); }
685-
tasks
697+
// Tell child task it's also in the main group.
698+
(tasks, true)
686699
}
687700
}
688701
}
689702

690703
fn spawn_raw(opts: task_opts, +f: fn~()) {
691704
// Decide whether the child needs to be in a new linked failure group.
692-
let child_tg: taskgroup_arc = if opts.supervise {
705+
let (child_tg, is_main) = if opts.supervise {
693706
share_parent_taskgroup()
694707
} else {
695-
arc::exclusive(some(dvec::from_elem(none)))
708+
// Detached from the parent group; create a new (non-main) one.
709+
(arc::exclusive(some(dvec::from_elem(none))), false)
696710
};
697711

698712
unsafe {
@@ -712,7 +726,8 @@ fn spawn_raw(opts: task_opts, +f: fn~()) {
712726
// Getting killed after here would leak the task.
713727

714728
let child_wrapper =
715-
make_child_wrapper(new_task, child_tg, opts.supervise, f);
729+
make_child_wrapper(new_task, child_tg,
730+
opts.supervise, is_main, f);
716731
let fptr = ptr::addr_of(child_wrapper);
717732
let closure: *rust_closure = unsafe::reinterpret_cast(fptr);
718733

@@ -730,7 +745,8 @@ fn spawn_raw(opts: task_opts, +f: fn~()) {
730745
}
731746

732747
fn make_child_wrapper(child_task: *rust_task, -child_tg: taskgroup_arc,
733-
supervise: bool, -f: fn~()) -> fn~() {
748+
supervise: bool, is_main: bool,
749+
-f: fn~()) -> fn~() {
734750
let child_tg_ptr = ~mut some(child_tg);
735751
fn~() {
736752
// Agh. Get move-mode items into the closure. FIXME (#2829)
@@ -746,13 +762,12 @@ fn spawn_raw(opts: task_opts, +f: fn~()) {
746762
// parent was already failing, so don't bother doing anything.
747763
alt enlist_in_taskgroup(child_tg, child_task) {
748764
some(my_index) {
749-
let group = @taskgroup(child_tg, child_task, my_index);
765+
let group =
766+
@taskgroup(child_tg, child_task, my_index, is_main);
750767
unsafe { local_set(child_task, taskgroup_key, group); }
751768
// Run the child's body.
752769
f();
753-
// Report successful exit. (TLS cleanup code will tear
754-
// down the group.)
755-
group.failed = false;
770+
// TLS cleanup code will exit the taskgroup.
756771
}
757772
none { }
758773
}
@@ -1006,6 +1021,7 @@ extern mod rustrt {
10061021
fn rust_task_inhibit_kill();
10071022
fn rust_task_allow_kill();
10081023
fn rust_task_kill_other(task: *rust_task);
1024+
fn rust_task_kill_all(task: *rust_task);
10091025

10101026
#[rust_stack]
10111027
fn rust_get_task_local_data(task: *rust_task) -> *libc::c_void;

src/rt/rust_builtin.cpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -863,6 +863,11 @@ rust_task_kill_other(rust_task *task) { /* Used for linked failure */
863863
task->kill();
864864
}
865865

866+
extern "C" void
867+
rust_task_kill_all(rust_task *task) {
868+
task->fail_sched_loop();
869+
}
870+
866871
extern "C" rust_cond_lock*
867872
rust_create_cond_lock() {
868873
return new rust_cond_lock();

src/rt/rust_sched_loop.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -260,8 +260,8 @@ rust_task *
260260
rust_sched_loop::create_task(rust_task *spawner, const char *name) {
261261
rust_task *task =
262262
new (this->kernel, "rust_task")
263-
rust_task (this, task_state_newborn,
264-
spawner, name, kernel->env->min_stack_size);
263+
rust_task(this, task_state_newborn,
264+
spawner, name, kernel->env->min_stack_size);
265265
DLOG(this, task, "created task: " PTR ", spawner: %s, name: %s",
266266
task, spawner ? spawner->name : "null", name);
267267

src/rt/rust_task.cpp

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -129,6 +129,11 @@ cleanup_task(cleanup_args *args) {
129129
// assert(task->task_local_data != NULL);
130130
task->task_local_data_cleanup(task->task_local_data);
131131
task->task_local_data = NULL;
132+
} else if (threw_exception) {
133+
// Edge case: If main never spawns any tasks, but fails anyway, TLS
134+
// won't be around to take down the kernel (task.rs:kill_taskgroup,
135+
// rust_task_kill_all). Do it here instead.
136+
task->fail_sched_loop();
132137
}
133138

134139
// FIXME (#2676): For performance we should do the annihilator
@@ -282,6 +287,7 @@ rust_task::kill() {
282287
LOG(this, task, "preparing to unwind task: 0x%" PRIxPTR, this);
283288
}
284289

290+
// TODO(bblum): Move this to rust_builtin.cpp (cleanup)
285291
extern "C" CDECL
286292
bool rust_task_is_unwinding(rust_task *rt) {
287293
return rt->unwinding;
@@ -315,10 +321,14 @@ rust_task::begin_failure(char const *expr, char const *file, size_t line) {
315321
#else
316322
die();
317323
// FIXME (#908): Need unwinding on windows. This will end up aborting
318-
sched_loop->fail();
324+
fail_sched_loop();
319325
#endif
320326
}
321327

328+
void rust_task::fail_sched_loop() {
329+
sched_loop->fail();
330+
}
331+
322332
void
323333
rust_task::unsupervise()
324334
{

src/rt/rust_task.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -275,6 +275,10 @@ rust_task : public kernel_owned<rust_task>
275275
void fail();
276276
void fail(char const *expr, char const *file, size_t line);
277277

278+
// Propagate failure to the entire rust runtime.
279+
// TODO(bblum): maybe this can be done at rust-level?
280+
void fail_sched_loop();
281+
278282
// Disconnect from our supervisor.
279283
void unsupervise();
280284

src/rt/rustrt.def.in

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -178,6 +178,7 @@ rust_port_task
178178
rust_task_inhibit_kill
179179
rust_task_allow_kill
180180
rust_task_kill_other
181+
rust_task_kill_all
181182
rust_create_cond_lock
182183
rust_destroy_cond_lock
183184
rust_lock_cond_lock

0 commit comments

Comments
 (0)