diff options
| author | Brian Anderson <banderson@mozilla.com> | 2012-09-22 15:33:50 -0700 |
|---|---|---|
| committer | Brian Anderson <banderson@mozilla.com> | 2012-09-22 15:34:01 -0700 |
| commit | fd0de8bfd70b01dd01ca541bfdfb52703fa44d9c (patch) | |
| tree | 62e7e2e37a386a71bebca0464350b47f75f20d57 /doc/tutorial-tasks.md | |
| parent | 6caafaa9ea44d404733af6f9a907b6295dccc966 (diff) | |
| download | rust-fd0de8bfd70b01dd01ca541bfdfb52703fa44d9c.tar.gz rust-fd0de8bfd70b01dd01ca541bfdfb52703fa44d9c.zip | |
doc: Split out task tutorail. Add links to sub-tutorials
Diffstat (limited to 'doc/tutorial-tasks.md')
| -rw-r--r-- | doc/tutorial-tasks.md | 173 |
1 files changed, 173 insertions, 0 deletions
diff --git a/doc/tutorial-tasks.md b/doc/tutorial-tasks.md new file mode 100644 index 00000000000..2f540ef9ee6 --- /dev/null +++ b/doc/tutorial-tasks.md @@ -0,0 +1,173 @@ +% Tasks and communication in Rust + +Rust supports a system of lightweight tasks, similar to what is found +in Erlang or other actor systems. Rust tasks communicate via messages +and do not share data. However, it is possible to send data without +copying it by making use of [the exchange heap](#unique-boxes), which +allow the sending task to release ownership of a value, so that the +receiving task can keep on using it. + +> ***Note:*** As Rust evolves, we expect the task API to grow and +> change somewhat. The tutorial documents the API as it exists today. + +# Spawning a task + +Spawning a task is done using the various spawn functions in the +module `task`. Let's begin with the simplest one, `task::spawn()`: + +~~~~ +use task::spawn; +use io::println; + +let some_value = 22; + +do spawn { + println(~"This executes in the child task."); + println(fmt!("%d", some_value)); +} +~~~~ + +The argument to `task::spawn()` is a [unique +closure](#unique-closures) of type `fn~()`, meaning that it takes no +arguments and generates no return value. The effect of `task::spawn()` +is to fire up a child task that will execute the closure in parallel +with the creator. + +# Communication + +Now that we have spawned a child task, it would be nice if we could +communicate with it. This is done using *pipes*. Pipes are simply a +pair of endpoints, with one for sending messages and another for +receiving messages. The easiest way to create a pipe is to use +`pipes::stream`. Imagine we wish to perform two expensive +computations in parallel. We might write something like: + +~~~~ +use task::spawn; +use pipes::{stream, Port, Chan}; + +let (chan, port) = stream(); + +do spawn { + let result = some_expensive_computation(); + chan.send(result); +} + +some_other_expensive_computation(); +let result = port.recv(); + +# fn some_expensive_computation() -> int { 42 } +# fn some_other_expensive_computation() {} +~~~~ + +Let's walk through this code line-by-line. The first line creates a +stream for sending and receiving integers: + +~~~~ {.ignore} +# use pipes::stream; +let (chan, port) = stream(); +~~~~ + +This port is where we will receive the message from the child task +once it is complete. The channel will be used by the child to send a +message to the port. The next statement actually spawns the child: + +~~~~ +# use task::{spawn}; +# use comm::{Port, Chan}; +# fn some_expensive_computation() -> int { 42 } +# let port = Port(); +# let chan = port.chan(); +do spawn { + let result = some_expensive_computation(); + chan.send(result); +} +~~~~ + +This child will perform the expensive computation send the result +over the channel. (Under the hood, `chan` was captured by the +closure that forms the body of the child task. This capture is +allowed because channels are sendable.) + +Finally, the parent continues by performing +some other expensive computation and then waiting for the child's result +to arrive on the port: + +~~~~ +# use pipes::{stream, Port, Chan}; +# fn some_other_expensive_computation() {} +# let (chan, port) = stream::<int>(); +# chan.send(0); +some_other_expensive_computation(); +let result = port.recv(); +~~~~ + +# Creating a task with a bi-directional communication path + +A very common thing to do is to spawn a child task where the parent +and child both need to exchange messages with each other. The +function `std::comm::DuplexStream()` supports this pattern. We'll +look briefly at how it is used. + +To see how `spawn_conversation()` works, we will create a child task +that receives `uint` messages, converts them to a string, and sends +the string in response. The child terminates when `0` is received. +Here is the function that implements the child task: + +~~~~ +# use std::comm::DuplexStream; +# use pipes::{Port, Chan}; +fn stringifier(channel: &DuplexStream<~str, uint>) { + let mut value: uint; + loop { + value = channel.recv(); + channel.send(uint::to_str(value, 10u)); + if value == 0u { break; } + } +} +~~~~ + +The implementation of `DuplexStream` supports both sending and +receiving. The `stringifier` function takes a `DuplexStream` that can +send strings (the first type parameter) and receive `uint` messages +(the second type parameter). The body itself simply loops, reading +from the channel and then sending its response back. The actual +response itself is simply the strified version of the received value, +`uint::to_str(value)`. + +Here is the code for the parent task: + +~~~~ +# use std::comm::DuplexStream; +# use pipes::{Port, Chan}; +# use task::spawn; +# fn stringifier(channel: &DuplexStream<~str, uint>) { +# let mut value: uint; +# loop { +# value = channel.recv(); +# channel.send(uint::to_str(value, 10u)); +# if value == 0u { break; } +# } +# } +# fn main() { + +let (from_child, to_child) = DuplexStream(); + +do spawn || { + stringifier(&to_child); +}; + +from_child.send(22u); +assert from_child.recv() == ~"22"; + +from_child.send(23u); +from_child.send(0u); + +assert from_child.recv() == ~"23"; +assert from_child.recv() == ~"0"; + +# } +~~~~ + +The parent task first calls `DuplexStream` to create a pair of bidirectional endpoints. It then uses `task::spawn` to create the child task, which captures one end of the communication channel. As a result, both parent +and child can send and receive data to and from the other. |
