Skip to content

Commit 20831d3

Browse files
committed
Linked failure: unidirectional failure with parented() (soon to be renamed)
1 parent 35bd579 commit 20831d3

File tree

1 file changed

+95
-31
lines changed

1 file changed

+95
-31
lines changed

src/libcore/task.rs

Lines changed: 95 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ export run;
4646
export future_result;
4747
export future_task;
4848
export unsupervise;
49+
export parent;
4950
export run_listener;
5051
export run_with;
5152

@@ -169,6 +170,7 @@ type sched_opts = {
169170
*/
170171
type task_opts = {
171172
supervise: bool,
173+
parented: bool,
172174
notify_chan: option<comm::chan<notification>>,
173175
sched: option<sched_opts>,
174176
};
@@ -206,6 +208,7 @@ fn default_task_opts() -> task_opts {
206208

207209
{
208210
supervise: true,
211+
parented: false,
209212
notify_chan: none,
210213
sched: none
211214
}
@@ -362,6 +365,11 @@ fn unsupervise(builder: builder) {
362365
});
363366
}
364367

368+
fn parent(builder: builder) {
369+
//! Configures the new task to be killed if the parent group is killed.
370+
set_opts(builder, { parented: true with get_opts(builder) });
371+
}
372+
365373
fn run_with<A:send>(-builder: builder,
366374
+arg: A,
367375
+f: fn~(+A)) {
@@ -591,15 +599,19 @@ class taskgroup {
591599
// FIXME (#2816): Change dvec to an O(1) data structure (and change 'me'
592600
// to a node-handle or somesuch when so done (or remove the field entirely
593601
// if keyed by *rust_task)).
594-
let tasks: taskgroup_arc; // 'none' means the group already failed.
595602
let me: *rust_task;
596-
let my_pos: uint;
597-
// let parent_group: taskgroup_arc; // FIXME (#1868) (bblum)
603+
// List of tasks with whose fates this one's is intertwined.
604+
let tasks: taskgroup_arc; // 'none' means the group already failed.
605+
let my_pos: uint; // Index into above for this task's slot.
606+
// Lists of tasks who will kill us if they fail, but whom we won't kill.
607+
let parents: option<(taskgroup_arc,uint)>;
598608
let is_main: bool;
599-
new(-tasks: taskgroup_arc, me: *rust_task, my_pos: uint, is_main: bool) {
600-
self.tasks = tasks;
609+
new(me: *rust_task, -tasks: taskgroup_arc, my_pos: uint,
610+
-parents: option<(taskgroup_arc,uint)>, is_main: bool) {
601611
self.me = me;
612+
self.tasks = tasks;
602613
self.my_pos = my_pos;
614+
self.parents = parents;
603615
self.is_main = is_main;
604616
}
605617
// Runs on task exit.
@@ -609,9 +621,17 @@ class taskgroup {
609621
// Take everybody down with us.
610622
kill_taskgroup(self.tasks, self.me, self.my_pos, self.is_main);
611623
} else {
612-
// Remove ourselves from the group.
624+
// Remove ourselves from the group(s).
613625
leave_taskgroup(self.tasks, self.me, self.my_pos);
614626
}
627+
// It doesn't matter whether this happens before or after dealing with
628+
// our own taskgroup, so long as both happen before we die.
629+
alt self.parents {
630+
some((parent_group,pos_in_group)) {
631+
leave_taskgroup(parent_group, self.me, pos_in_group);
632+
}
633+
none { }
634+
}
615635
}
616636
}
617637

@@ -714,7 +734,8 @@ fn share_parent_taskgroup() -> (taskgroup_arc, bool) {
714734
// Main task, doing first spawn ever.
715735
let tasks = arc::exclusive(some((dvec::from_elem(some(me)),
716736
dvec::dvec())));
717-
let group = @taskgroup(tasks.clone(), me, 0, true);
737+
// Main group has no parent group.
738+
let group = @taskgroup(me, tasks.clone(), 0, none, true);
718739
unsafe { local_set(me, taskgroup_key(), group); }
719740
// Tell child task it's also in the main group.
720741
(tasks, true)
@@ -724,21 +745,29 @@ fn share_parent_taskgroup() -> (taskgroup_arc, bool) {
724745

725746
fn spawn_raw(opts: task_opts, +f: fn~()) {
726747
// Decide whether the child needs to be in a new linked failure group.
727-
let (child_tg, is_main) = if opts.supervise {
728-
share_parent_taskgroup()
748+
let ((child_tg, is_main), parent_tg) = if opts.supervise {
749+
// It doesn't mean anything for a linked-spawned-task to have a parent
750+
// group. The spawning task is already bidirectionally linked to it.
751+
(share_parent_taskgroup(), none)
729752
} else {
730753
// Detached from the parent group; create a new (non-main) one.
731-
(arc::exclusive(some((dvec::dvec(),dvec::dvec()))), false)
754+
((arc::exclusive(some((dvec::dvec(),dvec::dvec()))), false),
755+
// Allow the parent to unidirectionally fail the child?
756+
if opts.parented { // FIXME(#1868) rename to unsupervise.
757+
let (pg,_) = share_parent_taskgroup(); some(pg)
758+
} else {
759+
none
760+
})
732761
};
733762

734763
unsafe {
735-
let child_data_ptr = ~mut some((child_tg, f));
764+
let child_data_ptr = ~mut some((child_tg, parent_tg, f));
736765
// Being killed with the unsafe task/closure pointers would leak them.
737766
do unkillable {
738767
// Agh. Get move-mode items into the closure. FIXME (#2829)
739768
let mut child_data = none;
740769
*child_data_ptr <-> child_data;
741-
let (child_tg, f) = option::unwrap(child_data);
770+
let (child_tg, parent_tg, f) = option::unwrap(child_data);
742771
// Create child task.
743772
let new_task = alt opts.sched {
744773
none { rustrt::new_task() }
@@ -748,7 +777,7 @@ fn spawn_raw(opts: task_opts, +f: fn~()) {
748777
// Getting killed after here would leak the task.
749778

750779
let child_wrapper =
751-
make_child_wrapper(new_task, child_tg, is_main, f);
780+
make_child_wrapper(new_task, child_tg, parent_tg, is_main, f);
752781
let fptr = ptr::addr_of(child_wrapper);
753782
let closure: *rust_closure = unsafe::reinterpret_cast(fptr);
754783

@@ -765,28 +794,63 @@ fn spawn_raw(opts: task_opts, +f: fn~()) {
765794
}
766795
}
767796

768-
fn make_child_wrapper(child_task: *rust_task, -child_tg: taskgroup_arc,
769-
is_main: bool, -f: fn~()) -> fn~() {
770-
let child_tg_ptr = ~mut some(child_tg);
797+
// This function returns a closure-wrapper that we pass to the child task.
798+
// In brief, it does the following:
799+
// if enlist_in_group(child_group) {
800+
// if parent_group {
801+
// if !enlist_in_group(parent_group) {
802+
// leave_group(child_group); // Roll back
803+
// ret; // Parent group failed. Don't run child's f().
804+
// }
805+
// }
806+
// stash_taskgroup_data_in_TLS(child_group, parent_group);
807+
// f();
808+
// } else {
809+
// // My group failed. Don't run chid's f().
810+
// }
811+
fn make_child_wrapper(child: *rust_task, -child_tg: taskgroup_arc,
812+
-parent_tg: option<taskgroup_arc>, is_main: bool,
813+
-f: fn~()) -> fn~() {
814+
let child_tg_ptr = ~mut some((child_tg, parent_tg));
771815
fn~() {
772816
// Agh. Get move-mode items into the closure. FIXME (#2829)
773-
let mut child_tg_opt = none;
774-
*child_tg_ptr <-> child_tg_opt;
775-
let child_tg = option::unwrap(child_tg_opt);
817+
let mut tg_data_opt = none;
818+
*child_tg_ptr <-> tg_data_opt;
819+
let (child_tg, parent_tg) = option::unwrap(tg_data_opt);
776820
// Child task runs this code.
777-
// Set up membership in taskgroup. If this returns none, the
778-
// parent was already failing, so don't bother doing anything.
779-
alt enlist_in_taskgroup(child_tg, child_task) {
780-
some(my_index) {
781-
let group =
782-
@taskgroup(child_tg, child_task, my_index, is_main);
783-
unsafe { local_set(child_task, taskgroup_key(), group); }
784-
// Run the child's body.
785-
f();
786-
// TLS cleanup code will exit the taskgroup.
787-
}
788-
none {
821+
// Set up membership in taskgroup. If this returns none, some
822+
// task was already failing, so don't bother doing anything.
823+
alt enlist_in_taskgroup(child_tg, child) {
824+
some(my_pos) {
825+
// Enlist in parent group too. If enlist returns none, a
826+
// parent was failing: don't spawn; leave this group too.
827+
let (pg, enlist_ok) = if parent_tg.is_some() {
828+
let parent_group = option::unwrap(parent_tg);
829+
alt enlist_in_taskgroup(parent_group, child) {
830+
some(my_p_index) {
831+
// Successful enlist.
832+
(some((parent_group, my_p_index)), true)
833+
}
834+
none {
835+
// Couldn't enlist. Have to quit here too.
836+
leave_taskgroup(child_tg, child, my_pos);
837+
(none, false)
838+
}
839+
}
840+
} else {
841+
// No parent group to enlist in. No worry.
842+
(none, true)
843+
};
844+
if enlist_ok {
845+
let group = @taskgroup(child, child_tg, my_pos,
846+
pg, is_main);
847+
unsafe { local_set(child, taskgroup_key(), group); }
848+
// Run the child's body.
849+
f();
850+
// TLS cleanup code will exit the taskgroup.
851+
}
789852
}
853+
none { }
790854
}
791855
}
792856
}

0 commit comments

Comments
 (0)