about summary refs log tree commit diff
diff options
context:
space:
mode:
authorJesse Ruderman <jruderman@gmail.com>2012-08-08 16:38:26 -0400
committerJesse Ruderman <jruderman@gmail.com>2012-08-08 16:57:13 -0400
commita76e4334b39a2287b039f858c3bd3d5f5d6e2449 (patch)
tree18fbc7835e6d70ffc18df6b36b0e80c83d204a5d
parent166cb1b28bc23303d15e8c1c4a71d0cdff0556a2 (diff)
Add spawn_conversation
-rw-r--r--doc/tutorial.md42
-rw-r--r--src/libcore/priv.rs5
-rw-r--r--src/libcore/task.rs57
-rw-r--r--src/rustdoc/page_pass.rs5
-rw-r--r--src/test/bench/msgsend.rs9
5 files changed, 67 insertions, 51 deletions
diff --git a/doc/tutorial.md b/doc/tutorial.md
index 1d0e87d1b73..f299ecccc90 100644
--- a/doc/tutorial.md
+++ b/doc/tutorial.md
@@ -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
 
diff --git a/src/libcore/priv.rs b/src/libcore/priv.rs
index ac286da79f6..2ec12b6c849 100644
--- a/src/libcore/priv.rs
+++ b/src/libcore/priv.rs
@@ -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);
diff --git a/src/libcore/task.rs b/src/libcore/task.rs
index 04882373b37..282f692d2d6 100644
--- a/src/libcore/task.rs
+++ b/src/libcore/task.rs
@@ -52,6 +52,7 @@ export spawn_unlinked;
 export spawn_supervised;
 export spawn_with;
 export spawn_listener;
+export spawn_conversation;
 export spawn_sched;
 export try;
 
@@ -377,6 +378,20 @@ impl task_builder for task_builder {
     }
 
     /**
+     * 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
@@ -1717,6 +1725,17 @@ fn test_spawn_listiner_bidi() {
 }
 
 #[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 {
         ~"Success!"
diff --git a/src/rustdoc/page_pass.rs b/src/rustdoc/page_pass.rs
index 09543ae59ca..592f8e00190 100644
--- a/src/rustdoc/page_pass.rs
+++ b/src/rustdoc/page_pass.rs
@@ -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));
     };
 
diff --git a/src/test/bench/msgsend.rs b/src/test/bench/msgsend.rs
index 120a5f3a1f0..3bf89e43035 100644
--- a/src/test/bench/msgsend.rs
+++ b/src/test/bench/msgsend.rs
@@ -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);