Add watched and indestructible spawn modes.
This commit is contained in:
parent
2183145850
commit
7ad7911222
@ -130,7 +130,7 @@ impl BlockedTask {
|
||||
|
||||
/// Create a blocked task, unless the task was already killed.
|
||||
pub fn try_block(mut task: ~Task) -> Either<~Task, BlockedTask> {
|
||||
if task.death.unkillable > 0 { // FIXME(#7544): || self.indestructible
|
||||
if task.death.unkillable > 0 {
|
||||
Right(Unkillable(task))
|
||||
} else {
|
||||
rtassert!(task.death.kill_handle.is_some());
|
||||
|
@ -148,6 +148,17 @@ pub struct SchedOpts {
|
||||
* * supervised - Propagate failure unidirectionally from parent to child,
|
||||
* but not from child to parent. False by default.
|
||||
*
|
||||
* * watched - Make parent task collect exit status notifications from child
|
||||
* before reporting its own exit status. (This delays the parent
|
||||
* task's death and cleanup until after all transitively watched
|
||||
* children also exit.) True by default.
|
||||
*
|
||||
* * indestructible - Configures the task to ignore kill signals received from
|
||||
* linked failure. This may cause process hangs during
|
||||
* failure if not used carefully, but causes task blocking
|
||||
* code paths (e.g. port recv() calls) to be faster by 2
|
||||
* atomic operations. False by default.
|
||||
*
|
||||
* * notify_chan - Enable lifecycle notifications on the given channel
|
||||
*
|
||||
* * sched - Specify the configuration of a new scheduler to create the task
|
||||
@ -166,6 +177,8 @@ pub struct SchedOpts {
|
||||
pub struct TaskOpts {
|
||||
linked: bool,
|
||||
supervised: bool,
|
||||
watched: bool,
|
||||
indestructible: bool,
|
||||
notify_chan: Option<Chan<TaskResult>>,
|
||||
sched: SchedOpts
|
||||
}
|
||||
@ -217,6 +230,8 @@ impl TaskBuilder {
|
||||
opts: TaskOpts {
|
||||
linked: self.opts.linked,
|
||||
supervised: self.opts.supervised,
|
||||
watched: self.opts.watched,
|
||||
indestructible: self.opts.indestructible,
|
||||
notify_chan: notify_chan,
|
||||
sched: self.opts.sched
|
||||
},
|
||||
@ -232,6 +247,7 @@ impl TaskBuilder {
|
||||
/// the other will not be killed.
|
||||
pub fn unlinked(&mut self) {
|
||||
self.opts.linked = false;
|
||||
self.opts.watched = false;
|
||||
}
|
||||
|
||||
/// Unidirectionally link the child task's failure with the parent's. The
|
||||
@ -240,6 +256,7 @@ impl TaskBuilder {
|
||||
pub fn supervised(&mut self) {
|
||||
self.opts.supervised = true;
|
||||
self.opts.linked = false;
|
||||
self.opts.watched = false;
|
||||
}
|
||||
|
||||
/// Link the child task's and parent task's failures. If either fails, the
|
||||
@ -247,6 +264,26 @@ impl TaskBuilder {
|
||||
pub fn linked(&mut self) {
|
||||
self.opts.linked = true;
|
||||
self.opts.supervised = false;
|
||||
self.opts.watched = true;
|
||||
}
|
||||
|
||||
/// Cause the parent task to collect the child's exit status (and that of
|
||||
/// all transitively-watched grandchildren) before reporting its own.
|
||||
pub fn watched(&mut self) {
|
||||
self.opts.watched = true;
|
||||
}
|
||||
|
||||
/// Allow the child task to outlive the parent task, at the possible cost
|
||||
/// of the parent reporting success even if the child task fails later.
|
||||
pub fn unwatched(&mut self) {
|
||||
self.opts.watched = false;
|
||||
}
|
||||
|
||||
/// Cause the child task to ignore any kill signals received from linked
|
||||
/// failure. This optimizes context switching, at the possible expense of
|
||||
/// process hangs in the case of unexpected failure.
|
||||
pub fn indestructible(&mut self) {
|
||||
self.opts.indestructible = true;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -341,6 +378,8 @@ impl TaskBuilder {
|
||||
let opts = TaskOpts {
|
||||
linked: x.opts.linked,
|
||||
supervised: x.opts.supervised,
|
||||
watched: x.opts.watched,
|
||||
indestructible: x.opts.indestructible,
|
||||
notify_chan: notify_chan,
|
||||
sched: x.opts.sched
|
||||
};
|
||||
@ -407,6 +446,8 @@ pub fn default_task_opts() -> TaskOpts {
|
||||
TaskOpts {
|
||||
linked: true,
|
||||
supervised: false,
|
||||
watched: true,
|
||||
indestructible: false,
|
||||
notify_chan: None,
|
||||
sched: SchedOpts {
|
||||
mode: DefaultScheduler,
|
||||
@ -448,6 +489,17 @@ pub fn spawn_supervised(f: ~fn()) {
|
||||
task.spawn(f)
|
||||
}
|
||||
|
||||
/// Creates a child task that cannot be killed by linked failure. This causes
|
||||
/// its context-switch path to be faster by 2 atomic swap operations.
|
||||
/// (Note that this convenience wrapper still uses linked-failure, so the
|
||||
/// child's children will still be killable by the parent. For the fastest
|
||||
/// possible spawn mode, use task::task().unlinked().indestructible().spawn.)
|
||||
pub fn spawn_indestructible(f: ~fn()) {
|
||||
let mut task = task();
|
||||
task.indestructible();
|
||||
task.spawn(f)
|
||||
}
|
||||
|
||||
pub fn spawn_with<A:Send>(arg: A, f: ~fn(v: A)) {
|
||||
/*!
|
||||
* Runs a task, while transfering ownership of one argument to the
|
||||
@ -1209,3 +1261,61 @@ fn test_simple_newsched_spawn() {
|
||||
}
|
||||
}
|
||||
|
||||
#[test] #[ignore(cfg(windows))]
|
||||
fn test_spawn_watched() {
|
||||
use rt::test::{run_in_newsched_task, spawntask_try};
|
||||
do run_in_newsched_task {
|
||||
let result = do spawntask_try {
|
||||
let mut t = task();
|
||||
t.unlinked();
|
||||
t.watched();
|
||||
do t.spawn {
|
||||
let mut t = task();
|
||||
t.unlinked();
|
||||
t.watched();
|
||||
do t.spawn {
|
||||
task::yield();
|
||||
fail!();
|
||||
}
|
||||
}
|
||||
};
|
||||
assert!(result.is_err());
|
||||
}
|
||||
}
|
||||
|
||||
#[test] #[ignore(cfg(windows))]
|
||||
fn test_indestructible() {
|
||||
use rt::test::{run_in_newsched_task, spawntask_try};
|
||||
do run_in_newsched_task {
|
||||
let result = do spawntask_try {
|
||||
let mut t = task();
|
||||
t.watched();
|
||||
t.supervised();
|
||||
t.indestructible();
|
||||
do t.spawn {
|
||||
let (p1, _c1) = stream::<()>();
|
||||
let (p2, c2) = stream::<()>();
|
||||
let (p3, c3) = stream::<()>();
|
||||
let mut t = task();
|
||||
t.unwatched();
|
||||
do t.spawn {
|
||||
do (|| {
|
||||
p1.recv(); // would deadlock if not killed
|
||||
}).finally {
|
||||
c2.send(());
|
||||
};
|
||||
}
|
||||
let mut t = task();
|
||||
t.unwatched();
|
||||
do t.spawn {
|
||||
p3.recv();
|
||||
task::yield();
|
||||
fail!();
|
||||
}
|
||||
c3.send(());
|
||||
p2.recv();
|
||||
}
|
||||
};
|
||||
assert!(result.is_ok());
|
||||
}
|
||||
}
|
||||
|
@ -671,6 +671,7 @@ pub fn spawn_raw(opts: TaskOpts, f: ~fn()) {
|
||||
|
||||
fn spawn_raw_newsched(mut opts: TaskOpts, f: ~fn()) {
|
||||
let child_data = Cell::new(gen_child_taskgroup(opts.linked, opts.supervised));
|
||||
let indestructible = opts.indestructible;
|
||||
|
||||
let child_wrapper: ~fn() = || {
|
||||
// Child task runs this code.
|
||||
@ -692,7 +693,11 @@ fn spawn_raw_newsched(mut opts: TaskOpts, f: ~fn()) {
|
||||
};
|
||||
// Should be run after the local-borrowed task is returned.
|
||||
if enlist_success {
|
||||
f()
|
||||
if indestructible {
|
||||
unsafe { do unkillable { f() } }
|
||||
} else {
|
||||
f()
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@ -700,13 +705,13 @@ fn spawn_raw_newsched(mut opts: TaskOpts, f: ~fn()) {
|
||||
let sched = Local::unsafe_borrow::<Scheduler>();
|
||||
rtdebug!("unsafe borrowed sched");
|
||||
|
||||
if opts.linked {
|
||||
if opts.watched {
|
||||
let child_wrapper = Cell::new(child_wrapper);
|
||||
do Local::borrow::<Task, ~Task>() |running_task| {
|
||||
~running_task.new_child(&mut (*sched).stack_pool, child_wrapper.take())
|
||||
}
|
||||
} else {
|
||||
// An unlinked task is a new root in the task tree
|
||||
// An unwatched task is a new root in the exit-code propagation tree
|
||||
~Task::new_root(&mut (*sched).stack_pool, child_wrapper)
|
||||
}
|
||||
};
|
||||
@ -848,6 +853,7 @@ fn test_spawn_raw_simple() {
|
||||
fn test_spawn_raw_unsupervise() {
|
||||
let opts = task::TaskOpts {
|
||||
linked: false,
|
||||
watched: false,
|
||||
notify_chan: None,
|
||||
.. default_task_opts()
|
||||
};
|
||||
@ -878,6 +884,7 @@ fn test_spawn_raw_notify_failure() {
|
||||
|
||||
let opts = task::TaskOpts {
|
||||
linked: false,
|
||||
watched: false,
|
||||
notify_chan: Some(notify_ch),
|
||||
.. default_task_opts()
|
||||
};
|
||||
|
Loading…
x
Reference in New Issue
Block a user