about summary refs log tree commit diff
path: root/src
diff options
context:
space:
mode:
authorEric Holk <eric.holk@gmail.com>2012-08-01 11:56:46 -0700
committerEric Holk <eric.holk@gmail.com>2012-08-01 12:30:07 -0700
commite7a69fbe4e33602dd6f78d140aafa5125b8eec71 (patch)
treeefeae6782ee2a24f579f17c7156300dc15e79178 /src
parent899400cd1a7465ffcc80d848d6cb2b6fb2436e02 (diff)
downloadrust-e7a69fbe4e33602dd6f78d140aafa5125b8eec71.tar.gz
rust-e7a69fbe4e33602dd6f78d140aafa5125b8eec71.zip
Working on documentation of pipes.
Diffstat (limited to 'src')
-rw-r--r--src/libcore/pipes.rs144
-rw-r--r--src/libsyntax/ext/pipes.rs34
2 files changed, 162 insertions, 16 deletions
diff --git a/src/libcore/pipes.rs b/src/libcore/pipes.rs
index 3d115463844..429651616a7 100644
--- a/src/libcore/pipes.rs
+++ b/src/libcore/pipes.rs
@@ -1,4 +1,47 @@
-// Runtime support for pipes.
+/*! Runtime support for message passing with protocol enforcement.
+
+
+Pipes consist of two endpoints. One endpoint can send messages and
+the other can receive messages. The set of legal messages and which
+directions they can flow at any given point are determined by a
+protocol. Below is an example protocol.
+
+~~~
+proto! pingpong {
+    ping: send {
+        ping -> pong
+    }
+    pong: recv {
+        pong -> ping
+    }
+}
+~~~
+
+The `proto!` syntax extension will convert this into a module called
+`pingpong`, which includes a set of types and functions that can be
+used to write programs that follow the pingpong protocol.
+
+*/
+
+/* IMPLEMENTATION NOTES
+
+The initial design for this feature is available at:
+
+https://github.com/eholk/rust/wiki/Proposal-for-channel-contracts
+
+Much of the design in that document is still accurate. There are
+several components for the pipe implementation. First of all is the
+syntax extension. To see how that works, it is best see comments in
+libsyntax/ext/pipes.rs.
+
+This module includes two related pieces of the runtime
+implementation. There is support for unbounded and bounded
+protocols. The main difference between the two is the type of the
+buffer that is carried along in the endpoint data structures.
+
+FIXME (#3072) - This is still incomplete
+
+*/
 
 import unsafe::{forget, reinterpret_cast, transmute};
 import either::{either, left, right};
@@ -11,7 +54,7 @@ export send_packet_buffered, recv_packet_buffered;
 export packet, mk_packet, entangle_buffer, has_buffer, buffer_header;
 
 // export these so we can find them in the buffer_resource
-// destructor. This is probably another metadata bug.
+// destructor. This is probably a symptom of #3005.
 export atomic_add_acq, atomic_sub_rel;
 
 // User-level things
@@ -20,16 +63,14 @@ export select, select2, selecti, select2i, selectable;
 export spawn_service, spawn_service_recv;
 export stream, port, chan, shared_chan, port_set, channel;
 
+#[doc(hidden)]
 const SPIN_COUNT: uint = 0;
 
 macro_rules! move_it {
     { $x:expr } => { unsafe { let y <- *ptr::addr_of($x); y } }
 }
 
-// This is to help make sure we only move out of enums in safe
-// places. Once there is unary move, it can be removed.
-fn move_it<T>(-x: T) -> T { x }
-
+#[doc(hidden)]
 enum state {
     empty,
     full,
@@ -37,7 +78,7 @@ enum state {
     terminated
 }
 
-class buffer_header {
+struct buffer_header {
     // Tracks whether this buffer needs to be freed. We can probably
     // get away with restricting it to 0 or 1, if we're careful.
     let mut ref_count: int;
@@ -49,12 +90,13 @@ class buffer_header {
 }
 
 // This is for protocols to associate extra data to thread around.
+#[doc(hidden)]
 type buffer<T: send> = {
     header: buffer_header,
     data: T,
 };
 
-class packet_header {
+struct packet_header {
     let mut state: state;
     let mut blocked_task: option<*rust_task>;
 
@@ -95,6 +137,7 @@ class packet_header {
     }
 }
 
+#[doc(hidden)]
 type packet<T: send> = {
     header: packet_header,
     mut payload: option<T>,
@@ -133,6 +176,7 @@ fn unibuffer<T: send>() -> ~buffer<packet<T>> {
     b
 }
 
+#[doc(hidden)]
 fn packet<T: send>() -> *packet<T> {
     let b = unibuffer();
     let p = ptr::addr_of(b.data);
@@ -141,6 +185,7 @@ fn packet<T: send>() -> *packet<T> {
     p
 }
 
+#[doc(hidden)]
 fn entangle_buffer<T: send, Tstart: send>(
     -buffer: ~buffer<T>,
     init: fn(*libc::c_void, x: &T) -> *packet<Tstart>)
@@ -163,18 +208,22 @@ extern mod rusti {
 
 // If I call the rusti versions directly from a polymorphic function,
 // I get link errors. This is a bug that needs investigated more.
+#[doc(hidden)]
 fn atomic_xchng_rel(&dst: int, src: int) -> int {
     rusti::atomic_xchng_rel(dst, src)
 }
 
+#[doc(hidden)]
 fn atomic_add_acq(&dst: int, src: int) -> int {
     rusti::atomic_add_acq(dst, src)
 }
 
+#[doc(hidden)]
 fn atomic_sub_rel(&dst: int, src: int) -> int {
     rusti::atomic_sub_rel(dst, src)
 }
 
+#[doc(hidden)]
 type rust_task = libc::c_void;
 
 extern mod rustrt {
@@ -188,6 +237,7 @@ extern mod rustrt {
     pure fn task_signal_event(target: *rust_task, event: *libc::c_void);
 }
 
+#[doc(hidden)]
 fn wait_event(this: *rust_task) -> *libc::c_void {
     let mut event = ptr::null();
 
@@ -198,6 +248,7 @@ fn wait_event(this: *rust_task) -> *libc::c_void {
     event
 }
 
+#[doc(hidden)]
 fn swap_state_acq(&dst: state, src: state) -> state {
     unsafe {
         reinterpret_cast(rusti::atomic_xchng_acq(
@@ -206,6 +257,7 @@ fn swap_state_acq(&dst: state, src: state) -> state {
     }
 }
 
+#[doc(hidden)]
 fn swap_state_rel(&dst: state, src: state) -> state {
     unsafe {
         reinterpret_cast(rusti::atomic_xchng_rel(
@@ -214,11 +266,12 @@ fn swap_state_rel(&dst: state, src: state) -> state {
     }
 }
 
+#[doc(hidden)]
 unsafe fn get_buffer<T: send>(p: *packet_header) -> ~buffer<T> {
     transmute((*p).buf_header())
 }
 
-class buffer_resource<T: send> {
+struct buffer_resource<T: send> {
     let buffer: ~buffer<T>;
     new(+b: ~buffer<T>) {
         //let p = ptr::addr_of(*b);
@@ -244,6 +297,7 @@ class buffer_resource<T: send> {
     }
 }
 
+#[doc(hidden)]
 fn send<T: send, Tbuffer: send>(-p: send_packet_buffered<T, Tbuffer>,
                                 -payload: T) {
     let header = p.header();
@@ -281,10 +335,21 @@ fn send<T: send, Tbuffer: send>(-p: send_packet_buffered<T, Tbuffer>,
     }
 }
 
+/** Receives a message from a pipe.
+
+Fails if the sender closes the connection.
+
+*/
 fn recv<T: send, Tbuffer: send>(-p: recv_packet_buffered<T, Tbuffer>) -> T {
     option::unwrap(try_recv(p))
 }
 
+/** Attempts to receive a message from a pipe.
+
+Returns `none` if the sender has closed the connection without sending
+a message, or `some(T)` if a message was received.
+
+*/
 fn try_recv<T: send, Tbuffer: send>(-p: recv_packet_buffered<T, Tbuffer>)
     -> option<T>
 {
@@ -351,6 +416,7 @@ impl peek<T: send, Tb: send> for recv_packet_buffered<T, Tb> {
     }
 }
 
+#[doc(hidden)]
 fn sender_terminate<T: send>(p: *packet<T>) {
     let p = unsafe { &*p };
     alt swap_state_rel(p.header.state, terminated) {
@@ -377,6 +443,7 @@ fn sender_terminate<T: send>(p: *packet<T>) {
     }
 }
 
+#[doc(hidden)]
 fn receiver_terminate<T: send>(p: *packet<T>) {
     let p = unsafe { &*p };
     alt swap_state_rel(p.header.state, terminated) {
@@ -394,8 +461,16 @@ fn receiver_terminate<T: send>(p: *packet<T>) {
     }
 }
 
-#[doc = "Returns when one of the packet headers reports data is
-available."]
+/** Returns when one of the packet headers reports data is available.
+
+This function is primarily intended for building higher level waiting
+functions, such as `select`, `select2`, etc.
+
+It takes a vector slice of packet_headers and returns an index into
+that vector. The index points to an endpoint that has either been
+closed by the sender or has a message waiting to be received.
+
+*/
 fn wait_many(pkts: &[*packet_header]) -> uint {
     let this = rustrt::rust_get_task();
 
@@ -447,6 +522,34 @@ fn wait_many(pkts: &[*packet_header]) -> uint {
     ready_packet
 }
 
+/** Receives a message from one of two endpoints.
+
+The return value is `left` if the first endpoint received something,
+or `right` if the second endpoint receives something. In each case,
+the result includes the other endpoint as well so it can be used
+again. Below is an example of using `select2`.
+
+~~~
+match select2(a, b) {
+  left((none, b)) {
+    // endpoint a was closed.
+  }
+  right((a, none)) {
+    // endpoint b was closed.
+  }
+  left((some(_), b)) {
+    // endpoint a received a message
+  }
+  right(a, some(_)) {
+    // endpoint b received a message.
+  }
+}
+~~~
+
+Sometimes messages will be available on both endpoints at once. In
+this case, `select2` may return either `left` or `right`.
+
+*/
 fn select2<A: send, Ab: send, B: send, Bb: send>(
     +a: recv_packet_buffered<A, Ab>,
     +b: recv_packet_buffered<B, Bb>)
@@ -500,13 +603,16 @@ fn select<T: send, Tb: send>(+endpoints: ~[recv_packet_buffered<T, Tb>])
     (ready, result, remaining)
 }
 
+/// The sending end of a pipe. It can be used to send exactly one
+/// message.
 type send_packet<T: send> = send_packet_buffered<T, packet<T>>;
 
+#[doc(hidden)]
 fn send_packet<T: send>(p: *packet<T>) -> send_packet<T> {
     send_packet_buffered(p)
 }
 
-class send_packet_buffered<T: send, Tbuffer: send> {
+struct send_packet_buffered<T: send, Tbuffer: send> {
     let mut p: option<*packet<T>>;
     let mut buffer: option<buffer_resource<Tbuffer>>;
     new(p: *packet<T>) {
@@ -560,13 +666,16 @@ class send_packet_buffered<T: send, Tbuffer: send> {
     }
 }
 
+/// Represents the receive end of a pipe. It can receive exactly one
+/// message.
 type recv_packet<T: send> = recv_packet_buffered<T, packet<T>>;
 
+#[doc(hidden)]
 fn recv_packet<T: send>(p: *packet<T>) -> recv_packet<T> {
     recv_packet_buffered(p)
 }
 
-class recv_packet_buffered<T: send, Tbuffer: send> : selectable {
+struct recv_packet_buffered<T: send, Tbuffer: send> : selectable {
     let mut p: option<*packet<T>>;
     let mut buffer: option<buffer_resource<Tbuffer>>;
     new(p: *packet<T>) {
@@ -620,6 +729,7 @@ class recv_packet_buffered<T: send, Tbuffer: send> : selectable {
     }
 }
 
+#[doc(hidden)]
 fn entangle<T: send>() -> (send_packet<T>, recv_packet<T>) {
     let p = packet();
     (send_packet(p), recv_packet(p))
@@ -686,12 +796,14 @@ trait recv<T: send> {
     pure fn peek() -> bool;
 }
 
+#[doc(hidden)]
 type chan_<T:send> = { mut endp: option<streamp::client::open<T>> };
 
 enum chan<T:send> {
     chan_(chan_<T>)
 }
 
+#[doc(hidden)]
 type port_<T:send> = { mut endp: option<streamp::server::open<T>> };
 
 enum port<T:send> {
@@ -725,7 +837,7 @@ impl port<T: send> of recv<T> for port<T> {
     fn try_recv() -> option<T> {
         let mut endp = none;
         endp <-> self.endp;
-        alt move_it(pipes::try_recv(unwrap(endp))) {
+        alt move pipes::try_recv(unwrap(endp)) {
           some(streamp::data(x, endp)) {
             self.endp = some(move_it!{endp});
             some(move_it!{x})
@@ -749,7 +861,7 @@ impl port<T: send> of recv<T> for port<T> {
 }
 
 // Treat a whole bunch of ports as one.
-class port_set<T: send> : recv<T> {
+struct port_set<T: send> : recv<T> {
     let mut ports: ~[pipes::port<T>];
 
     new() { self.ports = ~[]; }
@@ -770,7 +882,7 @@ class port_set<T: send> : recv<T> {
             let i = wait_many(self.ports.map(|p| p.header()));
             // dereferencing an unsafe pointer nonsense to appease the
             // borrowchecker.
-            alt move_it(unsafe {(*ptr::addr_of(self.ports[i])).try_recv()}) {
+            alt move unsafe {(*ptr::addr_of(self.ports[i])).try_recv()} {
               some(m) {
                   result = some(move_it!{m});
               }
diff --git a/src/libsyntax/ext/pipes.rs b/src/libsyntax/ext/pipes.rs
index 5a1462cea30..08562c4490f 100644
--- a/src/libsyntax/ext/pipes.rs
+++ b/src/libsyntax/ext/pipes.rs
@@ -1,3 +1,37 @@
+/*! Implementation of proto! extension.
+
+This is frequently called the pipe compiler. It handles code such as...
+
+~~~
+proto! pingpong {
+    ping: send {
+        ping -> pong
+    }
+    pong: recv {
+        pong -> ping
+    }
+}
+~~~
+
+There are several components:
+
+ * The parser (libsyntax/ext/pipes/parse_proto.rs)
+   * Responsible for building an AST from a protocol specification.
+
+ * The checker (libsyntax/ext/pipes/check.rs)
+   * Basic correctness checking for protocols (i.e. no undefined states, etc.)
+
+ * The analyzer (libsyntax/ext/pipes/liveness.rs)
+   * Determines whether the protocol is bounded or unbounded.
+
+ * The compiler (libsynatx/ext/pipes/pipec.rs)
+   * Generates a Rust AST from the protocol AST and the results of analysis.
+
+There is more documentation in each of the files referenced above.
+
+FIXME (#3072) - This is still incomplete.
+
+*/
 
 import codemap::span;
 import ext::base::ext_ctxt;