Add spawn_conversation
This commit is contained in:
parent
166cb1b28b
commit
a76e4334b3
@ -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
|
||||
@ -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:
|
||||
|
||||
@ -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:
|
||||
@ -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>) {
|
||||
@ -3020,9 +3024,7 @@ 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);
|
||||
};
|
||||
|
||||
@ -3030,22 +3032,22 @@ 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
|
||||
|
||||
|
@ -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);
|
||||
|
@ -52,6 +52,7 @@ export spawn_unlinked;
|
||||
export spawn_supervised;
|
||||
export spawn_with;
|
||||
export spawn_listener;
|
||||
export spawn_conversation;
|
||||
export spawn_sched;
|
||||
export try;
|
||||
|
||||
@ -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.
|
||||
@ -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
|
||||
@ -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 {
|
||||
|
@ -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));
|
||||
};
|
||||
|
||||
|
@ -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| {
|
||||
@ -65,7 +62,7 @@ fn main(args: ~[~str]) {
|
||||
~[~"", ~"10000", ~"4"]
|
||||
} else {
|
||||
args
|
||||
};
|
||||
};
|
||||
|
||||
debug!{"%?", args};
|
||||
run(args);
|
||||
|
Loading…
Reference in New Issue
Block a user