Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add spawn_conversation #3153

Merged
merged 1 commit into from
Aug 9, 2012
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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