Skip to content

Commit 35db5b7

Browse files
committed
Merge pull request #3153 from jruderman/sconv
Add spawn_conversation
2 parents 904a74e + a76e433 commit 35db5b7

File tree

5 files changed

+67
-51
lines changed

5 files changed

+67
-51
lines changed

doc/tutorial.md

+22-20
Original file line numberDiff line numberDiff line change
@@ -196,7 +196,7 @@ When complete, `make install` will place the following programs into
196196
`/usr/local/bin`:
197197

198198
* `rustc`, the Rust compiler
199-
* `rustdoc`, the API-documentation tool
199+
* `rustdoc`, the API-documentation tool
200200
* `cargo`, the Rust package manager
201201

202202
[wiki-get-started]: https://github.com/mozilla/rust/wiki/Note-getting-started-developing-Rust
@@ -2960,7 +2960,11 @@ do spawn {
29602960
~~~~
29612961

29622962
This child will perform the expensive computation send the result
2963-
over the channel. Finally, the parent continues by performing
2963+
over the channel. (Under the hood, `chan` was captured by the
2964+
closure that forms the body of the child task. This capture is
2965+
allowed because channels are sendable.)
2966+
2967+
Finally, the parent continues by performing
29642968
some other expensive computation and then waiting for the child's result
29652969
to arrive on the port:
29662970

@@ -2978,10 +2982,10 @@ let result = port.recv();
29782982

29792983
A very common thing to do is to spawn a child task where the parent
29802984
and child both need to exchange messages with each
2981-
other. The function `task::spawn_listener()` supports this pattern. We'll look
2982-
briefly at how it is used.
2985+
other. The function `task::spawn_conversation()` supports this pattern.
2986+
We'll look briefly at how it is used.
29832987

2984-
To see how `spawn_listener()` works, we will create a child task
2988+
To see how `spawn_conversation()` works, we will create a child task
29852989
that receives `uint` messages, converts them to a string, and sends
29862990
the string in response. The child terminates when `0` is received.
29872991
Here is the function that implements the child task:
@@ -3006,11 +3010,11 @@ loops, reading from the `from_parent` port and then sending its
30063010
response to the `to_parent` channel. The actual response itself is
30073011
simply the strified version of the received value,
30083012
`uint::to_str(value)`.
3009-
3013+
30103014
Here is the code for the parent task:
30113015

30123016
~~~~
3013-
# import task::{spawn_listener};
3017+
# import task::{spawn_conversation};
30143018
# import comm::{chan, port, methods};
30153019
# fn stringifier(from_parent: comm::port<uint>,
30163020
# to_parent: comm::chan<~str>) {
@@ -3020,32 +3024,30 @@ Here is the code for the parent task:
30203024
# }
30213025
# fn main() {
30223026
3023-
let from_child = port();
3024-
let to_parent = from_child.chan();
3025-
let to_child = do spawn_listener |from_parent| {
3027+
let (from_child, to_child) = do spawn_conversation |from_parent, to_parent| {
30263028
stringifier(from_parent, to_parent);
30273029
};
30283030
30293031
to_child.send(22u);
30303032
assert from_child.recv() == ~"22";
30313033
30323034
to_child.send(23u);
3033-
assert from_child.recv() == ~"23";
3034-
30353035
to_child.send(0u);
3036+
3037+
assert from_child.recv() == ~"23";
30363038
assert from_child.recv() == ~"0";
30373039
30383040
# }
30393041
~~~~
30403042

3041-
The parent first sets up a port to receive data from and a channel
3042-
that the child can use to send data to that port. The call to
3043-
`spawn_listener()` will spawn the child task, providing it with a port
3044-
on which to receive data from its parent, and returning to the parent
3045-
the associated channel. Finally, the closure passed to
3046-
`spawn_listener()` that forms the body of the child task captures the
3047-
`to_parent` channel in its environment, so both parent and child
3048-
can send and receive data to and from the other.
3043+
The parent task calls `spawn_conversation` with a function that takes
3044+
a `from_parent` port and a `to_parent` channel. In return, it gets a
3045+
`from_child` channel and a `to_child` port. As a result, both parent
3046+
and child can send and receive data to and from the other.
3047+
3048+
`spawn_conversation`
3049+
will create two port/channel pairs, passing one set to the child task
3050+
and returning the other set to the caller.
30493051

30503052
# Testing
30513053

src/libcore/priv.rs

+2-3
Original file line numberDiff line numberDiff line change
@@ -40,9 +40,8 @@ unsafe fn chan_from_global_ptr<T: send>(
4040
log(debug,~"is probably zero...");
4141
// There's no global channel. We must make it
4242
43-
let setup_po = comm::port();
44-
let setup_ch = comm::chan(setup_po);
45-
let setup_ch = do task_fn().spawn_listener |setup_po| {
43+
let (setup_po, setup_ch) = do task_fn().spawn_conversation
44+
|setup_po, setup_ch| {
4645
let po = comm::port::<T>();
4746
let ch = comm::chan(po);
4847
comm::send(setup_ch, ch);

src/libcore/task.rs

+38-19
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@ export spawn_unlinked;
5252
export spawn_supervised;
5353
export spawn_with;
5454
export spawn_listener;
55+
export spawn_conversation;
5556
export spawn_sched;
5657
export try;
5758

@@ -376,6 +377,20 @@ impl task_builder for task_builder {
376377
comm::recv(setup_po)
377378
}
378379
380+
/**
381+
* Runs a new task, setting up communication in both directions
382+
*/
383+
fn spawn_conversation<A: send, B: send>
384+
(+f: fn~(comm::port<A>, comm::chan<B>))
385+
-> (comm::port<B>, comm::chan<A>) {
386+
let from_child = comm::port();
387+
let to_parent = comm::chan(from_child);
388+
let to_child = do self.spawn_listener |from_parent| {
389+
f(from_parent, to_parent)
390+
};
391+
(from_child, to_child)
392+
}
393+
379394
/**
380395
* Execute a function in another task and return either the return value
381396
* of the function or result::err.
@@ -474,31 +489,24 @@ fn spawn_listener<A:send>(+f: fn~(comm::port<A>)) -> comm::chan<A> {
474489
/*!
475490
* Runs a new task while providing a channel from the parent to the child
476491
*
477-
* Sets up a communication channel from the current task to the new
478-
* child task, passes the port to child's body, and returns a channel
479-
* linked to the port to the parent.
480-
*
481-
* This encapsulates some boilerplate handshaking logic that would
482-
* otherwise be required to establish communication from the parent
483-
* to the child.
484-
*
485-
* The simplest way to establish bidirectional communication between
486-
* a parent in child is as follows:
487-
*
488-
* let po = comm::port();
489-
* let ch = comm::chan(po);
490-
* let ch = do spawn_listener |po| {
491-
* // Now the child has a port called 'po' to read from and
492-
* // an environment-captured channel called 'ch'.
493-
* };
494-
* // Likewise, the parent has both a 'po' and 'ch'
495-
*
496492
* This function is equivalent to `task().spawn_listener(f)`.
497493
*/
498494
499495
task().spawn_listener(f)
500496
}
501497
498+
fn spawn_conversation<A: send, B: send>
499+
(+f: fn~(comm::port<A>, comm::chan<B>))
500+
-> (comm::port<B>, comm::chan<A>) {
501+
/*!
502+
* Runs a new task, setting up communication in both directions
503+
*
504+
* This function is equivalent to `task().spawn_conversation(f)`.
505+
*/
506+
507+
task().spawn_conversation(f)
508+
}
509+
502510
fn spawn_sched(mode: sched_mode, +f: fn~()) {
503511
/*!
504512
* Creates a new scheduler and executes a task on it
@@ -1716,6 +1724,17 @@ fn test_spawn_listiner_bidi() {
17161724
assert res == ~"pong";
17171725
}
17181726

1727+
#[test]
1728+
fn test_spawn_conversation() {
1729+
let (recv_str, send_int) = do spawn_conversation |recv_int, send_str| {
1730+
let input = comm::recv(recv_int);
1731+
let output = int::str(input);
1732+
comm::send(send_str, output);
1733+
};
1734+
comm::send(send_int, 1);
1735+
assert comm::recv(recv_str) == ~"1";
1736+
}
1737+
17191738
#[test]
17201739
fn test_try_success() {
17211740
match do try {

src/rustdoc/page_pass.rs

+2-3
Original file line numberDiff line numberDiff line change
@@ -29,9 +29,8 @@ fn run(
2929
return doc;
3030
}
3131

32-
let result_port = comm::port();
33-
let result_chan = comm::chan(result_port);
34-
let page_chan = do task::spawn_listener |page_port| {
32+
let (result_port, page_chan) = do task::spawn_conversation
33+
|page_port, result_chan| {
3534
comm::send(result_chan, make_doc_from_pages(page_port));
3635
};
3736

src/test/bench/msgsend.rs

+3-6
Original file line numberDiff line numberDiff line change
@@ -28,15 +28,12 @@ fn server(requests: comm::port<request>, responses: comm::chan<uint>) {
2828
}
2929

3030
fn run(args: ~[~str]) {
31-
let from_child = comm::port();
32-
let to_parent = comm::chan(from_child);
33-
let to_child = do task::spawn_listener |po| {
34-
server(po, to_parent);
31+
let (from_child, to_child) = do task::spawn_conversation |po, ch| {
32+
server(po, ch);
3533
};
3634
let size = option::get(uint::from_str(args[1]));
3735
let workers = option::get(uint::from_str(args[2]));
3836
let start = std::time::precise_time_s();
39-
let to_child = to_child;
4037
let mut worker_results = ~[];
4138
for uint::range(0u, workers) |_i| {
4239
do task::task().future_result(|+r| {
@@ -65,7 +62,7 @@ fn main(args: ~[~str]) {
6562
~[~"", ~"10000", ~"4"]
6663
} else {
6764
args
68-
};
65+
};
6966

7067
debug!{"%?", args};
7168
run(args);

0 commit comments

Comments
 (0)