Skip to content

Commit 7ad7911

Browse files
committed
Add watched and indestructible spawn modes.
1 parent 2183145 commit 7ad7911

File tree

3 files changed

+121
-4
lines changed

3 files changed

+121
-4
lines changed

src/libstd/rt/kill.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -130,7 +130,7 @@ impl BlockedTask {
130130

131131
/// Create a blocked task, unless the task was already killed.
132132
pub fn try_block(mut task: ~Task) -> Either<~Task, BlockedTask> {
133-
if task.death.unkillable > 0 { // FIXME(#7544): || self.indestructible
133+
if task.death.unkillable > 0 {
134134
Right(Unkillable(task))
135135
} else {
136136
rtassert!(task.death.kill_handle.is_some());

src/libstd/task/mod.rs

Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -148,6 +148,17 @@ pub struct SchedOpts {
148148
* * supervised - Propagate failure unidirectionally from parent to child,
149149
* but not from child to parent. False by default.
150150
*
151+
* * watched - Make parent task collect exit status notifications from child
152+
* before reporting its own exit status. (This delays the parent
153+
* task's death and cleanup until after all transitively watched
154+
* children also exit.) True by default.
155+
*
156+
* * indestructible - Configures the task to ignore kill signals received from
157+
* linked failure. This may cause process hangs during
158+
* failure if not used carefully, but causes task blocking
159+
* code paths (e.g. port recv() calls) to be faster by 2
160+
* atomic operations. False by default.
161+
*
151162
* * notify_chan - Enable lifecycle notifications on the given channel
152163
*
153164
* * sched - Specify the configuration of a new scheduler to create the task
@@ -166,6 +177,8 @@ pub struct SchedOpts {
166177
pub struct TaskOpts {
167178
linked: bool,
168179
supervised: bool,
180+
watched: bool,
181+
indestructible: bool,
169182
notify_chan: Option<Chan<TaskResult>>,
170183
sched: SchedOpts
171184
}
@@ -217,6 +230,8 @@ impl TaskBuilder {
217230
opts: TaskOpts {
218231
linked: self.opts.linked,
219232
supervised: self.opts.supervised,
233+
watched: self.opts.watched,
234+
indestructible: self.opts.indestructible,
220235
notify_chan: notify_chan,
221236
sched: self.opts.sched
222237
},
@@ -232,6 +247,7 @@ impl TaskBuilder {
232247
/// the other will not be killed.
233248
pub fn unlinked(&mut self) {
234249
self.opts.linked = false;
250+
self.opts.watched = false;
235251
}
236252

237253
/// Unidirectionally link the child task's failure with the parent's. The
@@ -240,13 +256,34 @@ impl TaskBuilder {
240256
pub fn supervised(&mut self) {
241257
self.opts.supervised = true;
242258
self.opts.linked = false;
259+
self.opts.watched = false;
243260
}
244261

245262
/// Link the child task's and parent task's failures. If either fails, the
246263
/// other will be killed.
247264
pub fn linked(&mut self) {
248265
self.opts.linked = true;
249266
self.opts.supervised = false;
267+
self.opts.watched = true;
268+
}
269+
270+
/// Cause the parent task to collect the child's exit status (and that of
271+
/// all transitively-watched grandchildren) before reporting its own.
272+
pub fn watched(&mut self) {
273+
self.opts.watched = true;
274+
}
275+
276+
/// Allow the child task to outlive the parent task, at the possible cost
277+
/// of the parent reporting success even if the child task fails later.
278+
pub fn unwatched(&mut self) {
279+
self.opts.watched = false;
280+
}
281+
282+
/// Cause the child task to ignore any kill signals received from linked
283+
/// failure. This optimizes context switching, at the possible expense of
284+
/// process hangs in the case of unexpected failure.
285+
pub fn indestructible(&mut self) {
286+
self.opts.indestructible = true;
250287
}
251288

252289
/**
@@ -341,6 +378,8 @@ impl TaskBuilder {
341378
let opts = TaskOpts {
342379
linked: x.opts.linked,
343380
supervised: x.opts.supervised,
381+
watched: x.opts.watched,
382+
indestructible: x.opts.indestructible,
344383
notify_chan: notify_chan,
345384
sched: x.opts.sched
346385
};
@@ -407,6 +446,8 @@ pub fn default_task_opts() -> TaskOpts {
407446
TaskOpts {
408447
linked: true,
409448
supervised: false,
449+
watched: true,
450+
indestructible: false,
410451
notify_chan: None,
411452
sched: SchedOpts {
412453
mode: DefaultScheduler,
@@ -448,6 +489,17 @@ pub fn spawn_supervised(f: ~fn()) {
448489
task.spawn(f)
449490
}
450491

492+
/// Creates a child task that cannot be killed by linked failure. This causes
493+
/// its context-switch path to be faster by 2 atomic swap operations.
494+
/// (Note that this convenience wrapper still uses linked-failure, so the
495+
/// child's children will still be killable by the parent. For the fastest
496+
/// possible spawn mode, use task::task().unlinked().indestructible().spawn.)
497+
pub fn spawn_indestructible(f: ~fn()) {
498+
let mut task = task();
499+
task.indestructible();
500+
task.spawn(f)
501+
}
502+
451503
pub fn spawn_with<A:Send>(arg: A, f: ~fn(v: A)) {
452504
/*!
453505
* Runs a task, while transfering ownership of one argument to the
@@ -1209,3 +1261,61 @@ fn test_simple_newsched_spawn() {
12091261
}
12101262
}
12111263

1264+
#[test] #[ignore(cfg(windows))]
1265+
fn test_spawn_watched() {
1266+
use rt::test::{run_in_newsched_task, spawntask_try};
1267+
do run_in_newsched_task {
1268+
let result = do spawntask_try {
1269+
let mut t = task();
1270+
t.unlinked();
1271+
t.watched();
1272+
do t.spawn {
1273+
let mut t = task();
1274+
t.unlinked();
1275+
t.watched();
1276+
do t.spawn {
1277+
task::yield();
1278+
fail!();
1279+
}
1280+
}
1281+
};
1282+
assert!(result.is_err());
1283+
}
1284+
}
1285+
1286+
#[test] #[ignore(cfg(windows))]
1287+
fn test_indestructible() {
1288+
use rt::test::{run_in_newsched_task, spawntask_try};
1289+
do run_in_newsched_task {
1290+
let result = do spawntask_try {
1291+
let mut t = task();
1292+
t.watched();
1293+
t.supervised();
1294+
t.indestructible();
1295+
do t.spawn {
1296+
let (p1, _c1) = stream::<()>();
1297+
let (p2, c2) = stream::<()>();
1298+
let (p3, c3) = stream::<()>();
1299+
let mut t = task();
1300+
t.unwatched();
1301+
do t.spawn {
1302+
do (|| {
1303+
p1.recv(); // would deadlock if not killed
1304+
}).finally {
1305+
c2.send(());
1306+
};
1307+
}
1308+
let mut t = task();
1309+
t.unwatched();
1310+
do t.spawn {
1311+
p3.recv();
1312+
task::yield();
1313+
fail!();
1314+
}
1315+
c3.send(());
1316+
p2.recv();
1317+
}
1318+
};
1319+
assert!(result.is_ok());
1320+
}
1321+
}

src/libstd/task/spawn.rs

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -671,6 +671,7 @@ pub fn spawn_raw(opts: TaskOpts, f: ~fn()) {
671671

672672
fn spawn_raw_newsched(mut opts: TaskOpts, f: ~fn()) {
673673
let child_data = Cell::new(gen_child_taskgroup(opts.linked, opts.supervised));
674+
let indestructible = opts.indestructible;
674675

675676
let child_wrapper: ~fn() = || {
676677
// Child task runs this code.
@@ -692,21 +693,25 @@ fn spawn_raw_newsched(mut opts: TaskOpts, f: ~fn()) {
692693
};
693694
// Should be run after the local-borrowed task is returned.
694695
if enlist_success {
695-
f()
696+
if indestructible {
697+
unsafe { do unkillable { f() } }
698+
} else {
699+
f()
700+
}
696701
}
697702
};
698703

699704
let mut task = unsafe {
700705
let sched = Local::unsafe_borrow::<Scheduler>();
701706
rtdebug!("unsafe borrowed sched");
702707

703-
if opts.linked {
708+
if opts.watched {
704709
let child_wrapper = Cell::new(child_wrapper);
705710
do Local::borrow::<Task, ~Task>() |running_task| {
706711
~running_task.new_child(&mut (*sched).stack_pool, child_wrapper.take())
707712
}
708713
} else {
709-
// An unlinked task is a new root in the task tree
714+
// An unwatched task is a new root in the exit-code propagation tree
710715
~Task::new_root(&mut (*sched).stack_pool, child_wrapper)
711716
}
712717
};
@@ -848,6 +853,7 @@ fn test_spawn_raw_simple() {
848853
fn test_spawn_raw_unsupervise() {
849854
let opts = task::TaskOpts {
850855
linked: false,
856+
watched: false,
851857
notify_chan: None,
852858
.. default_task_opts()
853859
};
@@ -878,6 +884,7 @@ fn test_spawn_raw_notify_failure() {
878884

879885
let opts = task::TaskOpts {
880886
linked: false,
887+
watched: false,
881888
notify_chan: Some(notify_ch),
882889
.. default_task_opts()
883890
};

0 commit comments

Comments
 (0)