Replace *rust_task ptrs in taskgroup code with TaskHandle, for transitioning to newsched killing.

This commit is contained in:
Ben Blum
2013-07-12 22:45:19 -04:00
parent e2a42416dd
commit 9bbec651df
3 changed files with 219 additions and 128 deletions

View File

@@ -16,6 +16,7 @@ use either::{Either, Left, Right};
use option::{Option, Some, None}; use option::{Option, Some, None};
use prelude::*; use prelude::*;
use rt::task::Task; use rt::task::Task;
use to_bytes::IterBytes;
use unstable::atomics::{AtomicUint, Acquire, SeqCst}; use unstable::atomics::{AtomicUint, Acquire, SeqCst};
use unstable::sync::{UnsafeAtomicRcBox, LittleLock}; use unstable::sync::{UnsafeAtomicRcBox, LittleLock};
use util; use util;
@@ -194,6 +195,17 @@ impl BlockedTask {
} }
} }
// So that KillHandle can be hashed in the taskgroup bookkeeping code.
impl IterBytes for KillHandle {
fn iter_bytes(&self, lsb0: bool, f: &fn(buf: &[u8]) -> bool) -> bool {
self.data.iter_bytes(lsb0, f)
}
}
impl Eq for KillHandle {
#[inline] fn eq(&self, other: &KillHandle) -> bool { self.data.eq(&other.data) }
#[inline] fn ne(&self, other: &KillHandle) -> bool { self.data.ne(&other.data) }
}
impl KillHandle { impl KillHandle {
pub fn new() -> (KillHandle, KillFlagHandle) { pub fn new() -> (KillHandle, KillFlagHandle) {
let (flag, flag_clone) = let (flag, flag_clone) =

View File

@@ -27,6 +27,7 @@ use super::local_heap::LocalHeap;
use rt::sched::{Scheduler, SchedHandle}; use rt::sched::{Scheduler, SchedHandle};
use rt::stack::{StackSegment, StackPool}; use rt::stack::{StackSegment, StackPool};
use rt::context::Context; use rt::context::Context;
use task::spawn::TCB;
use cell::Cell; use cell::Cell;
pub struct Task { pub struct Task {
@@ -36,6 +37,7 @@ pub struct Task {
logger: StdErrLogger, logger: StdErrLogger,
unwinder: Unwinder, unwinder: Unwinder,
home: Option<SchedHome>, home: Option<SchedHome>,
taskgroup: Option<TCB>,
death: Death, death: Death,
destroyed: bool, destroyed: bool,
coroutine: Option<~Coroutine> coroutine: Option<~Coroutine>
@@ -85,6 +87,7 @@ impl Task {
logger: StdErrLogger, logger: StdErrLogger,
unwinder: Unwinder { unwinding: false }, unwinder: Unwinder { unwinding: false },
home: Some(home), home: Some(home),
taskgroup: None,
death: Death::new(), death: Death::new(),
destroyed: false, destroyed: false,
coroutine: Some(~Coroutine::new(stack_pool, start)) coroutine: Some(~Coroutine::new(stack_pool, start))
@@ -102,6 +105,7 @@ impl Task {
logger: StdErrLogger, logger: StdErrLogger,
home: Some(home), home: Some(home),
unwinder: Unwinder { unwinding: false }, unwinder: Unwinder { unwinding: false },
taskgroup: None,
// FIXME(#7544) make watching optional // FIXME(#7544) make watching optional
death: self.death.new_child(), death: self.death.new_child(),
destroyed: false, destroyed: false,
@@ -121,6 +125,7 @@ impl Task {
} }
self.unwinder.try(f); self.unwinder.try(f);
{ let _ = self.taskgroup.take(); }
self.death.collect_failure(!self.unwinder.unwinding); self.death.collect_failure(!self.unwinder.unwinding);
self.destroy(); self.destroy();
} }

View File

@@ -79,7 +79,7 @@ use cast;
use cell::Cell; use cell::Cell;
use container::MutableMap; use container::MutableMap;
use comm::{Chan, GenericChan}; use comm::{Chan, GenericChan};
use hashmap::HashSet; use hashmap::{HashSet, HashSetConsumeIterator};
use local_data; use local_data;
use task::local_data_priv::{local_get, local_set, OldHandle}; use task::local_data_priv::{local_get, local_set, OldHandle};
use task::rt::rust_task; use task::rt::rust_task;
@@ -88,32 +88,61 @@ use task::{Failure, ManualThreads, PlatformThread, SchedOpts, SingleThreaded};
use task::{Success, TaskOpts, TaskResult, ThreadPerTask}; use task::{Success, TaskOpts, TaskResult, ThreadPerTask};
use task::{ExistingScheduler, SchedulerHandle}; use task::{ExistingScheduler, SchedulerHandle};
use task::unkillable; use task::unkillable;
use to_bytes::IterBytes;
use uint; use uint;
use util; use util;
use unstable::sync::{Exclusive, exclusive}; use unstable::sync::{Exclusive, exclusive};
use rt::{OldTaskContext, TaskContext, SchedulerContext, GlobalContext, context};
use rt::local::Local; use rt::local::Local;
use rt::task::Task; use rt::task::Task;
use rt::kill::KillHandle;
use rt::sched::Scheduler;
use iterator::IteratorUtil; use iterator::IteratorUtil;
#[cfg(test)] use task::default_task_opts; #[cfg(test)] use task::default_task_opts;
#[cfg(test)] use comm; #[cfg(test)] use comm;
#[cfg(test)] use task; #[cfg(test)] use task;
type TaskSet = HashSet<*rust_task>; // Transitionary.
#[deriving(Eq)]
enum TaskHandle {
OldTask(*rust_task),
NewTask(KillHandle),
}
impl Clone for TaskHandle {
fn clone(&self) -> TaskHandle {
match *self {
OldTask(x) => OldTask(x),
NewTask(ref x) => NewTask(x.clone()),
}
}
}
impl IterBytes for TaskHandle {
fn iter_bytes(&self, lsb0: bool, f: &fn(buf: &[u8]) -> bool) -> bool {
match *self {
OldTask(ref x) => x.iter_bytes(lsb0, f),
NewTask(ref x) => x.iter_bytes(lsb0, f),
}
}
}
type TaskSet = HashSet<TaskHandle>;
fn new_taskset() -> TaskSet { fn new_taskset() -> TaskSet {
HashSet::new() HashSet::new()
} }
fn taskset_insert(tasks: &mut TaskSet, task: *rust_task) { fn taskset_insert(tasks: &mut TaskSet, task: TaskHandle) {
let didnt_overwrite = tasks.insert(task); let didnt_overwrite = tasks.insert(task);
assert!(didnt_overwrite); assert!(didnt_overwrite);
} }
fn taskset_remove(tasks: &mut TaskSet, task: *rust_task) { fn taskset_remove(tasks: &mut TaskSet, task: &TaskHandle) {
let was_present = tasks.remove(&task); let was_present = tasks.remove(task);
assert!(was_present); assert!(was_present);
} }
pub fn taskset_each(tasks: &TaskSet, blk: &fn(v: *rust_task) -> bool) -> bool { fn taskset_consume(tasks: TaskSet) -> HashSetConsumeIterator<TaskHandle> {
tasks.iter().advance(|k| blk(*k)) tasks.consume()
} }
// One of these per group of linked-failure tasks. // One of these per group of linked-failure tasks.
@@ -179,25 +208,23 @@ fn access_ancestors<U>(x: &Exclusive<AncestorNode>,
// taskgroups that forward_blk already ran on successfully (Note: bail_blk // taskgroups that forward_blk already ran on successfully (Note: bail_blk
// is NOT called on the block that forward_blk broke on!). // is NOT called on the block that forward_blk broke on!).
// (3) As a bonus, coalesces away all 'dead' taskgroup nodes in the list. // (3) As a bonus, coalesces away all 'dead' taskgroup nodes in the list.
// FIXME(#2190): Change Option<@fn(...)> to Option<&fn(...)>, to save on
// allocations. Once that bug is fixed, changing the sigil should suffice.
fn each_ancestor(list: &mut AncestorList, fn each_ancestor(list: &mut AncestorList,
bail_opt: Option<@fn(TaskGroupInner)>, bail_blk: &fn(TaskGroupInner),
forward_blk: &fn(TaskGroupInner) -> bool) forward_blk: &fn(TaskGroupInner) -> bool)
-> bool { -> bool {
// "Kickoff" call - there was no last generation. // "Kickoff" call - there was no last generation.
return !coalesce(list, bail_opt, forward_blk, uint::max_value); return !coalesce(list, bail_blk, forward_blk, uint::max_value);
// Recursively iterates, and coalesces afterwards if needed. Returns // Recursively iterates, and coalesces afterwards if needed. Returns
// whether or not unwinding is needed (i.e., !successful iteration). // whether or not unwinding is needed (i.e., !successful iteration).
fn coalesce(list: &mut AncestorList, fn coalesce(list: &mut AncestorList,
bail_opt: Option<@fn(TaskGroupInner)>, bail_blk: &fn(TaskGroupInner),
forward_blk: &fn(TaskGroupInner) -> bool, forward_blk: &fn(TaskGroupInner) -> bool,
last_generation: uint) -> bool { last_generation: uint) -> bool {
// Need to swap the list out to use it, to appease borrowck. // Need to swap the list out to use it, to appease borrowck.
let tmp_list = util::replace(&mut *list, AncestorList(None)); let tmp_list = util::replace(&mut *list, AncestorList(None));
let (coalesce_this, early_break) = let (coalesce_this, early_break) =
iterate(&tmp_list, bail_opt, forward_blk, last_generation); iterate(&tmp_list, bail_blk, forward_blk, last_generation);
// What should our next ancestor end up being? // What should our next ancestor end up being?
if coalesce_this.is_some() { if coalesce_this.is_some() {
// Needed coalesce. Our next ancestor becomes our old // Needed coalesce. Our next ancestor becomes our old
@@ -219,7 +246,7 @@ fn each_ancestor(list: &mut AncestorList,
// True if the supplied block did 'break', here or in any recursive // True if the supplied block did 'break', here or in any recursive
// calls. If so, must call the unwinder on all previous nodes. // calls. If so, must call the unwinder on all previous nodes.
fn iterate(ancestors: &AncestorList, fn iterate(ancestors: &AncestorList,
bail_opt: Option<@fn(TaskGroupInner)>, bail_blk: &fn(TaskGroupInner),
forward_blk: &fn(TaskGroupInner) -> bool, forward_blk: &fn(TaskGroupInner) -> bool,
last_generation: uint) last_generation: uint)
-> (Option<AncestorList>, bool) { -> (Option<AncestorList>, bool) {
@@ -257,7 +284,7 @@ fn each_ancestor(list: &mut AncestorList,
None => nobe_is_dead None => nobe_is_dead
}; };
// Call iterator block. (If the group is dead, it's // Call iterator block. (If the group is dead, it's
// safe to skip it. This will leave our *rust_task // safe to skip it. This will leave our TaskHandle
// hanging around in the group even after it's freed, // hanging around in the group even after it's freed,
// but that's ok because, by virtue of the group being // but that's ok because, by virtue of the group being
// dead, nobody will ever kill-all (foreach) over it.) // dead, nobody will ever kill-all (foreach) over it.)
@@ -271,17 +298,15 @@ fn each_ancestor(list: &mut AncestorList,
let mut need_unwind = false; let mut need_unwind = false;
if do_continue { if do_continue {
// NB: Takes many locks! (ancestor nodes & parent groups) // NB: Takes many locks! (ancestor nodes & parent groups)
need_unwind = coalesce(&mut nobe.ancestors, bail_opt, need_unwind = coalesce(&mut nobe.ancestors, |tg| bail_blk(tg),
forward_blk, nobe.generation); forward_blk, nobe.generation);
} }
/*##########################################################* /*##########################################################*
* Step 3: Maybe unwind; compute return info for our caller. * Step 3: Maybe unwind; compute return info for our caller.
*##########################################################*/ *##########################################################*/
if need_unwind && !nobe_is_dead { if need_unwind && !nobe_is_dead {
for bail_opt.iter().advance |bail_blk| {
do with_parent_tg(&mut nobe.parent_group) |tg_opt| { do with_parent_tg(&mut nobe.parent_group) |tg_opt| {
(*bail_blk)(tg_opt) bail_blk(tg_opt)
}
} }
} }
// Decide whether our caller should unwind. // Decide whether our caller should unwind.
@@ -311,8 +336,7 @@ fn each_ancestor(list: &mut AncestorList,
} }
// One of these per task. // One of these per task.
struct TCB { pub struct TCB {
me: *rust_task,
// List of tasks with whose fates this one's is intertwined. // List of tasks with whose fates this one's is intertwined.
tasks: TaskGroupArc, // 'none' means the group has failed. tasks: TaskGroupArc, // 'none' means the group has failed.
// Lists of tasks who will kill us if they fail, but whom we won't kill. // Lists of tasks who will kill us if they fail, but whom we won't kill.
@@ -329,33 +353,34 @@ impl Drop for TCB {
let this: &mut TCB = transmute(self); let this: &mut TCB = transmute(self);
// If we are failing, the whole taskgroup needs to die. // If we are failing, the whole taskgroup needs to die.
if rt::rust_task_is_unwinding(self.me) { do RuntimeGlue::with_task_handle_and_failing |me, failing| {
if failing {
for this.notifier.mut_iter().advance |x| { for this.notifier.mut_iter().advance |x| {
x.failed = true; x.failed = true;
} }
// Take everybody down with us. // Take everybody down with us.
do access_group(&self.tasks) |tg| { do access_group(&self.tasks) |tg| {
kill_taskgroup(tg, self.me, self.is_main); kill_taskgroup(tg, &me, self.is_main);
} }
} else { } else {
// Remove ourselves from the group(s). // Remove ourselves from the group(s).
do access_group(&self.tasks) |tg| { do access_group(&self.tasks) |tg| {
leave_taskgroup(tg, self.me, true); leave_taskgroup(tg, &me, true);
} }
} }
// It doesn't matter whether this happens before or after dealing // It doesn't matter whether this happens before or after dealing
// with our own taskgroup, so long as both happen before we die. // with our own taskgroup, so long as both happen before we die.
// We remove ourself from every ancestor we can, so no cleanup; no // We remove ourself from every ancestor we can, so no cleanup; no
// break. // break.
for each_ancestor(&mut this.ancestors, None) |ancestor_group| { for each_ancestor(&mut this.ancestors, |_| {}) |ancestor_group| {
leave_taskgroup(ancestor_group, self.me, false); leave_taskgroup(ancestor_group, &me, false);
}; };
} }
} }
}
} }
fn TCB(me: *rust_task, pub fn TCB(tasks: TaskGroupArc,
tasks: TaskGroupArc,
ancestors: AncestorList, ancestors: AncestorList,
is_main: bool, is_main: bool,
mut notifier: Option<AutoNotify>) -> TCB { mut notifier: Option<AutoNotify>) -> TCB {
@@ -364,7 +389,6 @@ fn TCB(me: *rust_task,
} }
TCB { TCB {
me: me,
tasks: tasks, tasks: tasks,
ancestors: ancestors, ancestors: ancestors,
is_main: is_main, is_main: is_main,
@@ -391,7 +415,7 @@ fn AutoNotify(chan: Chan<TaskResult>) -> AutoNotify {
} }
} }
fn enlist_in_taskgroup(state: TaskGroupInner, me: *rust_task, fn enlist_in_taskgroup(state: TaskGroupInner, me: TaskHandle,
is_member: bool) -> bool { is_member: bool) -> bool {
let newstate = util::replace(&mut *state, None); let newstate = util::replace(&mut *state, None);
// If 'None', the group was failing. Can't enlist. // If 'None', the group was failing. Can't enlist.
@@ -410,7 +434,7 @@ fn enlist_in_taskgroup(state: TaskGroupInner, me: *rust_task,
} }
// NB: Runs in destructor/post-exit context. Can't 'fail'. // NB: Runs in destructor/post-exit context. Can't 'fail'.
fn leave_taskgroup(state: TaskGroupInner, me: *rust_task, fn leave_taskgroup(state: TaskGroupInner, me: &TaskHandle,
is_member: bool) { is_member: bool) {
let newstate = util::replace(&mut *state, None); let newstate = util::replace(&mut *state, None);
// If 'None', already failing and we've already gotten a kill signal. // If 'None', already failing and we've already gotten a kill signal.
@@ -426,7 +450,7 @@ fn leave_taskgroup(state: TaskGroupInner, me: *rust_task,
} }
// NB: Runs in destructor/post-exit context. Can't 'fail'. // NB: Runs in destructor/post-exit context. Can't 'fail'.
fn kill_taskgroup(state: TaskGroupInner, me: *rust_task, is_main: bool) { fn kill_taskgroup(state: TaskGroupInner, me: &TaskHandle, is_main: bool) {
unsafe { unsafe {
// 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 // having "let mut newstate" here, swapping inside, and iterating
@@ -442,20 +466,21 @@ fn kill_taskgroup(state: TaskGroupInner, me: *rust_task, is_main: bool) {
// That's ok; only one task needs to do the dirty work. (Might also // That's ok; only one task needs to do the dirty work. (Might also
// see 'None' if Somebody already failed and we got a kill signal.) // see 'None' if Somebody already failed and we got a kill signal.)
if newstate.is_some() { if newstate.is_some() {
let group = newstate.unwrap(); let TaskGroupData { members: members, descendants: descendants } =
for taskset_each(&group.members) |sibling| { newstate.unwrap();
for taskset_consume(members).advance() |sibling| {
// Skip self - killing ourself won't do much good. // Skip self - killing ourself won't do much good.
if sibling != me { if &sibling != me {
rt::rust_task_kill_other(sibling); RuntimeGlue::kill_task(sibling);
} }
} }
for taskset_each(&group.descendants) |child| { do taskset_consume(descendants).advance() |child| {
assert!(child != me); assert!(&child != me);
rt::rust_task_kill_other(child); RuntimeGlue::kill_task(child);
} }
// Only one task should ever do this. // Only one task should ever do this.
if is_main { if is_main {
rt::rust_task_kill_all(me); RuntimeGlue::kill_all_tasks(me);
} }
// Do NOT restore state to Some(..)! It stays None to indicate // Do NOT restore state to Some(..)! It stays None to indicate
// that the whole taskgroup is failing, to forbid new spawns. // that the whole taskgroup is failing, to forbid new spawns.
@@ -475,43 +500,103 @@ fn taskgroup_key() -> local_data::Key<@@mut TCB> {
unsafe { cast::transmute((-2, 0)) } unsafe { cast::transmute((-2, 0)) }
} }
fn gen_child_taskgroup(linked: bool, supervised: bool) // Transitionary.
-> (TaskGroupArc, AncestorList, bool) { struct RuntimeGlue;
unsafe { impl RuntimeGlue {
let spawner = rt::rust_get_task(); unsafe fn kill_task(task: TaskHandle) {
/*##################################################################* match task {
* Step 1. Get spawner's taskgroup info. OldTask(ptr) => rt::rust_task_kill_other(ptr),
*##################################################################*/ NewTask(handle) => {
let spawner_group: @@mut TCB = let mut handle = handle;
do local_get(OldHandle(spawner), taskgroup_key()) |group| { do handle.kill().map_consume |killed_task| {
match group { let killed_task = Cell::new(killed_task);
do Local::borrow::<Scheduler, ()> |sched| {
sched.enqueue_task(killed_task.take());
}
};
}
}
}
unsafe fn kill_all_tasks(task: &TaskHandle) {
match *task {
OldTask(ptr) => rt::rust_task_kill_all(ptr),
NewTask(ref _handle) => rtabort!("unimplemented"), // FIXME(#7544)
}
}
fn with_task_handle_and_failing(blk: &fn(TaskHandle, bool)) {
match context() {
OldTaskContext => unsafe {
let me = rt::rust_get_task();
blk(OldTask(me), rt::rust_task_is_unwinding(me))
},
TaskContext => unsafe {
// Can't use safe borrow, because the taskgroup destructor needs to
// access the scheduler again to send kill signals to other tasks.
let me = Local::unsafe_borrow::<Task>();
// FIXME(#7544): Get rid of this clone by passing by-ref.
// Will probably have to wait until the old rt is gone.
blk(NewTask((*me).death.kill_handle.get_ref().clone()),
(*me).unwinder.unwinding)
},
SchedulerContext | GlobalContext => rtabort!("task dying in bad context"),
}
}
fn with_my_taskgroup<U>(blk: &fn(&mut TCB) -> U) -> U {
match context() {
OldTaskContext => unsafe {
let me = rt::rust_get_task();
do local_get(OldHandle(me), taskgroup_key()) |g| {
match g {
None => { None => {
// Main task, doing first spawn ever. Lazily initialise // Main task, doing first spawn ever. Lazily initialise here.
// here.
let mut members = new_taskset(); let mut members = new_taskset();
taskset_insert(&mut members, spawner); taskset_insert(&mut members, OldTask(me));
let tasks = exclusive(Some(TaskGroupData { let tasks = exclusive(Some(TaskGroupData {
members: members, members: members,
descendants: new_taskset(), descendants: new_taskset(),
})); }));
// Main task/group has no ancestors, no notifier, etc. // Main task/group has no ancestors, no notifier, etc.
let group = @@mut TCB(spawner, let group = @@mut TCB(tasks, AncestorList(None), true, None);
tasks, local_set(OldHandle(me), taskgroup_key(), group);
AncestorList(None), blk(&mut **group)
true,
None);
local_set(OldHandle(spawner), taskgroup_key(), group);
group
} }
Some(&group) => group Some(&group) => blk(&mut **group)
} }
}; }
let spawner_group: &mut TCB = *spawner_group; },
TaskContext => unsafe {
// Can't use safe borrow, because creating new hashmaps for the
// tasksets requires an rng, which needs to borrow the sched.
let me = Local::unsafe_borrow::<Task>();
blk(match (*me).taskgroup {
None => {
// Main task, doing first spawn ever. Lazily initialize.
let mut members = new_taskset();
let my_handle = (*me).death.kill_handle.get_ref().clone();
taskset_insert(&mut members, NewTask(my_handle));
let tasks = exclusive(Some(TaskGroupData {
members: members,
descendants: new_taskset(),
}));
let group = TCB(tasks, AncestorList(None), true, None);
(*me).taskgroup = Some(group);
(*me).taskgroup.get_mut_ref()
}
Some(ref mut group) => group,
})
},
SchedulerContext | GlobalContext => rtabort!("spawning in bad context"),
}
}
}
/*##################################################################* fn gen_child_taskgroup(linked: bool, supervised: bool)
* Step 2. Process spawn options for child. -> (TaskGroupArc, AncestorList, bool) {
*##################################################################*/ return do RuntimeGlue::with_my_taskgroup |spawner_group| {
return if linked { if linked {
// Child is in the same group as spawner. // Child is in the same group as spawner.
let g = spawner_group.tasks.clone(); let g = spawner_group.tasks.clone();
// Child's ancestors are spawner's ancestors. // Child's ancestors are spawner's ancestors.
@@ -550,8 +635,8 @@ fn gen_child_taskgroup(linked: bool, supervised: bool)
AncestorList(None) AncestorList(None)
}; };
(g, a, false) (g, a, false)
};
} }
};
fn share_ancestors(ancestors: &mut AncestorList) -> AncestorList { fn share_ancestors(ancestors: &mut AncestorList) -> AncestorList {
// Appease the borrow-checker. Really this wants to be written as: // Appease the borrow-checker. Really this wants to be written as:
@@ -562,6 +647,7 @@ fn gen_child_taskgroup(linked: bool, supervised: bool)
if tmp.is_some() { if tmp.is_some() {
let ancestor_arc = tmp.unwrap(); let ancestor_arc = tmp.unwrap();
let result = ancestor_arc.clone(); let result = ancestor_arc.clone();
error!("cloned ancestors");
**ancestors = Some(ancestor_arc); **ancestors = Some(ancestor_arc);
AncestorList(Some(result)) AncestorList(Some(result))
} else { } else {
@@ -570,9 +656,35 @@ fn gen_child_taskgroup(linked: bool, supervised: bool)
} }
} }
pub fn spawn_raw(opts: TaskOpts, f: ~fn()) { // Set up membership in taskgroup and descendantship in all ancestor
use rt::*; // groups. If any enlistment fails, Some task was already failing, so
// don't let the child task run, and undo every successful enlistment.
fn enlist_many(child: TaskHandle, child_arc: &TaskGroupArc,
ancestors: &mut AncestorList) -> bool {
// Join this taskgroup.
let mut result = do access_group(child_arc) |child_tg| {
enlist_in_taskgroup(child_tg, child.clone(), true) // member
};
if result {
// Unwinding function in case any ancestral enlisting fails
let bail: &fn(TaskGroupInner) = |tg| { leave_taskgroup(tg, &child, false) };
// Attempt to join every ancestor group.
result = do each_ancestor(ancestors, bail) |ancestor_tg| {
// Enlist as a descendant, not as an actual member.
// Descendants don't kill ancestor groups on failure.
enlist_in_taskgroup(ancestor_tg, child.clone(), false)
};
// If any ancestor group fails, need to exit this group too.
if !result {
do access_group(child_arc) |child_tg| {
leave_taskgroup(child_tg, &child, true); // member
}
}
}
result
}
pub fn spawn_raw(opts: TaskOpts, f: ~fn()) {
match context() { match context() {
OldTaskContext => { OldTaskContext => {
spawn_raw_oldsched(opts, f) spawn_raw_oldsched(opts, f)
@@ -590,8 +702,6 @@ pub fn spawn_raw(opts: TaskOpts, f: ~fn()) {
} }
fn spawn_raw_newsched(mut opts: TaskOpts, f: ~fn()) { fn spawn_raw_newsched(mut opts: TaskOpts, f: ~fn()) {
use rt::sched::*;
let f = Cell::new(f); let f = Cell::new(f);
let mut task = unsafe { let mut task = unsafe {
@@ -686,12 +796,8 @@ fn spawn_raw_oldsched(mut opts: TaskOpts, f: ~fn()) {
let notifier = notify_chan.map_consume(|c| AutoNotify(c)); let notifier = notify_chan.map_consume(|c| AutoNotify(c));
if enlist_many(child, &child_arc, &mut ancestors) { if enlist_many(OldTask(child), &child_arc, &mut ancestors) {
let group = @@mut TCB(child, let group = @@mut TCB(child_arc, ancestors, is_main, notifier);
child_arc,
ancestors,
is_main,
notifier);
unsafe { unsafe {
local_set(OldHandle(child), taskgroup_key(), group); local_set(OldHandle(child), taskgroup_key(), group);
} }
@@ -707,38 +813,6 @@ fn spawn_raw_oldsched(mut opts: TaskOpts, f: ~fn()) {
// unsafe { cleanup::annihilate(); } // unsafe { cleanup::annihilate(); }
}; };
return result; return result;
// Set up membership in taskgroup and descendantship in all ancestor
// groups. If any enlistment fails, Some task was already failing, so
// don't let the child task run, and undo every successful enlistment.
fn enlist_many(child: *rust_task, child_arc: &TaskGroupArc,
ancestors: &mut AncestorList) -> bool {
// Join this taskgroup.
let mut result =
do access_group(child_arc) |child_tg| {
enlist_in_taskgroup(child_tg, child, true) // member
};
if result {
// Unwinding function in case any ancestral enlisting fails
let bail: @fn(TaskGroupInner) = |tg| {
leave_taskgroup(tg, child, false)
};
// Attempt to join every ancestor group.
result =
each_ancestor(ancestors, Some(bail), |ancestor_tg| {
// Enlist as a descendant, not as an actual member.
// Descendants don't kill ancestor groups on failure.
enlist_in_taskgroup(ancestor_tg, child, false)
});
// If any ancestor group fails, need to exit this group too.
if !result {
do access_group(child_arc) |child_tg| {
leave_taskgroup(child_tg, child, true); // member
}
}
}
result
}
} }
fn new_task_in_sched(opts: SchedOpts) -> *rust_task { fn new_task_in_sched(opts: SchedOpts) -> *rust_task {