Replace *rust_task ptrs in taskgroup code with TaskHandle, for transitioning to newsched killing.
This commit is contained in:
@@ -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) =
|
||||||
|
|||||||
@@ -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();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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 {
|
||||||
|
|||||||
Reference in New Issue
Block a user