Skip to content

Commit c936ae5

Browse files
committed
Linked failure: Make joining a taskgroup O(1)
1 parent e0ea67a commit c936ae5

File tree

1 file changed

+33
-20
lines changed

1 file changed

+33
-20
lines changed

src/libcore/task.rs

Lines changed: 33 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -584,7 +584,8 @@ type rust_closure = libc::c_void;
584584

585585
/* linked failure */
586586

587-
type taskgroup_arc = arc::exclusive<option<dvec::dvec<option<*rust_task>>>>;
587+
type taskgroup_arc =
588+
arc::exclusive<option<(dvec::dvec<option<*rust_task>>,dvec::dvec<uint>)>>;
588589

589590
class taskgroup {
590591
// FIXME (#2816): Change dvec to an O(1) data structure (and change 'me'
@@ -594,9 +595,6 @@ class taskgroup {
594595
let me: *rust_task;
595596
let my_pos: uint;
596597
// let parent_group: taskgroup_arc; // FIXME (#1868) (bblum)
597-
// FIXME (#1868) XXX bblum: add a list of empty slots to get runtime back
598-
// Indicates whether this is the main (root) taskgroup. If so, failure
599-
// here should take down the entire runtime.
600598
let is_main: bool;
601599
new(-tasks: taskgroup_arc, me: *rust_task, my_pos: uint, is_main: bool) {
602600
self.tasks = tasks;
@@ -621,29 +619,40 @@ fn enlist_in_taskgroup(group_arc: taskgroup_arc,
621619
me: *rust_task) -> option<uint> {
622620
do group_arc.with |_c, state| {
623621
// If 'none', the group was failing. Can't enlist.
624-
do state.map |tasks| {
622+
let mut newstate = none;
623+
*state <-> newstate;
624+
if newstate.is_some() {
625+
let (tasks,empty_slots) = option::unwrap(newstate);
625626
// Try to find an empty slot.
626-
alt tasks.position(|x| x == none) {
627-
some(empty_index) {
628-
tasks.set_elt(empty_index, some(me));
629-
empty_index
630-
}
631-
none {
632-
tasks.push(some(me));
633-
tasks.len() - 1
634-
}
635-
}
627+
let slotno = if empty_slots.len() > 0 {
628+
let empty_index = empty_slots.pop();
629+
assert tasks[empty_index] == none;
630+
tasks.set_elt(empty_index, some(me));
631+
empty_index
632+
} else {
633+
tasks.push(some(me));
634+
tasks.len() - 1
635+
};
636+
*state = some((tasks,empty_slots));
637+
some(slotno)
638+
} else {
639+
none
636640
}
637641
}
638642
}
639643

640644
// NB: Runs in destructor/post-exit context. Can't 'fail'.
641645
fn leave_taskgroup(group_arc: taskgroup_arc, me: *rust_task, index: uint) {
642646
do group_arc.with |_c, state| {
647+
let mut newstate = none;
648+
*state <-> newstate;
643649
// If 'none', already failing and we've already gotten a kill signal.
644-
do state.map |tasks| {
650+
if newstate.is_some() {
651+
let (tasks,empty_slots) = option::unwrap(newstate);
645652
assert tasks[index] == some(me);
646653
tasks.set_elt(index, none);
654+
empty_slots.push(index);
655+
*state = some((tasks,empty_slots));
647656
};
648657
};
649658
}
@@ -664,7 +673,8 @@ fn kill_taskgroup(group_arc: taskgroup_arc, me: *rust_task, index: uint,
664673
// Might already be none, if somebody is failing simultaneously.
665674
// That's ok; only one task needs to do the dirty work. (Might also
666675
// see 'none' if somebody already failed and we got a kill signal.)
667-
do newstate.map |tasks| {
676+
if newstate.is_some() {
677+
let (tasks,_empty_slots) = option::unwrap(newstate);
668678
// First remove ourself (killing ourself won't do much good). This
669679
// is duplicated here to avoid having to lock twice.
670680
assert tasks[index] == some(me);
@@ -679,7 +689,9 @@ fn kill_taskgroup(group_arc: taskgroup_arc, me: *rust_task, index: uint,
679689
if is_main {
680690
rustrt::rust_task_kill_all(me);
681691
}
682-
};
692+
// Do NOT restore state to some(..)! It stays none to indicate
693+
// that the whole taskgroup is failing, to forbid new spawns.
694+
}
683695
// (note: multiple tasks may reach this point)
684696
};
685697
}
@@ -700,7 +712,8 @@ fn share_parent_taskgroup() -> (taskgroup_arc, bool) {
700712
}
701713
none {
702714
// Main task, doing first spawn ever.
703-
let tasks = arc::exclusive(some(dvec::from_elem(some(me))));
715+
let tasks = arc::exclusive(some((dvec::from_elem(some(me)),
716+
dvec::dvec())));
704717
let group = @taskgroup(tasks.clone(), me, 0, true);
705718
unsafe { local_set(me, taskgroup_key(), group); }
706719
// Tell child task it's also in the main group.
@@ -715,7 +728,7 @@ fn spawn_raw(opts: task_opts, +f: fn~()) {
715728
share_parent_taskgroup()
716729
} else {
717730
// Detached from the parent group; create a new (non-main) one.
718-
(arc::exclusive(some(dvec::from_elem(none))), false)
731+
(arc::exclusive(some((dvec::dvec(),dvec::dvec()))), false)
719732
};
720733

721734
unsafe {

0 commit comments

Comments
 (0)