Skip to content

Commit d88497d

Browse files
committed
tutorial: Overhaul task section
1 parent 172bf3a commit d88497d

File tree

1 file changed

+63
-50
lines changed

1 file changed

+63
-50
lines changed

doc/tutorial.md

Lines changed: 63 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -2830,8 +2830,8 @@ supposed to point at, this is safe.
28302830
Rust supports a system of lightweight tasks, similar to what is found
28312831
in Erlang or other actor systems. Rust tasks communicate via messages
28322832
and do not share data. However, it is possible to send data without
2833-
copying it by making use of [unique boxes](#unique-boxes), which allow
2834-
the sending task to release ownership of a value, so that the
2833+
copying it by making use of [the exchange heap](#unique-boxes), which
2834+
allow the sending task to release ownership of a value, so that the
28352835
receiving task can keep on using it.
28362836
28372837
> ***Note:*** As Rust evolves, we expect the task API to grow and
@@ -2843,10 +2843,13 @@ Spawning a task is done using the various spawn functions in the
28432843
module `task`. Let's begin with the simplest one, `task::spawn()`:
28442844
28452845
~~~~
2846+
import task::spawn;
2847+
import io::println;
2848+
28462849
let some_value = 22;
2847-
do task::spawn {
2848-
io::println("This executes in the child task.");
2849-
io::println(#fmt("%d", some_value));
2850+
do spawn {
2851+
println("This executes in the child task.");
2852+
println(#fmt("%d", some_value));
28502853
}
28512854
~~~~
28522855
@@ -2866,44 +2869,54 @@ For example, imagine we wish to perform two expensive computations
28662869
in parallel. We might write something like:
28672870
28682871
~~~~
2869-
# fn some_expensive_computation() -> int { 42 }
2870-
# fn some_other_expensive_computation() {}
2871-
let port = comm::port::<int>();
2872-
let chan = comm::chan::<int>(port);
2873-
do task::spawn {
2872+
import task::spawn;
2873+
import comm::{port, chan, methods};
2874+
2875+
let port = port();
2876+
let chan = port.chan();
2877+
2878+
do spawn {
28742879
let result = some_expensive_computation();
2875-
comm::send(chan, result);
2880+
chan.send(result);
28762881
}
2882+
28772883
some_other_expensive_computation();
2878-
let result = comm::recv(port);
2884+
let result = port.recv();
2885+
2886+
# fn some_expensive_computation() -> int { 42 }
2887+
# fn some_other_expensive_computation() {}
28792888
~~~~
28802889
28812890
Let's walk through this code line-by-line. The first line creates a
28822891
port for receiving integers:
28832892
2893+
~~~~ {.ignore}
2894+
# import comm::port;
2895+
let port = port();
28842896
~~~~
2885-
let port = comm::port::<int>();
28862897
2887-
~~~~
28882898
This port is where we will receive the message from the child task
28892899
once it is complete. The second line creates a channel for sending
28902900
integers to the port `port`:
28912901
28922902
~~~~
2893-
# let port = comm::port::<int>();
2894-
let chan = comm::chan::<int>(port);
2903+
# import comm::{port, chan, methods};
2904+
# let port = port::<int>();
2905+
let chan = port.chan();
28952906
~~~~
28962907
28972908
The channel will be used by the child to send a message to the port.
28982909
The next statement actually spawns the child:
28992910
29002911
~~~~
2912+
# import task::{spawn};
2913+
# import comm::{port, chan, methods};
29012914
# fn some_expensive_computation() -> int { 42 }
2902-
# let port = comm::port::<int>();
2903-
# let chan = comm::chan::<int>(port);
2904-
do task::spawn {
2915+
# let port = port();
2916+
# let chan = port.chan();
2917+
do spawn {
29052918
let result = some_expensive_computation();
2906-
comm::send(chan, result);
2919+
chan.send(result);
29072920
}
29082921
~~~~
29092922
@@ -2913,12 +2926,13 @@ some other expensive computation and then waiting for the child's result
29132926
to arrive on the port:
29142927
29152928
~~~~
2929+
# import comm::{port, chan, methods};
29162930
# fn some_other_expensive_computation() {}
2917-
# let port = comm::port::<int>();
2918-
# let chan = comm::chan::<int>(port);
2919-
# comm::send(chan, 0);
2931+
# let port = port::<int>();
2932+
# let chan = chan::<int>(port);
2933+
# chan.send(0);
29202934
some_other_expensive_computation();
2921-
let result = comm::recv(port);
2935+
let result = port.recv();
29222936
~~~~
29232937
29242938
## Creating a task with a bi-directional communication path
@@ -2934,12 +2948,13 @@ the string in response. The child terminates when `0` is received.
29342948
Here is the function which implements the child task:
29352949
29362950
~~~~
2937-
fn stringifier(from_parent: comm::port<uint>,
2938-
to_parent: comm::chan<str>) {
2951+
# import comm::{port, chan, methods};
2952+
fn stringifier(from_parent: port<uint>,
2953+
to_parent: chan<str>) {
29392954
let mut value: uint;
29402955
loop {
2941-
value = comm::recv(from_parent);
2942-
comm::send(to_parent, uint::to_str(value, 10u));
2956+
value = from_parent.recv();
2957+
to_parent.send(uint::to_str(value, 10u));
29432958
if value == 0u { break; }
29442959
}
29452960
}
@@ -2956,25 +2971,32 @@ simply the strified version of the received value,
29562971
Here is the code for the parent task:
29572972
29582973
~~~~
2974+
# import task::{spawn_listener};
2975+
# import comm::{chan, port, methods};
29592976
# fn stringifier(from_parent: comm::port<uint>,
29602977
# to_parent: comm::chan<str>) {
29612978
# comm::send(to_parent, "22");
29622979
# comm::send(to_parent, "23");
29632980
# comm::send(to_parent, "0");
29642981
# }
2965-
fn main() {
2966-
let from_child = comm::port();
2967-
let to_parent = comm::chan(from_child);
2968-
let to_child = do task::spawn_listener |from_parent| {
2969-
stringifier(from_parent, to_parent);
2970-
};
2971-
comm::send(to_child, 22u);
2972-
assert comm::recv(from_child) == "22";
2973-
comm::send(to_child, 23u);
2974-
assert comm::recv(from_child) == "23";
2975-
comm::send(to_child, 0u);
2976-
assert comm::recv(from_child) == "0";
2977-
}
2982+
# fn main() {
2983+
2984+
let from_child = port();
2985+
let to_parent = from_child.chan();
2986+
let to_child = do spawn_listener |from_parent| {
2987+
stringifier(from_parent, to_parent);
2988+
};
2989+
2990+
to_child.send(22u);
2991+
assert from_child.recv() == "22";
2992+
2993+
to_child.send(23u);
2994+
assert from_child.recv() == "23";
2995+
2996+
to_child.send(0u);
2997+
assert from_child.recv() == "0";
2998+
2999+
# }
29783000
~~~~
29793001
29803002
The parent first sets up a port to receive data from and a channel
@@ -2986,15 +3008,6 @@ the associated channel. Finally, the closure passed to
29863008
`to_parent` channel in its environment, so both parent and child
29873009
can send and receive data to and from the other.
29883010
2989-
## The supervisor relationship
2990-
2991-
By default, failures in Rust propagate upward through the task tree.
2992-
We say that each task is supervised by its parent, meaning that if the
2993-
task fails, that failure is propagated to the parent task, which will
2994-
fail sometime later. This propagation can be disabled by using the
2995-
function `task::unsupervise()`, which disables error propagation from
2996-
the current task to its parent.
2997-
29983011
# Testing
29993012
30003013
The Rust language has a facility for testing built into the language.

0 commit comments

Comments
 (0)