Skip to content

Commit d30acc0

Browse files
committed
---
yaml --- r: 146815 b: refs/heads/try2 c: 6acf227 h: refs/heads/master i: 146813: e491930 146811: c41c48d 146807: d03ffb6 146799: 87d8465 146783: f15cb6a 146751: 0e67e03 146687: 06c094d v: v3
1 parent 5ce5d1d commit d30acc0

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

63 files changed

+2706
-790
lines changed

[refs]

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ refs/heads/snap-stage3: 78a7676898d9f80ab540c6df5d4c9ce35bb50463
55
refs/heads/try: 519addf6277dbafccbb4159db4b710c37eaa2ec5
66
refs/tags/release-0.1: 1f5c5126e96c79d22cb7862f75304136e204f105
77
refs/heads/ndm: f3868061cd7988080c30d6d5bf352a5a5fe2460b
8-
refs/heads/try2: 1746c0269d356a93aaef367541872d363651cb23
8+
refs/heads/try2: 6acf227cc8db09ca25d274918f860849c41df19a
99
refs/heads/dist-snap: ba4081a5a8573875fed17545846f6f6902c8ba8d
1010
refs/tags/release-0.2: c870d2dffb391e14efb05aa27898f1f6333a9596
1111
refs/tags/release-0.3: b5f0d0f648d9a6153664837026ba1be43d3e2503

branches/try2/doc/rust.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2349,9 +2349,9 @@ Indices are zero-based, and may be of any integral type. Vector access
23492349
is bounds-checked at run-time. When the check fails, it will put the
23502350
task in a _failing state_.
23512351

2352-
~~~~ {.xfail-test}
2352+
~~~~
23532353
# use std::task;
2354-
# do task::spawn {
2354+
# do task::spawn_unlinked {
23552355
23562356
([1, 2, 3, 4])[0];
23572357
(["a", "b"])[10]; // fails

branches/try2/doc/tutorial-tasks.md

Lines changed: 111 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -402,6 +402,22 @@ freeing memory along the way---and then exits. Unlike exceptions in C++,
402402
exceptions in Rust are unrecoverable within a single task: once a task fails,
403403
there is no way to "catch" the exception.
404404

405+
All tasks are, by default, _linked_ to each other. That means that the fates
406+
of all tasks are intertwined: if one fails, so do all the others.
407+
408+
~~~{.xfail-test .linked-failure}
409+
# use std::task::spawn;
410+
# use std::task;
411+
# fn do_some_work() { loop { task::yield() } }
412+
# do task::try {
413+
// Create a child task that fails
414+
do spawn { fail!() }
415+
416+
// This will also fail because the task we spawned failed
417+
do_some_work();
418+
# };
419+
~~~
420+
405421
While it isn't possible for a task to recover from failure, tasks may notify
406422
each other of failure. The simplest way of handling task failure is with the
407423
`try` function, which is similar to `spawn`, but immediately blocks waiting
@@ -448,7 +464,101 @@ it trips, indicates an unrecoverable logic error); in other cases you
448464
might want to contain the failure at a certain boundary (perhaps a
449465
small piece of input from the outside world, which you happen to be
450466
processing in parallel, is malformed and its processing task can't
451-
proceed).
467+
proceed). Hence, you will need different _linked failure modes_.
468+
469+
## Failure modes
470+
471+
By default, task failure is _bidirectionally linked_, which means that if
472+
either task fails, it kills the other one.
473+
474+
~~~{.xfail-test .linked-failure}
475+
# use std::task;
476+
# use std::comm::oneshot;
477+
# fn sleep_forever() { loop { let (p, c) = oneshot::<()>(); p.recv(); } }
478+
# do task::try {
479+
do spawn {
480+
do spawn {
481+
fail!(); // All three tasks will fail.
482+
}
483+
sleep_forever(); // Will get woken up by force, then fail
484+
}
485+
sleep_forever(); // Will get woken up by force, then fail
486+
# };
487+
~~~
488+
489+
If you want parent tasks to be able to kill their children, but do not want a
490+
parent to fail automatically if one of its child task fails, you can call
491+
`task::spawn_supervised` for _unidirectionally linked_ failure. The
492+
function `task::try`, which we saw previously, uses `spawn_supervised`
493+
internally, with additional logic to wait for the child task to finish
494+
before returning. Hence:
495+
496+
~~~{.xfail-test .linked-failure}
497+
# use std::comm::{stream, Chan, Port};
498+
# use std::comm::oneshot;
499+
# use std::task::{spawn, try};
500+
# use std::task;
501+
# fn sleep_forever() { loop { let (p, c) = oneshot::<()>(); p.recv(); } }
502+
# do task::try {
503+
let (receiver, sender): (Port<int>, Chan<int>) = stream();
504+
do spawn { // Bidirectionally linked
505+
// Wait for the supervised child task to exist.
506+
let message = receiver.recv();
507+
// Kill both it and the parent task.
508+
assert!(message != 42);
509+
}
510+
do try { // Unidirectionally linked
511+
sender.send(42);
512+
sleep_forever(); // Will get woken up by force
513+
}
514+
// Flow never reaches here -- parent task was killed too.
515+
# };
516+
~~~
517+
518+
Supervised failure is useful in any situation where one task manages
519+
multiple fallible child tasks, and the parent task can recover
520+
if any child fails. On the other hand, if the _parent_ (supervisor) fails,
521+
then there is nothing the children can do to recover, so they should
522+
also fail.
523+
524+
Supervised task failure propagates across multiple generations even if
525+
an intermediate generation has already exited:
526+
527+
~~~{.xfail-test .linked-failure}
528+
# use std::task;
529+
# use std::comm::oneshot;
530+
# fn sleep_forever() { loop { let (p, c) = oneshot::<()>(); p.recv(); } }
531+
# fn wait_for_a_while() { for _ in range(0, 1000u) { task::yield() } }
532+
# do task::try::<int> {
533+
do task::spawn_supervised {
534+
do task::spawn_supervised {
535+
sleep_forever(); // Will get woken up by force, then fail
536+
}
537+
// Intermediate task immediately exits
538+
}
539+
wait_for_a_while();
540+
fail!(); // Will kill grandchild even if child has already exited
541+
# };
542+
~~~
543+
544+
Finally, tasks can be configured to not propagate failure to each
545+
other at all, using `task::spawn_unlinked` for _isolated failure_.
546+
547+
~~~{.xfail-test .linked-failure}
548+
# use std::task;
549+
# fn random() -> uint { 100 }
550+
# fn sleep_for(i: uint) { for _ in range(0, i) { task::yield() } }
551+
# do task::try::<()> {
552+
let (time1, time2) = (random(), random());
553+
do task::spawn_unlinked {
554+
sleep_for(time2); // Won't get forced awake
555+
fail!();
556+
}
557+
sleep_for(time1); // Won't get forced awake
558+
fail!();
559+
// It will take MAX(time1,time2) for the program to finish.
560+
# };
561+
~~~
452562

453563
## Creating a task with a bi-directional communication path
454564

branches/try2/mk/docs.mk

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -215,12 +215,10 @@ RUSTDOC = $(HBIN2_H_$(CFG_BUILD))/rustdoc$(X_$(CFG_BUILD))
215215
# $(1) - The crate name (std/extra)
216216
# $(2) - The crate file
217217
# $(3) - The relevant host build triple (to depend on libstd)
218-
#
219-
# Passes --cfg stage2 to rustdoc because it uses the stage2 librustc.
220218
define libdoc
221219
doc/$(1)/index.html: $$(RUSTDOC) $$(TLIB2_T_$(3)_H_$(3))/$(CFG_STDLIB_$(3))
222220
@$$(call E, rustdoc: $$@)
223-
$(Q)$(RUSTDOC) --cfg stage2 $(2)
221+
$(Q)$(RUSTDOC) $(2)
224222

225223
DOCS += doc/$(1)/index.html
226224
endef

branches/try2/src/libextra/arc.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -655,7 +655,7 @@ mod tests {
655655
let arc2 = ~arc.clone();
656656
let (p, c) = comm::stream();
657657

658-
do spawn {
658+
do task::spawn_unlinked || {
659659
let _ = p.recv();
660660
do arc2.access_cond |one, cond| {
661661
cond.signal();

branches/try2/src/libextra/comm.rs

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -137,6 +137,7 @@ pub fn rendezvous<T: Send>() -> (SyncPort<T>, SyncChan<T>) {
137137
mod test {
138138
use comm::{DuplexStream, rendezvous};
139139
use std::rt::test::run_in_uv_task;
140+
use std::task::spawn_unlinked;
140141

141142

142143
#[test]
@@ -176,7 +177,7 @@ mod test {
176177
#[test]
177178
fn send_and_fail_and_try_recv() {
178179
let (port, chan) = rendezvous();
179-
do spawn {
180+
do spawn_unlinked {
180181
chan.duplex_stream.send(()); // Can't access this field outside this module
181182
fail!()
182183
}
@@ -186,7 +187,7 @@ mod test {
186187
#[test]
187188
fn try_send_and_recv_then_fail_before_ack() {
188189
let (port, chan) = rendezvous();
189-
do spawn {
190+
do spawn_unlinked {
190191
port.duplex_stream.recv();
191192
fail!()
192193
}
@@ -197,7 +198,7 @@ mod test {
197198
#[should_fail]
198199
fn send_and_recv_then_fail_before_ack() {
199200
let (port, chan) = rendezvous();
200-
do spawn {
201+
do spawn_unlinked {
201202
port.duplex_stream.recv();
202203
fail!()
203204
}

branches/try2/src/libextra/future.rs

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727

2828
use std::cell::Cell;
2929
use std::comm::{PortOne, oneshot};
30+
use std::task;
3031
use std::util::replace;
3132

3233
/// A type encapsulating the result of a computation which may not be complete
@@ -129,12 +130,29 @@ impl<A:Send> Future<A> {
129130

130131
let (port, chan) = oneshot();
131132

132-
do spawn {
133+
do task::spawn_with(chan) |chan| {
133134
chan.send(blk());
134135
}
135136

136137
Future::from_port(port)
137138
}
139+
140+
pub fn spawn_with<B: Send>(v: B, blk: proc(B) -> A) -> Future<A> {
141+
/*!
142+
* Create a future from a unique closure taking one argument.
143+
*
144+
* The closure and its argument will be moved into a new task. The
145+
* closure will be run and its result used as the value of the future.
146+
*/
147+
148+
let (port, chan) = oneshot();
149+
150+
do task::spawn_with((v, chan)) |(v, chan)| {
151+
chan.send(blk(v));
152+
}
153+
154+
Future::from_port(port)
155+
}
138156
}
139157

140158
#[cfg(test)]
@@ -189,6 +207,12 @@ mod test {
189207
assert_eq!(f.get(), ~"bale");
190208
}
191209
210+
#[test]
211+
fn test_spawn_with() {
212+
let mut f = Future::spawn_with(~"gale", |s| { s });
213+
assert_eq!(f.get(), ~"gale");
214+
}
215+
192216
#[test]
193217
#[should_fail]
194218
fn test_futurefail() {

0 commit comments

Comments
 (0)