Skip to content

Commit acca9e3

Browse files
committed
Remove linked failure from the runtime
The reasons for doing this are: * The model on which linked failure is based is inherently complex * The implementation is also very complex, and there are few remaining who fully understand the implementation * There are existing race conditions in the core context switching function of the scheduler, and possibly others. * It's unclear whether this model of linked failure maps well to a 1:1 threading model Linked failure is often a desired aspect of tasks, but we would like to take a much more conservative approach in re-implementing linked failure if at all. Closes #8674 Closes #8318 Closes #8863
1 parent 85a1eff commit acca9e3

39 files changed

+403
-2531
lines changed

doc/rust.md

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

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

doc/tutorial-tasks.md

+1-111
Original file line numberDiff line numberDiff line change
@@ -402,22 +402,6 @@ 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-
421405
While it isn't possible for a task to recover from failure, tasks may notify
422406
each other of failure. The simplest way of handling task failure is with the
423407
`try` function, which is similar to `spawn`, but immediately blocks waiting
@@ -464,101 +448,7 @@ it trips, indicates an unrecoverable logic error); in other cases you
464448
might want to contain the failure at a certain boundary (perhaps a
465449
small piece of input from the outside world, which you happen to be
466450
processing in parallel, is malformed and its processing task can't
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-
~~~
451+
proceed).
562452

563453
## Creating a task with a bi-directional communication path
564454

src/libextra/arc.rs

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

src/libextra/comm.rs

+3-4
Original file line numberDiff line numberDiff line change
@@ -137,7 +137,6 @@ 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;
141140

142141

143142
#[test]
@@ -177,7 +176,7 @@ mod test {
177176
#[test]
178177
fn send_and_fail_and_try_recv() {
179178
let (port, chan) = rendezvous();
180-
do spawn_unlinked {
179+
do spawn {
181180
chan.duplex_stream.send(()); // Can't access this field outside this module
182181
fail!()
183182
}
@@ -187,7 +186,7 @@ mod test {
187186
#[test]
188187
fn try_send_and_recv_then_fail_before_ack() {
189188
let (port, chan) = rendezvous();
190-
do spawn_unlinked {
189+
do spawn {
191190
port.duplex_stream.recv();
192191
fail!()
193192
}
@@ -198,7 +197,7 @@ mod test {
198197
#[should_fail]
199198
fn send_and_recv_then_fail_before_ack() {
200199
let (port, chan) = rendezvous();
201-
do spawn_unlinked {
200+
do spawn {
202201
port.duplex_stream.recv();
203202
fail!()
204203
}

src/libextra/future.rs

+1-25
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,6 @@
2727

2828
use std::cell::Cell;
2929
use std::comm::{PortOne, oneshot};
30-
use std::task;
3130
use std::util::replace;
3231

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

131130
let (port, chan) = oneshot();
132131

133-
do task::spawn_with(chan) |chan| {
132+
do spawn {
134133
chan.send(blk());
135134
}
136135

137136
Future::from_port(port)
138137
}
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-
}
156138
}
157139

158140
#[cfg(test)]
@@ -207,12 +189,6 @@ mod test {
207189
assert_eq!(f.get(), ~"bale");
208190
}
209191
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-
216192
#[test]
217193
#[should_fail]
218194
fn test_futurefail() {

0 commit comments

Comments
 (0)