Skip to content

Commit

Permalink
Merge pull request #3153 from jruderman/sconv
Browse files Browse the repository at this point in the history
Add spawn_conversation
  • Loading branch information
bblum committed Aug 9, 2012
2 parents 904a74e + a76e433 commit 35db5b7
Show file tree
Hide file tree
Showing 5 changed files with 67 additions and 51 deletions.
42 changes: 22 additions & 20 deletions doc/tutorial.md
Original file line number Diff line number Diff line change
Expand Up @@ -196,7 +196,7 @@ When complete, `make install` will place the following programs into
`/usr/local/bin`:

* `rustc`, the Rust compiler
* `rustdoc`, the API-documentation tool
* `rustdoc`, the API-documentation tool
* `cargo`, the Rust package manager

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

This child will perform the expensive computation send the result
over the channel. Finally, the parent continues by performing
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:

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

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 `task::spawn_listener()` supports this pattern. We'll look
briefly at how it is used.
other. The function `task::spawn_conversation()` supports this pattern.
We'll look briefly at how it is used.

To see how `spawn_listener()` works, we will create a child task
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:
Expand All @@ -3006,11 +3010,11 @@ loops, reading from the `from_parent` port and then sending its
response to the `to_parent` channel. 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:

~~~~
# import task::{spawn_listener};
# import task::{spawn_conversation};
# import comm::{chan, port, methods};
# fn stringifier(from_parent: comm::port<uint>,
# to_parent: comm::chan<~str>) {
Expand All @@ -3020,32 +3024,30 @@ Here is the code for the parent task:
# }
# fn main() {
let from_child = port();
let to_parent = from_child.chan();
let to_child = do spawn_listener |from_parent| {
let (from_child, to_child) = do spawn_conversation |from_parent, to_parent| {
stringifier(from_parent, to_parent);
};
to_child.send(22u);
assert from_child.recv() == ~"22";
to_child.send(23u);
assert from_child.recv() == ~"23";
to_child.send(0u);
assert from_child.recv() == ~"23";
assert from_child.recv() == ~"0";
# }
~~~~

The parent first sets up a port to receive data from and a channel
that the child can use to send data to that port. The call to
`spawn_listener()` will spawn the child task, providing it with a port
on which to receive data from its parent, and returning to the parent
the associated channel. Finally, the closure passed to
`spawn_listener()` that forms the body of the child task captures the
`to_parent` channel in its environment, so both parent and child
can send and receive data to and from the other.
The parent task calls `spawn_conversation` with a function that takes
a `from_parent` port and a `to_parent` channel. In return, it gets a
`from_child` channel and a `to_child` port. As a result, both parent
and child can send and receive data to and from the other.

`spawn_conversation`
will create two port/channel pairs, passing one set to the child task
and returning the other set to the caller.

# Testing

Expand Down
5 changes: 2 additions & 3 deletions src/libcore/priv.rs
Original file line number Diff line number Diff line change
Expand Up @@ -40,9 +40,8 @@ unsafe fn chan_from_global_ptr<T: send>(
log(debug,~"is probably zero...");
// There's no global channel. We must make it
let setup_po = comm::port();
let setup_ch = comm::chan(setup_po);
let setup_ch = do task_fn().spawn_listener |setup_po| {
let (setup_po, setup_ch) = do task_fn().spawn_conversation
|setup_po, setup_ch| {
let po = comm::port::<T>();
let ch = comm::chan(po);
comm::send(setup_ch, ch);
Expand Down
57 changes: 38 additions & 19 deletions src/libcore/task.rs
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ export spawn_unlinked;
export spawn_supervised;
export spawn_with;
export spawn_listener;
export spawn_conversation;
export spawn_sched;
export try;

Expand Down Expand Up @@ -376,6 +377,20 @@ impl task_builder for task_builder {
comm::recv(setup_po)
}
/**
* Runs a new task, setting up communication in both directions
*/
fn spawn_conversation<A: send, B: send>
(+f: fn~(comm::port<A>, comm::chan<B>))
-> (comm::port<B>, comm::chan<A>) {
let from_child = comm::port();
let to_parent = comm::chan(from_child);
let to_child = do self.spawn_listener |from_parent| {
f(from_parent, to_parent)
};
(from_child, to_child)
}
/**
* Execute a function in another task and return either the return value
* of the function or result::err.
Expand Down Expand Up @@ -474,31 +489,24 @@ fn spawn_listener<A:send>(+f: fn~(comm::port<A>)) -> comm::chan<A> {
/*!
* Runs a new task while providing a channel from the parent to the child
*
* Sets up a communication channel from the current task to the new
* child task, passes the port to child's body, and returns a channel
* linked to the port to the parent.
*
* This encapsulates some boilerplate handshaking logic that would
* otherwise be required to establish communication from the parent
* to the child.
*
* The simplest way to establish bidirectional communication between
* a parent in child is as follows:
*
* let po = comm::port();
* let ch = comm::chan(po);
* let ch = do spawn_listener |po| {
* // Now the child has a port called 'po' to read from and
* // an environment-captured channel called 'ch'.
* };
* // Likewise, the parent has both a 'po' and 'ch'
*
* This function is equivalent to `task().spawn_listener(f)`.
*/
task().spawn_listener(f)
}
fn spawn_conversation<A: send, B: send>
(+f: fn~(comm::port<A>, comm::chan<B>))
-> (comm::port<B>, comm::chan<A>) {
/*!
* Runs a new task, setting up communication in both directions
*
* This function is equivalent to `task().spawn_conversation(f)`.
*/
task().spawn_conversation(f)
}
fn spawn_sched(mode: sched_mode, +f: fn~()) {
/*!
* Creates a new scheduler and executes a task on it
Expand Down Expand Up @@ -1716,6 +1724,17 @@ fn test_spawn_listiner_bidi() {
assert res == ~"pong";
}

#[test]
fn test_spawn_conversation() {
let (recv_str, send_int) = do spawn_conversation |recv_int, send_str| {
let input = comm::recv(recv_int);
let output = int::str(input);
comm::send(send_str, output);
};
comm::send(send_int, 1);
assert comm::recv(recv_str) == ~"1";
}

#[test]
fn test_try_success() {
match do try {
Expand Down
5 changes: 2 additions & 3 deletions src/rustdoc/page_pass.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,9 +29,8 @@ fn run(
return doc;
}

let result_port = comm::port();
let result_chan = comm::chan(result_port);
let page_chan = do task::spawn_listener |page_port| {
let (result_port, page_chan) = do task::spawn_conversation
|page_port, result_chan| {
comm::send(result_chan, make_doc_from_pages(page_port));
};

Expand Down
9 changes: 3 additions & 6 deletions src/test/bench/msgsend.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,15 +28,12 @@ fn server(requests: comm::port<request>, responses: comm::chan<uint>) {
}

fn run(args: ~[~str]) {
let from_child = comm::port();
let to_parent = comm::chan(from_child);
let to_child = do task::spawn_listener |po| {
server(po, to_parent);
let (from_child, to_child) = do task::spawn_conversation |po, ch| {
server(po, ch);
};
let size = option::get(uint::from_str(args[1]));
let workers = option::get(uint::from_str(args[2]));
let start = std::time::precise_time_s();
let to_child = to_child;
let mut worker_results = ~[];
for uint::range(0u, workers) |_i| {
do task::task().future_result(|+r| {
Expand Down Expand Up @@ -65,7 +62,7 @@ fn main(args: ~[~str]) {
~[~"", ~"10000", ~"4"]
} else {
args
};
};

debug!{"%?", args};
run(args);
Expand Down

0 comments on commit 35db5b7

Please sign in to comment.