@@ -2882,21 +2882,20 @@ arguments and generates no return value. The effect of `task::spawn()`
2882
2882
is to fire up a child task that will execute the closure in parallel
2883
2883
with the creator.
2884
2884
2885
- ## Ports and channels
2885
+ ## Communication
2886
2886
2887
2887
Now that we have spawned a child task, it would be nice if we could
2888
- communicate with it. This is done by creating a * port * with an
2889
- associated * channel * . A port is simply a location to receive messages
2890
- of a particular type. A channel is used to send messages to a port.
2891
- For example, imagine we wish to perform two expensive computations
2892
- in parallel. We might write something like:
2888
+ communicate with it. This is done using * pipes * . Pipes are simply a
2889
+ pair of endpoints, with one for sending messages and another for
2890
+ receiving messages. The easiest way to create a pipe is to use
2891
+ ` pipes::stream ` . Imagine we wish to perform two expensive
2892
+ computations in parallel. We might write something like:
2893
2893
2894
2894
~~~~
2895
2895
import task::spawn;
2896
- import comm ::{port, chan};
2896
+ import pipes ::{stream, port, chan};
2897
2897
2898
- let port = port();
2899
- let chan = port.chan();
2898
+ let (chan, port) = stream();
2900
2899
2901
2900
do spawn {
2902
2901
let result = some_expensive_computation();
@@ -2911,25 +2910,16 @@ let result = port.recv();
2911
2910
~~~~
2912
2911
2913
2912
Let's walk through this code line-by-line. The first line creates a
2914
- port for receiving integers:
2913
+ stream for sending and receiving integers:
2915
2914
2916
2915
~~~~ {.ignore}
2917
- # import comm::port ;
2918
- let port = port ();
2916
+ # import pipes::stream ;
2917
+ let (chan, port) = stream ();
2919
2918
~~~~
2920
2919
2921
2920
This port is where we will receive the message from the child task
2922
- once it is complete. The second line creates a channel for sending
2923
- integers to the port ` port ` :
2924
-
2925
- ~~~~
2926
- # import comm::{port, chan};
2927
- # let port = port::<int>();
2928
- let chan = port.chan();
2929
- ~~~~
2930
-
2931
- The channel will be used by the child to send a message to the port.
2932
- The next statement actually spawns the child:
2921
+ once it is complete. The channel will be used by the child to send a
2922
+ message to the port. The next statement actually spawns the child:
2933
2923
2934
2924
~~~~
2935
2925
# import task::{spawn};
@@ -2953,10 +2943,9 @@ some other expensive computation and then waiting for the child's result
2953
2943
to arrive on the port:
2954
2944
2955
2945
~~~~
2956
- # import comm ::{port, chan};
2946
+ # import pipes ::{stream, port, chan};
2957
2947
# fn some_other_expensive_computation() {}
2958
- # let port = port::<int>();
2959
- # let chan = chan::<int>(port);
2948
+ # let (chan, port) = stream::<int>();
2960
2949
# chan.send(0);
2961
2950
some_other_expensive_computation();
2962
2951
let result = port.recv();
@@ -2965,74 +2954,73 @@ let result = port.recv();
2965
2954
## Creating a task with a bi-directional communication path
2966
2955
2967
2956
A very common thing to do is to spawn a child task where the parent
2968
- and child both need to exchange messages with each
2969
- other. The function ` task::spawn_conversation ()` supports this pattern.
2970
- We'll look briefly at how it is used.
2957
+ and child both need to exchange messages with each other. There
2958
+ function ` std::comm::DuplexStream ()` supports this pattern. We'll
2959
+ look briefly at how it is used.
2971
2960
2972
2961
To see how ` spawn_conversation() ` works, we will create a child task
2973
2962
that receives ` uint ` messages, converts them to a string, and sends
2974
2963
the string in response. The child terminates when ` 0 ` is received.
2975
2964
Here is the function that implements the child task:
2976
2965
2977
2966
~~~~
2978
- # import comm::{Port, port, Chan, chan} ;
2979
- fn stringifier(from_parent: Port<uint>,
2980
- to_parent: Chan <~str>) {
2967
+ # import std:: comm::DuplexStream ;
2968
+ # import pipes::{port, chan};
2969
+ fn stringifier(channel: DuplexStream <~str, uint >) {
2981
2970
let mut value: uint;
2982
2971
loop {
2983
- value = from_parent .recv();
2984
- to_parent .send(uint::to_str(value, 10u));
2972
+ value = channel .recv();
2973
+ channel .send(uint::to_str(value, 10u));
2985
2974
if value == 0u { break; }
2986
2975
}
2987
2976
}
2988
2977
~~~~
2989
2978
2990
- You can see that the function takes two parameters. The first is a
2991
- port used to receive messages from the parent, and the second is a
2992
- channel used to send messages to the parent. The body itself simply
2993
- loops, reading from the ` from_parent ` port and then sending its
2994
- response to the ` to_parent ` channel. The actual response itself is
2995
- simply the strified version of the received value,
2979
+ The implementation of ` DuplexStream ` supports both sending and
2980
+ receiving. The ` stringifier ` function takes a ` DuplexStream ` that can
2981
+ send strings ( the first type parameter) and receive ` uint ` messages
2982
+ (the second type parameter). The body itself simply loops, reading
2983
+ from the channel and then sending its response back. The actual
2984
+ response itself is simply the strified version of the received value,
2996
2985
` uint::to_str(value) ` .
2997
2986
2998
2987
Here is the code for the parent task:
2999
2988
3000
2989
~~~~
3001
- # import task::{spawn_conversation};
3002
- # import comm::{Chan, chan, Port, port};
3003
- # fn stringifier(from_parent: comm::Port<uint>,
3004
- # to_parent: comm::Chan<~str>) {
3005
- # comm::send(to_parent, ~"22");
3006
- # comm::send(to_parent, ~"23");
3007
- # comm::send(to_parent, ~"0");
2990
+ # import std::comm::DuplexStream;
2991
+ # import pipes::{port, chan};
2992
+ # import task::spawn;
2993
+ # fn stringifier(channel: DuplexStream<~str, uint>) {
2994
+ # let mut value: uint;
2995
+ # loop {
2996
+ # value = channel.recv();
2997
+ # channel.send(uint::to_str(value, 10u));
2998
+ # if value == 0u { break; }
2999
+ # }
3008
3000
# }
3009
3001
# fn main() {
3010
3002
3011
- let (from_child, to_child) = do spawn_conversation |from_parent, to_parent| {
3012
- stringifier(from_parent, to_parent);
3003
+ let (from_child, to_child) = DuplexStream();
3004
+
3005
+ do spawn |to_child| {
3006
+ stringifier();
3013
3007
};
3014
3008
3015
- to_child .send(22u);
3009
+ from_child .send(22u);
3016
3010
assert from_child.recv() == ~"22";
3017
3011
3018
- to_child .send(23u);
3019
- to_child .send(0u);
3012
+ from_child .send(23u);
3013
+ from_child .send(0u);
3020
3014
3021
3015
assert from_child.recv() == ~"23";
3022
3016
assert from_child.recv() == ~"0";
3023
3017
3024
3018
# }
3025
3019
~~~~
3026
3020
3027
- The parent task calls ` spawn_conversation ` with a function that takes
3028
- a ` from_parent ` port and a ` to_parent ` channel. In return, it gets a
3029
- ` from_child ` channel and a ` to_child ` port. As a result, both parent
3021
+ 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
3030
3022
and child can send and receive data to and from the other.
3031
3023
3032
- ` spawn_conversation `
3033
- will create two port/channel pairs, passing one set to the child task
3034
- and returning the other set to the caller.
3035
-
3036
3024
# Testing
3037
3025
3038
3026
The Rust language has a facility for testing built into the language.
0 commit comments