diff --git a/doc/tutorial-tasks.md b/doc/tutorial-tasks.md index 41cd796325c32..6213a0cfe1c25 100644 --- a/doc/tutorial-tasks.md +++ b/doc/tutorial-tasks.md @@ -121,7 +121,7 @@ receiving messages. Pipes are low-level communication building-blocks and so come in a variety of forms, each one appropriate for a different use case. In what follows, we cover the most commonly used varieties. -The simplest way to create a pipe is to use the `comm::stream` +The simplest way to create a pipe is to use `Chan::new` function to create a `(Port, Chan)` pair. In Rust parlance, a *channel* is a sending endpoint of a pipe, and a *port* is the receiving endpoint. Consider the following example of calculating two results @@ -129,9 +129,8 @@ concurrently: ~~~~ # use std::task::spawn; -# use std::comm::{stream, Port, Chan}; -let (port, chan): (Port, Chan) = stream(); +let (port, chan): (Port, Chan) = Chan::new(); do spawn || { let result = some_expensive_computation(); @@ -150,8 +149,7 @@ stream for sending and receiving integers (the left-hand side of the `let`, a tuple into its component parts). ~~~~ -# use std::comm::{stream, Chan, Port}; -let (port, chan): (Port, Chan) = stream(); +let (port, chan): (Port, Chan) = Chan::new(); ~~~~ The child task will use the channel to send data to the parent task, @@ -160,9 +158,8 @@ spawns the child task. ~~~~ # use std::task::spawn; -# use std::comm::stream; # fn some_expensive_computation() -> int { 42 } -# let (port, chan) = stream(); +# let (port, chan) = Chan::new(); do spawn || { let result = some_expensive_computation(); chan.send(result); @@ -180,25 +177,23 @@ computation, then waits for the child's result to arrive on the port: ~~~~ -# use std::comm::{stream}; # fn some_other_expensive_computation() {} -# let (port, chan) = stream::(); +# let (port, chan) = Chan::::new(); # chan.send(0); some_other_expensive_computation(); let result = port.recv(); ~~~~ -The `Port` and `Chan` pair created by `stream` enables efficient communication -between a single sender and a single receiver, but multiple senders cannot use -a single `Chan`, and multiple receivers cannot use a single `Port`. What if our -example needed to compute multiple results across a number of tasks? The -following program is ill-typed: +The `Port` and `Chan` pair created by `Chan::new` enables efficient +communication between a single sender and a single receiver, but multiple +senders cannot use a single `Chan`, and multiple receivers cannot use a single +`Port`. What if our example needed to compute multiple results across a number +of tasks? The following program is ill-typed: ~~~ {.xfail-test} # use std::task::{spawn}; -# use std::comm::{stream, Port, Chan}; # fn some_expensive_computation() -> int { 42 } -let (port, chan) = stream(); +let (port, chan) = Chan::new(); do spawn { chan.send(some_expensive_computation()); @@ -216,10 +211,8 @@ Instead we can use a `SharedChan`, a type that allows a single ~~~ # use std::task::spawn; -# use std::comm::{stream, SharedChan}; -let (port, chan) = stream(); -let chan = SharedChan::new(chan); +let (port, chan) = SharedChan::new(); for init_val in range(0u, 3) { // Create a new channel handle to distribute to the child task @@ -238,23 +231,22 @@ Here we transfer ownership of the channel into a new `SharedChan` value. Like as an *affine* or *linear* type). Unlike with `Chan`, though, the programmer may duplicate a `SharedChan`, with the `clone()` method. A cloned `SharedChan` produces a new handle to the same channel, allowing multiple -tasks to send data to a single port. Between `spawn`, `stream` and +tasks to send data to a single port. Between `spawn`, `Chan` and `SharedChan`, we have enough tools to implement many useful concurrency patterns. Note that the above `SharedChan` example is somewhat contrived since -you could also simply use three `stream` pairs, but it serves to +you could also simply use three `Chan` pairs, but it serves to illustrate the point. For reference, written with multiple streams, it might look like the example below. ~~~ # use std::task::spawn; -# use std::comm::stream; # use std::vec; // Create a vector of ports, one for each child task let ports = vec::from_fn(3, |init_val| { - let (port, chan) = stream(); + let (port, chan) = Chan::new(); do spawn { chan.send(some_expensive_computation(init_val)); } @@ -341,7 +333,7 @@ fn main() { let numbers_arc = Arc::new(numbers); for num in range(1u, 10) { - let (port, chan) = stream(); + let (port, chan) = Chan::new(); chan.send(numbers_arc.clone()); do spawn { @@ -370,7 +362,7 @@ and a clone of it is sent to each task # use std::rand; # let numbers=vec::from_fn(1000000, |_| rand::random::()); # let numbers_arc = Arc::new(numbers); -# let (port, chan) = stream(); +# let (port, chan) = Chan::new(); chan.send(numbers_arc.clone()); ~~~ copying only the wrapper and not its contents. @@ -382,7 +374,7 @@ Each task recovers the underlying data by # use std::rand; # let numbers=vec::from_fn(1000000, |_| rand::random::()); # let numbers_arc=Arc::new(numbers); -# let (port, chan) = stream(); +# let (port, chan) = Chan::new(); # chan.send(numbers_arc.clone()); # let local_arc : Arc<~[f64]> = port.recv(); let task_numbers = local_arc.get(); @@ -499,7 +491,7 @@ Here is the code for the parent task: # } # fn main() { -let (from_child, to_child) = DuplexStream(); +let (from_child, to_child) = DuplexStream::new(); do spawn { stringifier(&to_child); diff --git a/src/etc/licenseck.py b/src/etc/licenseck.py index 91231430d2a0b..78d0973fdfe2e 100644 --- a/src/etc/licenseck.py +++ b/src/etc/licenseck.py @@ -77,6 +77,7 @@ "rt/isaac/rand.h", # public domain "rt/isaac/standard.h", # public domain "libstd/rt/mpsc_queue.rs", # BSD + "libstd/rt/spsc_queue.rs", # BSD "libstd/rt/mpmc_bounded_queue.rs", # BSD ] diff --git a/src/libextra/arc.rs b/src/libextra/arc.rs index 5700a29994592..6add053fa8181 100644 --- a/src/libextra/arc.rs +++ b/src/libextra/arc.rs @@ -597,7 +597,6 @@ mod tests { use arc::*; - use std::comm; use std::task; #[test] @@ -605,7 +604,7 @@ mod tests { let v = ~[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; let arc_v = Arc::new(v); - let (p, c) = comm::stream(); + let (p, c) = Chan::new(); do task::spawn { let arc_v: Arc<~[int]> = p.recv(); @@ -626,7 +625,7 @@ mod tests { fn test_mutex_arc_condvar() { let arc = ~MutexArc::new(false); let arc2 = ~arc.clone(); - let (p,c) = comm::oneshot(); + let (p,c) = Chan::new(); do task::spawn { // wait until parent gets in p.recv(); @@ -636,9 +635,8 @@ mod tests { }) } - let mut c = Some(c); arc.access_cond(|state, cond| { - c.take_unwrap().send(()); + c.send(()); assert!(!*state); while !*state { cond.wait(); @@ -650,7 +648,7 @@ mod tests { fn test_arc_condvar_poison() { let arc = ~MutexArc::new(1); let arc2 = ~arc.clone(); - let (p, c) = comm::stream(); + let (p, c) = Chan::new(); do spawn { let _ = p.recv(); @@ -687,7 +685,7 @@ mod tests { pub fn test_mutex_arc_unwrap_poison() { let arc = MutexArc::new(1); let arc2 = ~(&arc).clone(); - let (p, c) = comm::stream(); + let (p, c) = Chan::new(); do task::spawn { arc2.access(|one| { c.send(()); @@ -804,7 +802,7 @@ mod tests { fn test_rw_arc() { let arc = RWArc::new(0); let arc2 = arc.clone(); - let (p, c) = comm::stream(); + let (p, c) = Chan::new(); do task::spawn { arc2.write(|num| { @@ -832,7 +830,7 @@ mod tests { }); // Wait for children to pass their asserts - for r in children.iter() { + for r in children.mut_iter() { r.recv(); } @@ -855,7 +853,7 @@ mod tests { // Reader tasks let mut reader_convos = ~[]; 10.times(|| { - let ((rp1, rc1), (rp2, rc2)) = (comm::stream(), comm::stream()); + let ((rp1, rc1), (rp2, rc2)) = (Chan::new(), Chan::new()); reader_convos.push((rc1, rp2)); let arcn = arc.clone(); do task::spawn { @@ -869,7 +867,7 @@ mod tests { // Writer task let arc2 = arc.clone(); - let ((wp1, wc1), (wp2, wc2)) = (comm::stream(), comm::stream()); + let ((wp1, wc1), (wp2, wc2)) = (Chan::new(), Chan::new()); do task::spawn || { wp1.recv(); arc2.write_cond(|state, cond| { @@ -897,14 +895,14 @@ mod tests { assert_eq!(*state, 42); *state = 31337; // send to other readers - for &(ref rc, _) in reader_convos.iter() { + for &(ref mut rc, _) in reader_convos.mut_iter() { rc.send(()) } }); let read_mode = arc.downgrade(write_mode); read_mode.read(|state| { // complete handshake with other readers - for &(_, ref rp) in reader_convos.iter() { + for &(_, ref mut rp) in reader_convos.mut_iter() { rp.recv() } wc1.send(()); // tell writer to try again @@ -926,7 +924,7 @@ mod tests { // "blk(&Condvar { order: opt_lock, ..*cond })" // with just "blk(cond)". let x = RWArc::new(true); - let (wp, wc) = comm::stream(); + let (wp, wc) = Chan::new(); // writer task let xw = x.clone(); @@ -951,7 +949,7 @@ mod tests { }); // make a reader task to trigger the "reader cloud lock" handoff let xr = x.clone(); - let (rp, rc) = comm::stream(); + let (rp, rc) = Chan::new(); do task::spawn { rc.send(()); xr.read(|_state| { }) diff --git a/src/libextra/comm.rs b/src/libextra/comm.rs index 42287736ffa5f..09dd85fe0de46 100644 --- a/src/libextra/comm.rs +++ b/src/libextra/comm.rs @@ -16,11 +16,6 @@ Higher level communication abstractions. #[allow(missing_doc)]; - -use std::comm::{GenericChan, GenericSmartChan, GenericPort}; -use std::comm::{Chan, Port, Peekable}; -use std::comm; - /// An extension of `pipes::stream` that allows both sending and receiving. pub struct DuplexStream { priv chan: Chan, @@ -29,108 +24,73 @@ pub struct DuplexStream { // Allow these methods to be used without import: impl DuplexStream { + /// Creates a bidirectional stream. + pub fn new() -> (DuplexStream, DuplexStream) { + let (p1, c2) = Chan::new(); + let (p2, c1) = Chan::new(); + (DuplexStream { chan: c1, port: p1 }, + DuplexStream { chan: c2, port: p2 }) + } pub fn send(&self, x: T) { self.chan.send(x) } pub fn try_send(&self, x: T) -> bool { self.chan.try_send(x) } - pub fn recv(&self, ) -> U { + pub fn recv(&self) -> U { self.port.recv() } pub fn try_recv(&self) -> Option { self.port.try_recv() } - pub fn peek(&self) -> bool { - self.port.peek() - } -} - -impl GenericChan for DuplexStream { - fn send(&self, x: T) { - self.chan.send(x) - } -} - -impl GenericSmartChan for DuplexStream { - fn try_send(&self, x: T) -> bool { - self.chan.try_send(x) - } -} - -impl GenericPort for DuplexStream { - fn recv(&self) -> U { - self.port.recv() - } - - fn try_recv(&self) -> Option { - self.port.try_recv() - } -} - -impl Peekable for DuplexStream { - fn peek(&self) -> bool { - self.port.peek() + pub fn recv_opt(&self) -> Option { + self.port.recv_opt() } } -/// Creates a bidirectional stream. -pub fn DuplexStream() - -> (DuplexStream, DuplexStream) -{ - let (p1, c2) = comm::stream(); - let (p2, c1) = comm::stream(); - (DuplexStream { - chan: c1, - port: p1 - }, - DuplexStream { - chan: c2, - port: p2 - }) -} - /// An extension of `pipes::stream` that provides synchronous message sending. pub struct SyncChan { priv duplex_stream: DuplexStream } /// An extension of `pipes::stream` that acknowledges each message received. pub struct SyncPort { priv duplex_stream: DuplexStream<(), T> } -impl GenericChan for SyncChan { - fn send(&self, val: T) { +impl SyncChan { + pub fn send(&self, val: T) { assert!(self.try_send(val), "SyncChan.send: receiving port closed"); } -} -impl GenericSmartChan for SyncChan { - /// Sends a message, or report if the receiver has closed the connection before receiving. - fn try_send(&self, val: T) -> bool { - self.duplex_stream.try_send(val) && self.duplex_stream.try_recv().is_some() + /// Sends a message, or report if the receiver has closed the connection + /// before receiving. + pub fn try_send(&self, val: T) -> bool { + self.duplex_stream.try_send(val) && self.duplex_stream.recv_opt().is_some() } } -impl GenericPort for SyncPort { - fn recv(&self) -> T { - self.try_recv().expect("SyncPort.recv: sending channel closed") +impl SyncPort { + pub fn recv(&self) -> T { + self.recv_opt().expect("SyncPort.recv: sending channel closed") } - fn try_recv(&self) -> Option { - self.duplex_stream.try_recv().map(|val| { + pub fn recv_opt(&self) -> Option { + self.duplex_stream.recv_opt().map(|val| { self.duplex_stream.try_send(()); val }) } -} -impl Peekable for SyncPort { - fn peek(&self) -> bool { - self.duplex_stream.peek() + pub fn try_recv(&self) -> Option { + self.duplex_stream.try_recv().map(|val| { + self.duplex_stream.try_send(()); + val + }) } } -/// Creates a stream whose channel, upon sending a message, blocks until the message is received. +/// Creates a stream whose channel, upon sending a message, blocks until the +/// message is received. pub fn rendezvous() -> (SyncPort, SyncChan) { - let (chan_stream, port_stream) = DuplexStream(); - (SyncPort { duplex_stream: port_stream }, SyncChan { duplex_stream: chan_stream }) + let (chan_stream, port_stream) = DuplexStream::new(); + (SyncPort { duplex_stream: port_stream }, + SyncChan { duplex_stream: chan_stream }) } #[cfg(test)] @@ -141,7 +101,7 @@ mod test { #[test] pub fn DuplexStream1() { - let (left, right) = DuplexStream(); + let (mut left, mut right) = DuplexStream::new(); left.send(~"abc"); right.send(123); @@ -152,9 +112,10 @@ mod test { #[test] pub fn basic_rendezvous_test() { - let (port, chan) = rendezvous(); + let (mut port, chan) = rendezvous(); do spawn { + let mut chan = chan; chan.send("abc"); } @@ -165,8 +126,9 @@ mod test { fn recv_a_lot() { // Rendezvous streams should be able to handle any number of messages being sent do run_in_uv_task { - let (port, chan) = rendezvous(); + let (mut port, chan) = rendezvous(); do spawn { + let mut chan = chan; 1000000.times(|| { chan.send(()) }) } 1000000.times(|| { port.recv() }) @@ -175,8 +137,9 @@ mod test { #[test] fn send_and_fail_and_try_recv() { - let (port, chan) = rendezvous(); + let (mut port, chan) = rendezvous(); do spawn { + let mut chan = chan; chan.duplex_stream.send(()); // Can't access this field outside this module fail!() } @@ -185,8 +148,9 @@ mod test { #[test] fn try_send_and_recv_then_fail_before_ack() { - let (port, chan) = rendezvous(); + let (port, mut chan) = rendezvous(); do spawn { + let mut port = port; port.duplex_stream.recv(); fail!() } @@ -196,8 +160,9 @@ mod test { #[test] #[should_fail] fn send_and_recv_then_fail_before_ack() { - let (port, chan) = rendezvous(); + let (port, mut chan) = rendezvous(); do spawn { + let mut port = port; port.duplex_stream.recv(); fail!() } diff --git a/src/libextra/future.rs b/src/libextra/future.rs index 1a2ac39813270..eb61b7781f1ba 100644 --- a/src/libextra/future.rs +++ b/src/libextra/future.rs @@ -25,7 +25,6 @@ #[allow(missing_doc)]; -use std::comm::{PortOne, oneshot}; use std::util::replace; /// A type encapsulating the result of a computation which may not be complete @@ -104,7 +103,7 @@ impl Future { } impl Future { - pub fn from_port(port: PortOne) -> Future { + pub fn from_port(port: Port) -> Future { /*! * Create a future from a port * @@ -125,7 +124,7 @@ impl Future { * value of the future. */ - let (port, chan) = oneshot(); + let (port, chan) = Chan::new(); do spawn { chan.send(blk()); @@ -139,7 +138,6 @@ impl Future { mod test { use future::Future; - use std::comm::oneshot; use std::task; #[test] @@ -150,7 +148,7 @@ mod test { #[test] fn test_from_port() { - let (po, ch) = oneshot(); + let (po, ch) = Chan::new(); ch.send(~"whale"); let mut f = Future::from_port(po); assert_eq!(f.get(), ~"whale"); diff --git a/src/libextra/sync.rs b/src/libextra/sync.rs index f00c3d8db9a0c..6e58298296277 100644 --- a/src/libextra/sync.rs +++ b/src/libextra/sync.rs @@ -19,9 +19,6 @@ use std::borrow; -use std::comm; -use std::comm::SendDeferred; -use std::comm::{GenericPort, Peekable}; use std::unstable::sync::{Exclusive, UnsafeArc}; use std::unstable::atomics; use std::unstable::finally::Finally; @@ -34,48 +31,53 @@ use std::util::NonCopyable; // Each waiting task receives on one of these. #[doc(hidden)] -type WaitEnd = comm::PortOne<()>; +type WaitEnd = Port<()>; #[doc(hidden)] -type SignalEnd = comm::ChanOne<()>; +type SignalEnd = Chan<()>; // A doubly-ended queue of waiting tasks. #[doc(hidden)] -struct WaitQueue { head: comm::Port, - tail: comm::Chan } +struct WaitQueue { head: Port, + tail: Chan } impl WaitQueue { fn new() -> WaitQueue { - let (block_head, block_tail) = comm::stream(); + let (block_head, block_tail) = Chan::new(); WaitQueue { head: block_head, tail: block_tail } } // Signals one live task from the queue. fn signal(&self) -> bool { - // The peek is mandatory to make sure recv doesn't block. - if self.head.peek() { - // Pop and send a wakeup signal. If the waiter was killed, its port - // will have closed. Keep trying until we get a live task. - if self.head.recv().try_send_deferred(()) { - true - } else { - self.signal() + match self.head.try_recv() { + Some(ch) => { + // Send a wakeup signal. If the waiter was killed, its port will + // have closed. Keep trying until we get a live task. + if ch.try_send_deferred(()) { + true + } else { + self.signal() + } } - } else { - false + None => false } } fn broadcast(&self) -> uint { let mut count = 0; - while self.head.peek() { - if self.head.recv().try_send_deferred(()) { - count += 1; + loop { + match self.head.try_recv() { + None => break, + Some(ch) => { + if ch.try_send_deferred(()) { + count += 1; + } + } } } count } fn wait_end(&self) -> WaitEnd { - let (wait_end, signal_end) = comm::oneshot(); + let (wait_end, signal_end) = Chan::new(); self.tail.send_deferred(signal_end); wait_end } @@ -282,8 +284,7 @@ impl<'a> Condvar<'a> { condvar_id, "cond.signal_on()", || { - let queue = queue.take_unwrap(); - queue.broadcast() + queue.take_unwrap().broadcast() }) } } @@ -676,7 +677,6 @@ mod tests { use sync::*; use std::cast; - use std::comm; use std::result; use std::task; @@ -711,7 +711,7 @@ mod tests { #[test] fn test_sem_as_cvar() { /* Child waits and parent signals */ - let (p, c) = comm::stream(); + let (p, c) = Chan::new(); let s = Semaphore::new(0); let s2 = s.clone(); do task::spawn { @@ -723,7 +723,7 @@ mod tests { let _ = p.recv(); /* Parent waits and child signals */ - let (p, c) = comm::stream(); + let (p, c) = Chan::new(); let s = Semaphore::new(0); let s2 = s.clone(); do task::spawn { @@ -740,8 +740,8 @@ mod tests { // time, and shake hands. let s = Semaphore::new(2); let s2 = s.clone(); - let (p1,c1) = comm::stream(); - let (p2,c2) = comm::stream(); + let (p1,c1) = Chan::new(); + let (p2,c2) = Chan::new(); do task::spawn { s2.access(|| { let _ = p2.recv(); @@ -760,7 +760,7 @@ mod tests { do task::spawn_sched(task::SingleThreaded) { let s = Semaphore::new(1); let s2 = s.clone(); - let (p, c) = comm::stream(); + let (p, c) = Chan::new(); let mut child_data = Some((s2, c)); s.access(|| { let (s2, c) = child_data.take_unwrap(); @@ -782,7 +782,7 @@ mod tests { fn test_mutex_lock() { // Unsafely achieve shared state, and do the textbook // "load tmp = move ptr; inc tmp; store ptr <- tmp" dance. - let (p, c) = comm::stream(); + let (p, c) = Chan::new(); let m = Mutex::new(); let m2 = m.clone(); let mut sharedstate = ~0; @@ -829,7 +829,7 @@ mod tests { cond.wait(); }); // Parent wakes up child - let (port,chan) = comm::stream(); + let (port,chan) = Chan::new(); let m3 = m.clone(); do task::spawn { m3.lock_cond(|cond| { @@ -852,7 +852,7 @@ mod tests { num_waiters.times(|| { let mi = m.clone(); - let (port, chan) = comm::stream(); + let (port, chan) = Chan::new(); ports.push(port); do task::spawn { mi.lock_cond(|cond| { @@ -864,13 +864,13 @@ mod tests { }); // wait until all children get in the mutex - for port in ports.iter() { let _ = port.recv(); } + for port in ports.mut_iter() { let _ = port.recv(); } m.lock_cond(|cond| { let num_woken = cond.broadcast(); assert_eq!(num_woken, num_waiters); }); // wait until all children wake up - for port in ports.iter() { let _ = port.recv(); } + for port in ports.mut_iter() { let _ = port.recv(); } } #[test] fn test_mutex_cond_broadcast() { @@ -915,8 +915,8 @@ mod tests { let m2 = m.clone(); let result: result::Result<(), ~Any> = do task::try { - let (p, c) = comm::stream(); - do task::spawn || { // linked + let (p, c) = Chan::new(); + do task::spawn { // linked let _ = p.recv(); // wait for sibling to get in the mutex task::deschedule(); fail!(); @@ -940,19 +940,17 @@ mod tests { let m = Mutex::new(); let m2 = m.clone(); - let (p, c) = comm::stream(); + let (p, c) = Chan::new(); let result: result::Result<(), ~Any> = do task::try { let mut sibling_convos = ~[]; 2.times(|| { - let (p, c) = comm::stream(); + let (p, c) = Chan::new(); sibling_convos.push(p); let mi = m2.clone(); // spawn sibling task do task::spawn { // linked - let mut c = Some(c); mi.lock_cond(|cond| { - let c = c.take_unwrap(); c.send(()); // tell sibling to go ahead (|| { cond.wait(); // block forever @@ -964,7 +962,7 @@ mod tests { }) } }); - for p in sibling_convos.iter() { + for p in sibling_convos.mut_iter() { let _ = p.recv(); // wait for sibling to get in the mutex } m2.lock(|| { }); @@ -973,8 +971,8 @@ mod tests { }; assert!(result.is_err()); // child task must have finished by the time try returns - let r = p.recv(); - for p in r.iter() { p.recv(); } // wait on all its siblings + let mut r = p.recv(); + for p in r.mut_iter() { p.recv(); } // wait on all its siblings m.lock_cond(|cond| { let woken = cond.broadcast(); assert_eq!(woken, 0); @@ -995,11 +993,12 @@ mod tests { }) } #[test] + #[ignore(reason = "linked failure?")] fn test_mutex_different_conds() { let result = do task::try { let m = Mutex::new_with_condvars(2); let m2 = m.clone(); - let (p, c) = comm::stream(); + let (p, c) = Chan::new(); do task::spawn { m2.lock_cond(|cond| { c.send(()); @@ -1060,7 +1059,7 @@ mod tests { mode2: RWLockMode) { // Test mutual exclusion between readers and writers. Just like the // mutex mutual exclusion test, a ways above. - let (p, c) = comm::stream(); + let (p, c) = Chan::new(); let x2 = x.clone(); let mut sharedstate = ~0; { @@ -1111,8 +1110,8 @@ mod tests { make_mode2_go_first: bool) { // Much like sem_multi_resource. let x2 = x.clone(); - let (p1, c1) = comm::stream(); - let (p2, c2) = comm::stream(); + let (p1, c1) = Chan::new(); + let (p2, c2) = Chan::new(); do task::spawn { if !make_mode2_go_first { let _ = p2.recv(); // parent sends to us once it locks, or ... @@ -1177,7 +1176,7 @@ mod tests { cond.wait(); }); // Parent wakes up child - let (port, chan) = comm::stream(); + let (port, chan) = Chan::new(); let x3 = x.clone(); do task::spawn { x3.write_cond(|cond| { @@ -1214,7 +1213,7 @@ mod tests { num_waiters.times(|| { let xi = x.clone(); - let (port, chan) = comm::stream(); + let (port, chan) = Chan::new(); ports.push(port); do task::spawn { lock_cond(&xi, dg1, |cond| { @@ -1226,13 +1225,13 @@ mod tests { }); // wait until all children get in the mutex - for port in ports.iter() { let _ = port.recv(); } + for port in ports.mut_iter() { let _ = port.recv(); } lock_cond(&x, dg2, |cond| { let num_woken = cond.broadcast(); assert_eq!(num_woken, num_waiters); }); // wait until all children wake up - for port in ports.iter() { let _ = port.recv(); } + for port in ports.mut_iter() { let _ = port.recv(); } } #[test] fn test_rwlock_cond_broadcast() { diff --git a/src/libextra/task_pool.rs b/src/libextra/task_pool.rs index bda6935643f04..f0c9833adf804 100644 --- a/src/libextra/task_pool.rs +++ b/src/libextra/task_pool.rs @@ -14,8 +14,6 @@ /// parallelism. -use std::comm::{Chan, GenericChan, GenericPort}; -use std::comm; use std::task::SchedMode; use std::task; use std::vec; @@ -35,7 +33,7 @@ pub struct TaskPool { #[unsafe_destructor] impl Drop for TaskPool { fn drop(&mut self) { - for channel in self.channels.iter() { + for channel in self.channels.mut_iter() { channel.send(Quit); } } @@ -54,7 +52,7 @@ impl TaskPool { assert!(n_tasks >= 1); let channels = vec::from_fn(n_tasks, |i| { - let (port, chan) = comm::stream::>(); + let (port, chan) = Chan::>::new(); let init_fn = init_fn_factory(); let task_body: proc() = proc() { diff --git a/src/libextra/test.rs b/src/libextra/test.rs index c2b4ff05d5d03..974d4dc1dc520 100644 --- a/src/libextra/test.rs +++ b/src/libextra/test.rs @@ -29,7 +29,6 @@ use time::precise_time_ns; use treemap::TreeMap; use std::clone::Clone; -use std::comm::{stream, SharedChan, GenericPort, GenericChan}; use std::io; use std::io::File; use std::io::Writer; @@ -746,8 +745,7 @@ fn run_tests(opts: &TestOpts, remaining.reverse(); let mut pending = 0; - let (p, ch) = stream(); - let ch = SharedChan::new(ch); + let (p, ch) = SharedChan::new(); while pending > 0 || !remaining.is_empty() { while pending < concurrency && !remaining.is_empty() { @@ -872,7 +870,7 @@ pub fn run_test(force_ignore: bool, fn run_test_inner(desc: TestDesc, monitor_ch: SharedChan, testfn: proc()) { - do task::spawn { + do spawn { let mut task = task::task(); task.name(match desc.name { DynTestName(ref name) => SendStrOwned(name.clone()), @@ -1206,7 +1204,6 @@ mod tests { StaticTestName, DynTestName, DynTestFn}; use test::{TestOpts, run_test}; - use std::comm::{stream, SharedChan}; use tempfile::TempDir; #[test] @@ -1220,8 +1217,7 @@ mod tests { }, testfn: DynTestFn(proc() f()), }; - let (p, ch) = stream(); - let ch = SharedChan::new(ch); + let (p, ch) = SharedChan::new(); run_test(false, desc, ch); let (_, res) = p.recv(); assert!(res != TrOk); @@ -1238,8 +1234,7 @@ mod tests { }, testfn: DynTestFn(proc() f()), }; - let (p, ch) = stream(); - let ch = SharedChan::new(ch); + let (p, ch) = SharedChan::new(); run_test(false, desc, ch); let (_, res) = p.recv(); assert_eq!(res, TrIgnored); @@ -1256,8 +1251,7 @@ mod tests { }, testfn: DynTestFn(proc() f()), }; - let (p, ch) = stream(); - let ch = SharedChan::new(ch); + let (p, ch) = SharedChan::new(); run_test(false, desc, ch); let (_, res) = p.recv(); assert_eq!(res, TrOk); @@ -1274,8 +1268,7 @@ mod tests { }, testfn: DynTestFn(proc() f()), }; - let (p, ch) = stream(); - let ch = SharedChan::new(ch); + let (p, ch) = SharedChan::new(); run_test(false, desc, ch); let (_, res) = p.recv(); assert_eq!(res, TrFailed); diff --git a/src/libextra/workcache.rs b/src/libextra/workcache.rs index 91f1f1a0d0ba2..8713dbde9205a 100644 --- a/src/libextra/workcache.rs +++ b/src/libextra/workcache.rs @@ -15,8 +15,7 @@ use json::ToJson; use serialize::{Encoder, Encodable, Decoder, Decodable}; use arc::{Arc,RWArc}; use treemap::TreeMap; -use std::comm::{PortOne, oneshot}; -use std::{str, task}; +use std::str; use std::io; use std::io::{File, Decorator}; use std::io::mem::MemWriter; @@ -252,7 +251,7 @@ pub struct Exec { enum Work<'a, T> { WorkValue(T), - WorkFromTask(&'a Prep<'a>, PortOne<(Exec, T)>), + WorkFromTask(&'a Prep<'a>, Port<(Exec, T)>), } fn json_encode<'a, T:Encodable>>(t: &T) -> ~str { @@ -427,11 +426,11 @@ impl<'a> Prep<'a> { _ => { debug!("Cache miss!"); - let (port, chan) = oneshot(); + let (port, chan) = Chan::new(); let blk = bo.take_unwrap(); // XXX: What happens if the task fails? - do task::spawn { + do spawn { let mut exe = Exec { discovered_inputs: WorkMap::new(), discovered_outputs: WorkMap::new(), @@ -453,7 +452,7 @@ impl<'a, T:Send + pub fn from_value(elt: T) -> Work<'a, T> { WorkValue(elt) } - pub fn from_task(prep: &'a Prep<'a>, port: PortOne<(Exec, T)>) + pub fn from_task(prep: &'a Prep<'a>, port: Port<(Exec, T)>) -> Work<'a, T> { WorkFromTask(prep, port) } diff --git a/src/librustc/lib.rs b/src/librustc/lib.rs index a26fd9f5af2dd..c5be6776eef11 100644 --- a/src/librustc/lib.rs +++ b/src/librustc/lib.rs @@ -27,6 +27,7 @@ use driver::driver::{compile_input}; use driver::session; use middle::lint; +use std::cast; use std::comm; use std::io; use std::io::Reader; @@ -303,7 +304,8 @@ impl diagnostic::Emitter for RustcEmitter { msg: &str, lvl: diagnostic::level) { if lvl == diagnostic::fatal { - self.ch_capture.send(fatal) + let this = unsafe { cast::transmute_mut(self) }; + this.ch_capture.send(fatal) } diagnostic::DefaultEmitter.emit(cmsp, msg, lvl) @@ -333,8 +335,7 @@ pub fn monitor(f: proc(@diagnostic::Emitter)) { #[cfg(not(rtopt))] static STACK_SIZE: uint = 20000000; // 20MB - let (p, ch) = stream(); - let ch = SharedChan::new(ch); + let (p, ch) = SharedChan::new(); let ch_capture = ch.clone(); let mut task_builder = task::task(); task_builder.name("rustc"); diff --git a/src/librustdoc/html/format.rs b/src/librustdoc/html/format.rs index 661f96e7c1a3c..04da17d4ec441 100644 --- a/src/librustdoc/html/format.rs +++ b/src/librustdoc/html/format.rs @@ -174,71 +174,70 @@ fn path(w: &mut io::Writer, path: &clean::Path, print_all: bool, let loc = loc.unwrap(); local_data::get(cache_key, |cache| { - cache.unwrap().read(|cache| { - let abs_root = root(cache, loc.as_slice()); - let rel_root = match path.segments[0].name.as_slice() { - "self" => Some(~"./"), - _ => None, - }; - - if print_all { - let amt = path.segments.len() - 1; - match rel_root { - Some(root) => { - let mut root = root; - for seg in path.segments.slice_to(amt).iter() { - if "super" == seg.name || "self" == seg.name { - write!(w, "{}::", seg.name); - } else { - root.push_str(seg.name); - root.push_str("/"); - write!(w, "{}::", - root, - seg.name); - } - } - } - None => { - for seg in path.segments.slice_to(amt).iter() { + let cache = cache.unwrap().get(); + let abs_root = root(cache, loc.as_slice()); + let rel_root = match path.segments[0].name.as_slice() { + "self" => Some(~"./"), + _ => None, + }; + + if print_all { + let amt = path.segments.len() - 1; + match rel_root { + Some(root) => { + let mut root = root; + for seg in path.segments.slice_to(amt).iter() { + if "super" == seg.name || "self" == seg.name { write!(w, "{}::", seg.name); + } else { + root.push_str(seg.name); + root.push_str("/"); + write!(w, "{}::", + root, + seg.name); } } } + None => { + for seg in path.segments.slice_to(amt).iter() { + write!(w, "{}::", seg.name); + } + } } + } - match info(cache) { - // This is a documented path, link to it! - Some((ref fqp, shortty)) if abs_root.is_some() => { - let mut url = abs_root.unwrap(); - let to_link = fqp.slice_to(fqp.len() - 1); - for component in to_link.iter() { - url.push_str(*component); - url.push_str("/"); + match info(cache) { + // This is a documented path, link to it! + Some((ref fqp, shortty)) if abs_root.is_some() => { + let mut url = abs_root.unwrap(); + let to_link = fqp.slice_to(fqp.len() - 1); + for component in to_link.iter() { + url.push_str(*component); + url.push_str("/"); + } + match shortty { + "mod" => { + url.push_str(*fqp.last()); + url.push_str("/index.html"); } - match shortty { - "mod" => { - url.push_str(*fqp.last()); - url.push_str("/index.html"); - } - _ => { - url.push_str(shortty); - url.push_str("."); - url.push_str(*fqp.last()); - url.push_str(".html"); - } + _ => { + url.push_str(shortty); + url.push_str("."); + url.push_str(*fqp.last()); + url.push_str(".html"); } - - write!(w, "{}", - shortty, url, fqp.connect("::"), last.name); } - _ => { - write!(w, "{}", last.name); - } + write!(w, "{}", + shortty, url, fqp.connect("::"), last.name); } - write!(w, "{}", generics); - }) + + _ => { + write!(w, "{}", last.name); + } + } + write!(w, "{}", generics); }) }) } @@ -263,9 +262,8 @@ impl fmt::Default for clean::Type { match *g { clean::TyParamBinder(id) | clean::Generic(id) => { local_data::get(cache_key, |cache| { - cache.unwrap().read(|m| { - f.buf.write(m.typarams.get(&id).as_bytes()); - }) + let m = cache.unwrap().get(); + f.buf.write(m.typarams.get(&id).as_bytes()); }) } clean::ResolvedPath{id, typarams: ref tp, path: ref path} => { diff --git a/src/librustdoc/html/render.rs b/src/librustdoc/html/render.rs index 0c703e780f19b..82122c4c32fb5 100644 --- a/src/librustdoc/html/render.rs +++ b/src/librustdoc/html/render.rs @@ -33,8 +33,6 @@ //! These tasks are not parallelized (they haven't been a bottleneck yet), and //! both occur before the crate is rendered. -use std::comm::{SharedPort, SharedChan}; -use std::comm; use std::fmt; use std::hashmap::{HashMap, HashSet}; use std::local_data; @@ -42,12 +40,10 @@ use std::io::buffered::BufferedWriter; use std::io; use std::io::fs; use std::io::File; -use std::os; use std::str; -use std::task; use std::vec; -use extra::arc::RWArc; +use extra::arc::Arc; use extra::json::ToJson; use extra::sort; @@ -121,7 +117,7 @@ enum Implementor { /// /// This structure purposefully does not implement `Clone` because it's intended /// to be a fairly large and expensive structure to clone. Instead this adheres -/// to both `Send` and `Freeze` so it may be stored in a `RWArc` instance and +/// to both `Send` and `Freeze` so it may be stored in a `Arc` instance and /// shared among the various rendering tasks. pub struct Cache { /// Mapping of typaram ids to the name of the type parameter. This is used @@ -197,7 +193,7 @@ struct IndexItem { // TLS keys used to carry information around during rendering. -local_data_key!(pub cache_key: RWArc) +local_data_key!(pub cache_key: Arc) local_data_key!(pub current_location_key: ~[~str]) /// Generates the documentation for `crate` into the directory `dst` @@ -640,22 +636,6 @@ impl<'a> Cache { } } -enum Progress { - JobNew, - JobDone, -} - -/// A helper object to unconditionally send a value on a chanel. -struct ChannelGuard { - channel: SharedChan, -} - -impl Drop for ChannelGuard { - fn drop(&mut self) { - self.channel.send(JobDone) - } -} - impl Context { /// Recurse in the directory structure and change the "root path" to make /// sure it always points to the top (relatively) @@ -680,97 +660,26 @@ impl Context { return ret; } - /// Main method for rendering a crate. This parallelizes the task of - /// rendering a crate, and requires ownership of the crate in order to break - /// it up into its separate components. - fn crate(self, mut crate: clean::Crate, cache: Cache) { - enum Work { - Die, - Process(Context, clean::Item), - } - let workers = match os::getenv("RUSTDOC_WORKERS") { - Some(s) => { - match from_str::(s) { - Some(n) => n, None => fail!("{} not a number", s) - } - } - None => 10, - }; - + /// Main method for rendering a crate. + /// + /// This currently isn't parallelized, but it'd be pretty easy to add + /// parallelization to this function. + fn crate(mut self, mut crate: clean::Crate, cache: Cache) { let mut item = match crate.module.take() { Some(i) => i, None => return }; item.name = Some(crate.name); - let (port, chan) = comm::stream::(); - let port = SharedPort::new(port); - let chan = SharedChan::new(chan); - let (prog_port, prog_chan) = comm::stream(); - let prog_chan = SharedChan::new(prog_chan); - let cache = RWArc::new(cache); - - // Each worker thread receives work from a shared port and publishes - // new work onto the corresponding shared port. All of the workers are - // using the same channel/port. Through this, the crate is recursed on - // in a hierarchical fashion, and parallelization is only achieved if - // one node in the hierarchy has more than one child (very common). - for i in range(0, workers) { - let port = port.clone(); - let chan = chan.clone(); - let prog_chan = prog_chan.clone(); - - let mut task = task::task(); - task.name(format!("worker{}", i)); - let cache = cache.clone(); - do task.spawn { - worker(cache, &port, &chan, &prog_chan); - } - - fn worker(cache: RWArc, - port: &SharedPort, - chan: &SharedChan, - prog_chan: &SharedChan) { - local_data::set(cache_key, cache); - - loop { - match port.recv() { - Process(cx, item) => { - let mut cx = cx; - - // If we fail, everything else should still get - // completed. - let _guard = ChannelGuard { - channel: prog_chan.clone(), - }; - cx.item(item, |cx, item| { - prog_chan.send(JobNew); - chan.send(Process(cx.clone(), item)); - }) - } - Die => break, - } - } - } - } - - // Send off the initial job - chan.send(Process(self, item)); - let mut jobs = 1; + // using a rwarc makes this parallelizable in the future + local_data::set(cache_key, Arc::new(cache)); - // Keep track of the number of jobs active in the system and kill - // everything once there are no more jobs remaining. - loop { - match prog_port.recv() { - JobNew => jobs += 1, - JobDone => jobs -= 1, - } - - if jobs == 0 { break } - } - - for _ in range(0, workers) { - chan.send(Die); + let mut work = ~[item]; + while work.len() > 0 { + let item = work.pop(); + self.item(item, |_cx, item| { + work.push(item); + }) } } @@ -1210,29 +1119,28 @@ fn item_trait(w: &mut Writer, it: &clean::Item, t: &clean::Trait) { } local_data::get(cache_key, |cache| { - cache.unwrap().read(|cache| { - match cache.implementors.find(&it.id) { - Some(implementors) => { - write!(w, " -

Implementors

-
    - "); - for i in implementors.iter() { - match *i { - PathType(ref ty) => { - write!(w, "
  • {}
  • ", *ty); - } - OtherType(ref generics, ref trait_, ref for_) => { - write!(w, "
  • impl{} {} for {}
  • ", - *generics, *trait_, *for_); - } + let cache = cache.unwrap().get(); + match cache.implementors.find(&it.id) { + Some(implementors) => { + write!(w, " +

    Implementors

    +
      + "); + for i in implementors.iter() { + match *i { + PathType(ref ty) => { + write!(w, "
    • {}
    • ", *ty); + } + OtherType(ref generics, ref trait_, ref for_) => { + write!(w, "
    • impl{} {} for {}
    • ", + *generics, *trait_, *for_); } } - write!(w, "
    "); } - None => {} + write!(w, "
"); } - }) + None => {} + } }) } @@ -1422,36 +1330,34 @@ fn render_struct(w: &mut Writer, it: &clean::Item, fn render_methods(w: &mut Writer, it: &clean::Item) { local_data::get(cache_key, |cache| { - let cache = cache.unwrap(); - cache.read(|c| { - match c.impls.find(&it.id) { - Some(v) => { - let mut non_trait = v.iter().filter(|p| { - p.n0_ref().trait_.is_none() - }); - let non_trait = non_trait.to_owned_vec(); - let mut traits = v.iter().filter(|p| { - p.n0_ref().trait_.is_some() - }); - let traits = traits.to_owned_vec(); - - if non_trait.len() > 0 { - write!(w, "

Methods

"); - for &(ref i, ref dox) in non_trait.move_iter() { - render_impl(w, i, dox); - } + let c = cache.unwrap().get(); + match c.impls.find(&it.id) { + Some(v) => { + let mut non_trait = v.iter().filter(|p| { + p.n0_ref().trait_.is_none() + }); + let non_trait = non_trait.to_owned_vec(); + let mut traits = v.iter().filter(|p| { + p.n0_ref().trait_.is_some() + }); + let traits = traits.to_owned_vec(); + + if non_trait.len() > 0 { + write!(w, "

Methods

"); + for &(ref i, ref dox) in non_trait.move_iter() { + render_impl(w, i, dox); } - if traits.len() > 0 { - write!(w, "

Trait \ - Implementations

"); - for &(ref i, ref dox) in traits.move_iter() { - render_impl(w, i, dox); - } + } + if traits.len() > 0 { + write!(w, "

Trait \ + Implementations

"); + for &(ref i, ref dox) in traits.move_iter() { + render_impl(w, i, dox); } } - None => {} } - }) + None => {} + } }) } @@ -1502,27 +1408,26 @@ fn render_impl(w: &mut Writer, i: &clean::Impl, dox: &Option<~str>) { Some(id) => id, }; local_data::get(cache_key, |cache| { - cache.unwrap().read(|cache| { - match cache.traits.find(&trait_id) { - Some(t) => { - let name = meth.name.clone(); - match t.methods.iter().find(|t| t.item().name == name) { - Some(method) => { - match method.item().doc_value() { - Some(s) => { - write!(w, - "
{}
", - Markdown(s)); - } - None => {} + let cache = cache.unwrap().get(); + match cache.traits.find(&trait_id) { + Some(t) => { + let name = meth.name.clone(); + match t.methods.iter().find(|t| t.item().name == name) { + Some(method) => { + match method.item().doc_value() { + Some(s) => { + write!(w, + "
{}
", + Markdown(s)); } + None => {} } - None => {} } + None => {} } - None => {} } - }) + None => {} + } }) } @@ -1532,22 +1437,21 @@ fn render_impl(w: &mut Writer, i: &clean::Impl, dox: &Option<~str>) { None => {} Some(id) => { local_data::get(cache_key, |cache| { - cache.unwrap().read(|cache| { - match cache.traits.find(&id) { - Some(t) => { - for method in t.methods.iter() { - let n = method.item().name.clone(); - match i.methods.iter().find(|m| m.name == n) { - Some(..) => continue, - None => {} - } - - docmeth(w, method.item()); + let cache = cache.unwrap().get(); + match cache.traits.find(&id) { + Some(t) => { + for method in t.methods.iter() { + let n = method.item().name.clone(); + match i.methods.iter().find(|m| m.name == n) { + Some(..) => continue, + None => {} } + + docmeth(w, method.item()); } - None => {} } - }) + None => {} + } }) } } diff --git a/src/librustuv/net.rs b/src/librustuv/net.rs index 6f1930bc7fe31..ce543eafd2f64 100644 --- a/src/librustuv/net.rs +++ b/src/librustuv/net.rs @@ -646,7 +646,6 @@ impl Drop for UdpWatcher { #[cfg(test)] mod test { - use std::comm::oneshot; use std::rt::test::*; use std::rt::rtio::{RtioTcpStream, RtioTcpListener, RtioTcpAcceptor, RtioUdpSocket}; @@ -689,7 +688,7 @@ mod test { #[test] fn listen_ip4() { - let (port, chan) = oneshot(); + let (port, chan) = Chan::new(); let addr = next_test_ip4(); do spawn { @@ -725,7 +724,7 @@ mod test { #[test] fn listen_ip6() { - let (port, chan) = oneshot(); + let (port, chan) = Chan::new(); let addr = next_test_ip6(); do spawn { @@ -761,7 +760,7 @@ mod test { #[test] fn udp_recv_ip4() { - let (port, chan) = oneshot(); + let (port, chan) = Chan::new(); let client = next_test_ip4(); let server = next_test_ip4(); @@ -793,7 +792,7 @@ mod test { #[test] fn udp_recv_ip6() { - let (port, chan) = oneshot(); + let (port, chan) = Chan::new(); let client = next_test_ip6(); let server = next_test_ip6(); @@ -828,7 +827,7 @@ mod test { use std::rt::rtio::*; let addr = next_test_ip4(); static MAX: uint = 5000; - let (port, chan) = oneshot(); + let (port, chan) = Chan::new(); do spawn { let listener = TcpListener::bind(local_loop(), addr).unwrap(); @@ -865,7 +864,7 @@ mod test { fn test_udp_twice() { let server_addr = next_test_ip4(); let client_addr = next_test_ip4(); - let (port, chan) = oneshot(); + let (port, chan) = Chan::new(); do spawn { let mut client = UdpWatcher::bind(local_loop(), client_addr).unwrap(); @@ -896,8 +895,8 @@ mod test { let client_in_addr = next_test_ip4(); static MAX: uint = 500_000; - let (p1, c1) = oneshot(); - let (p2, c2) = oneshot(); + let (p1, c1) = Chan::new(); + let (p2, c2) = Chan::new(); do spawn { let l = local_loop(); @@ -953,12 +952,12 @@ mod test { #[test] fn test_read_and_block() { let addr = next_test_ip4(); - let (port, chan) = oneshot(); + let (port, chan) = Chan::new(); do spawn { let listener = TcpListener::bind(local_loop(), addr).unwrap(); let mut acceptor = listener.listen().unwrap(); - let (port2, chan2) = stream(); + let (port2, chan2) = Chan::new(); chan.send(port2); let mut stream = acceptor.accept().unwrap(); let mut buf = [0, .. 2048]; @@ -1026,7 +1025,7 @@ mod test { // thread, close itself, and then come back to the last thread. #[test] fn test_homing_closes_correctly() { - let (port, chan) = oneshot(); + let (port, chan) = Chan::new(); do task::spawn_sched(task::SingleThreaded) { let listener = UdpWatcher::bind(local_loop(), next_test_ip4()).unwrap(); @@ -1048,9 +1047,9 @@ mod test { use std::rt::sched::{Shutdown, TaskFromFriend}; use std::rt::sleeper_list::SleeperList; use std::rt::task::Task; - use std::rt::task::UnwindResult; use std::rt::thread::Thread; use std::rt::deque::BufferPool; + use std::task::TaskResult; use std::unstable::run_in_bare_thread; use uvio::UvEventLoop; @@ -1072,12 +1071,12 @@ mod test { let handle2 = sched2.make_handle(); let tasksFriendHandle = sched2.make_handle(); - let on_exit: proc(UnwindResult) = proc(exit_status) { + let on_exit: proc(TaskResult) = proc(exit_status) { let mut handle1 = handle1; let mut handle2 = handle2; handle1.send(Shutdown); handle2.send(Shutdown); - assert!(exit_status.is_success()); + assert!(exit_status.is_ok()); }; unsafe fn local_io() -> &'static mut IoFactory { @@ -1148,7 +1147,7 @@ mod test { #[should_fail] #[test] fn tcp_stream_fail_cleanup() { - let (port, chan) = oneshot(); + let (port, chan) = Chan::new(); let addr = next_test_ip4(); do spawn { @@ -1172,7 +1171,7 @@ mod test { #[should_fail] #[test] fn udp_fail_other_task() { let addr = next_test_ip4(); - let (port, chan) = oneshot(); + let (port, chan) = Chan::new(); // force the handle to be created on a different scheduler, failure in // the original task will force a homing operation back to this @@ -1190,7 +1189,7 @@ mod test { #[test] #[ignore(reason = "linked failure")] fn linked_failure1() { - let (port, chan) = oneshot(); + let (port, chan) = Chan::new(); let addr = next_test_ip4(); do spawn { @@ -1208,7 +1207,7 @@ mod test { #[test] #[ignore(reason = "linked failure")] fn linked_failure2() { - let (port, chan) = oneshot(); + let (port, chan) = Chan::new(); let addr = next_test_ip4(); do spawn { @@ -1229,7 +1228,7 @@ mod test { #[test] #[ignore(reason = "linked failure")] fn linked_failure3() { - let (port, chan) = stream(); + let (port, chan) = Chan::new(); let addr = next_test_ip4(); do spawn { diff --git a/src/librustuv/pipe.rs b/src/librustuv/pipe.rs index 86ebae45f1953..814205cbbf1cc 100644 --- a/src/librustuv/pipe.rs +++ b/src/librustuv/pipe.rs @@ -231,7 +231,6 @@ impl HomingIO for PipeAcceptor { #[cfg(test)] mod tests { - use std::comm::oneshot; use std::rt::rtio::{RtioUnixListener, RtioUnixAcceptor, RtioPipe}; use std::rt::test::next_test_unix; @@ -274,7 +273,7 @@ mod tests { fn connect() { let path = next_test_unix(); let path2 = path.clone(); - let (port, chan) = oneshot(); + let (port, chan) = Chan::new(); do spawn { let p = PipeListener::bind(local_loop(), &path2.to_c_str()).unwrap(); @@ -298,7 +297,7 @@ mod tests { fn connect_fail() { let path = next_test_unix(); let path2 = path.clone(); - let (port, chan) = oneshot(); + let (port, chan) = Chan::new(); do spawn { let p = PipeListener::bind(local_loop(), &path2.to_c_str()).unwrap(); diff --git a/src/librustuv/signal.rs b/src/librustuv/signal.rs index db698f80e38c1..f082aef003c60 100644 --- a/src/librustuv/signal.rs +++ b/src/librustuv/signal.rs @@ -11,7 +11,7 @@ use std::libc::c_int; use std::io::signal::Signum; use std::rt::sched::{SchedHandle, Scheduler}; -use std::comm::{SharedChan, SendDeferred}; +use std::comm::SharedChan; use std::rt::local::Local; use std::rt::rtio::RtioSignal; @@ -78,13 +78,11 @@ mod test { use super::*; use super::super::local_loop; use std::io::signal; - use std::comm::{SharedChan, stream}; #[test] fn closing_channel_during_drop_doesnt_kill_everything() { // see issue #10375, relates to timers as well. - let (port, chan) = stream(); - let chan = SharedChan::new(chan); + let (port, chan) = SharedChan::new(); let _signal = SignalWatcher::new(local_loop(), signal::Interrupt, chan); diff --git a/src/librustuv/timer.rs b/src/librustuv/timer.rs index a229c000066d8..ab143d6e8b077 100644 --- a/src/librustuv/timer.rs +++ b/src/librustuv/timer.rs @@ -8,7 +8,6 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -use std::comm::{oneshot, stream, PortOne, ChanOne, SendDeferred}; use std::libc::c_int; use std::rt::BlockedTask; use std::rt::local::Local; @@ -24,12 +23,13 @@ pub struct TimerWatcher { handle: *uvll::uv_timer_t, home: SchedHandle, action: Option, + id: uint, // see comments in timer_cb } pub enum NextAction { WakeTask(BlockedTask), - SendOnce(ChanOne<()>), - SendMany(Chan<()>), + SendOnce(Chan<()>), + SendMany(Chan<()>, uint), } impl TimerWatcher { @@ -42,6 +42,7 @@ impl TimerWatcher { handle: handle, action: None, home: get_handle_to_current_scheduler!(), + id: 0, }; return me.install(); } @@ -73,6 +74,7 @@ impl RtioTimer for TimerWatcher { // we must temporarily un-home ourselves, then destroy the action, and // then re-home again. let missile = self.fire_homing_missile(); + self.id += 1; self.stop(); let _missile = match util::replace(&mut self.action, None) { None => missile, // no need to do a homing dance @@ -95,13 +97,14 @@ impl RtioTimer for TimerWatcher { self.stop(); } - fn oneshot(&mut self, msecs: u64) -> PortOne<()> { - let (port, chan) = oneshot(); + fn oneshot(&mut self, msecs: u64) -> Port<()> { + let (port, chan) = Chan::new(); // similarly to the destructor, we must drop the previous action outside // of the homing missile let _prev_action = { let _m = self.fire_homing_missile(); + self.id += 1; self.stop(); self.start(msecs, 0); util::replace(&mut self.action, Some(SendOnce(chan))) @@ -111,15 +114,16 @@ impl RtioTimer for TimerWatcher { } fn period(&mut self, msecs: u64) -> Port<()> { - let (port, chan) = stream(); + let (port, chan) = Chan::new(); // similarly to the destructor, we must drop the previous action outside // of the homing missile let _prev_action = { let _m = self.fire_homing_missile(); + self.id += 1; self.stop(); self.start(msecs, msecs); - util::replace(&mut self.action, Some(SendMany(chan))) + util::replace(&mut self.action, Some(SendMany(chan, self.id))) }; return port; @@ -136,10 +140,21 @@ extern fn timer_cb(handle: *uvll::uv_timer_t, status: c_int) { let sched: ~Scheduler = Local::take(); sched.resume_blocked_task_immediately(task); } - SendOnce(chan) => chan.send_deferred(()), - SendMany(chan) => { - chan.send_deferred(()); - timer.action = Some(SendMany(chan)); + SendOnce(chan) => { chan.try_send_deferred(()); } + SendMany(chan, id) => { + chan.try_send_deferred(()); + + // Note that the above operation could have performed some form of + // scheduling. This means that the timer may have decided to insert + // some other action to happen. This 'id' keeps track of the updates + // to the timer, so we only reset the action back to sending on this + // channel if the id has remained the same. This is essentially a + // bug in that we have mutably aliasable memory, but that's libuv + // for you. We're guaranteed to all be running on the same thread, + // so there's no need for any synchronization here. + if timer.id == id { + timer.action = Some(SendMany(chan, id)); + } } } } @@ -181,8 +196,8 @@ mod test { let oport = timer.oneshot(1); let pport = timer.period(1); timer.sleep(1); - assert_eq!(oport.try_recv(), None); - assert_eq!(pport.try_recv(), None); + assert_eq!(oport.recv_opt(), None); + assert_eq!(pport.recv_opt(), None); timer.oneshot(1).recv(); } @@ -231,7 +246,7 @@ mod test { let timer_port = timer.period(1000); do spawn { - timer_port.try_recv(); + timer_port.recv_opt(); } // when we drop the TimerWatcher we're going to destroy the channel, @@ -245,7 +260,7 @@ mod test { let timer_port = timer.period(1000); do spawn { - timer_port.try_recv(); + timer_port.recv_opt(); } timer.oneshot(1); @@ -257,7 +272,7 @@ mod test { let timer_port = timer.period(1000); do spawn { - timer_port.try_recv(); + timer_port.recv_opt(); } timer.sleep(1); @@ -269,7 +284,7 @@ mod test { let mut timer = TimerWatcher::new(local_loop()); timer.oneshot(1000) }; - assert_eq!(port.try_recv(), None); + assert_eq!(port.recv_opt(), None); } #[test] @@ -278,7 +293,7 @@ mod test { let mut timer = TimerWatcher::new(local_loop()); timer.period(1000) }; - assert_eq!(port.try_recv(), None); + assert_eq!(port.recv_opt(), None); } #[test] diff --git a/src/libstd/comm.rs b/src/libstd/comm.rs deleted file mode 100644 index c5ed464de23c5..0000000000000 --- a/src/libstd/comm.rs +++ /dev/null @@ -1,311 +0,0 @@ -// Copyright 2012 The Rust Project Developers. See the COPYRIGHT -// file at the top-level directory of this distribution and at -// http://rust-lang.org/COPYRIGHT. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -/*! -Message passing -*/ - -#[allow(missing_doc)]; - -use clone::Clone; -use iter::Iterator; -use kinds::Send; -use option::Option; -use rtcomm = rt::comm; - -/// A trait for things that can send multiple messages. -pub trait GenericChan { - /// Sends a message. - fn send(&self, x: T); -} - -/// Things that can send multiple messages and can detect when the receiver -/// is closed -pub trait GenericSmartChan { - /// Sends a message, or report if the receiver has closed the connection. - fn try_send(&self, x: T) -> bool; -} - -/// Trait for non-rescheduling send operations, similar to `send_deferred` on ChanOne. -pub trait SendDeferred { - fn send_deferred(&self, val: T); - fn try_send_deferred(&self, val: T) -> bool; -} - -/// A trait for things that can receive multiple messages. -pub trait GenericPort { - /// Receives a message, or fails if the connection closes. - fn recv(&self) -> T; - - /// Receives a message, or returns `none` if - /// the connection is closed or closes. - fn try_recv(&self) -> Option; - - /// Returns an iterator that breaks once the connection closes. - /// - /// # Example - /// - /// ~~~rust - /// do spawn { - /// for x in port.recv_iter() { - /// if pred(x) { break; } - /// println!("{}", x); - /// } - /// } - /// ~~~ - fn recv_iter<'a>(&'a self) -> RecvIterator<'a, Self> { - RecvIterator { port: self } - } -} - -pub struct RecvIterator<'a, P> { - priv port: &'a P, -} - -impl<'a, T, P: GenericPort> Iterator for RecvIterator<'a, P> { - fn next(&mut self) -> Option { - self.port.try_recv() - } -} - -/// Ports that can `peek` -pub trait Peekable { - /// Returns true if a message is available - fn peek(&self) -> bool; -} - -/* priv is disabled to allow users to get at traits like Select. */ -pub struct PortOne { /* priv */ x: rtcomm::PortOne } -pub struct ChanOne { /* priv */ x: rtcomm::ChanOne } - -pub fn oneshot() -> (PortOne, ChanOne) { - let (p, c) = rtcomm::oneshot(); - (PortOne { x: p }, ChanOne { x: c }) -} - -pub struct Port { /* priv */ x: rtcomm::Port } -pub struct Chan { /* priv */ x: rtcomm::Chan } - -pub fn stream() -> (Port, Chan) { - let (p, c) = rtcomm::stream(); - (Port { x: p }, Chan { x: c }) -} - -impl ChanOne { - pub fn send(self, val: T) { - let ChanOne { x: c } = self; - c.send(val) - } - - pub fn try_send(self, val: T) -> bool { - let ChanOne { x: c } = self; - c.try_send(val) - } - - pub fn send_deferred(self, val: T) { - let ChanOne { x: c } = self; - c.send_deferred(val) - } - - pub fn try_send_deferred(self, val: T) -> bool { - let ChanOne{ x: c } = self; - c.try_send_deferred(val) - } -} - -impl PortOne { - pub fn recv(self) -> T { - let PortOne { x: p } = self; - p.recv() - } - - pub fn try_recv(self) -> Option { - let PortOne { x: p } = self; - p.try_recv() - } -} - -impl Peekable for PortOne { - fn peek(&self) -> bool { - let &PortOne { x: ref p } = self; - p.peek() - } -} - -impl GenericChan for Chan { - fn send(&self, val: T) { - let &Chan { x: ref c } = self; - c.send(val) - } -} - -impl GenericSmartChan for Chan { - fn try_send(&self, val: T) -> bool { - let &Chan { x: ref c } = self; - c.try_send(val) - } -} - -impl SendDeferred for Chan { - fn send_deferred(&self, val: T) { - let &Chan { x: ref c } = self; - c.send_deferred(val) - } - - fn try_send_deferred(&self, val: T) -> bool { - let &Chan { x: ref c } = self; - c.try_send_deferred(val) - } -} - -impl GenericPort for Port { - fn recv(&self) -> T { - let &Port { x: ref p } = self; - p.recv() - } - - fn try_recv(&self) -> Option { - let &Port { x: ref p } = self; - p.try_recv() - } -} - -impl Peekable for Port { - fn peek(&self) -> bool { - let &Port { x: ref p } = self; - p.peek() - } -} - - -pub struct SharedChan { /* priv */ x: rtcomm::SharedChan } - -impl SharedChan { - pub fn new(c: Chan) -> SharedChan { - let Chan { x: c } = c; - SharedChan { x: rtcomm::SharedChan::new(c) } - } -} - -impl GenericChan for SharedChan { - fn send(&self, val: T) { - let &SharedChan { x: ref c } = self; - c.send(val) - } -} - -impl GenericSmartChan for SharedChan { - fn try_send(&self, val: T) -> bool { - let &SharedChan { x: ref c } = self; - c.try_send(val) - } -} - -impl SendDeferred for SharedChan { - fn send_deferred(&self, val: T) { - let &SharedChan { x: ref c } = self; - c.send_deferred(val) - } - - fn try_send_deferred(&self, val: T) -> bool { - let &SharedChan { x: ref c } = self; - c.try_send_deferred(val) - } -} - -impl Clone for SharedChan { - fn clone(&self) -> SharedChan { - let &SharedChan { x: ref c } = self; - SharedChan { x: c.clone() } - } -} - -pub struct SharedPort { /* priv */ x: rtcomm::SharedPort } - -impl SharedPort { - pub fn new(p: Port) -> SharedPort { - let Port { x: p } = p; - SharedPort { x: rtcomm::SharedPort::new(p) } - } -} - -impl GenericPort for SharedPort { - fn recv(&self) -> T { - let &SharedPort { x: ref p } = self; - p.recv() - } - - fn try_recv(&self) -> Option { - let &SharedPort { x: ref p } = self; - p.try_recv() - } -} - -impl Clone for SharedPort { - fn clone(&self) -> SharedPort { - let &SharedPort { x: ref p } = self; - SharedPort { x: p.clone() } - } -} - -#[cfg(test)] -mod tests { - use comm::*; - use prelude::*; - - #[test] - fn test_nested_recv_iter() { - let (port, chan) = stream::(); - let (total_port, total_chan) = oneshot::(); - - do spawn { - let mut acc = 0; - for x in port.recv_iter() { - acc += x; - for x in port.recv_iter() { - acc += x; - for x in port.try_recv().move_iter() { - acc += x; - total_chan.send(acc); - } - } - } - } - - chan.send(3); - chan.send(1); - chan.send(2); - assert_eq!(total_port.recv(), 6); - } - - #[test] - fn test_recv_iter_break() { - let (port, chan) = stream::(); - let (count_port, count_chan) = oneshot::(); - - do spawn { - let mut count = 0; - for x in port.recv_iter() { - if count >= 3 { - count_chan.send(count); - break; - } else { - count += x; - } - } - } - - chan.send(2); - chan.send(2); - chan.send(2); - chan.send(2); - assert_eq!(count_port.recv(), 4); - } -} diff --git a/src/libstd/comm/imp.rs b/src/libstd/comm/imp.rs new file mode 100644 index 0000000000000..bd1d6fed901ca --- /dev/null +++ b/src/libstd/comm/imp.rs @@ -0,0 +1,337 @@ +// Copyright 2013 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +//! One of the major goals behind this channel implementation is to work +//! seamlessly on and off the runtime. This also means that the code isn't +//! littered with "if is_green() { ... } else { ... }". Right now, the rest of +//! the runtime isn't quite ready to for this abstraction to be done very +//! nicely, so the conditional "if green" blocks are all contained in this inner +//! module. +//! +//! The goal of this module is to mirror what the runtime "should be", not the +//! state that it is currently in today. You'll notice that there is no mention +//! of schedulers or is_green inside any of the channel code, it is currently +//! entirely contained in this one module. +//! +//! In the ideal world, nothing in this module exists and it is all implemented +//! elsewhere in the runtime (in the proper location). All of this code is +//! structured in order to easily refactor this to the correct location whenever +//! we have the trait objects in place to serve as the boundary of the +//! abstraction. + +use iter::{range, Iterator}; +use ops::Drop; +use option::{Some, None, Option}; +use rt::local::Local; +use rt::sched::{SchedHandle, Scheduler, TaskFromFriend}; +use rt::thread::Thread; +use rt; +use unstable::mutex::Mutex; +use unstable::sync::UnsafeArc; + +// A task handle is a method of waking up a blocked task. The handle itself +// is completely opaque and only has a wake() method defined on it. This +// method will wake the method regardless of the context of the thread which +// is currently calling wake(). +// +// This abstraction should be able to be created when putting a task to +// sleep. This should basically be a method on whatever the local Task is, +// consuming the local Task. + +pub struct TaskHandle { + priv inner: TaskRepr +} +enum TaskRepr { + Green(rt::BlockedTask, *mut SchedHandle), + Native(NativeWakeupStyle), +} +enum NativeWakeupStyle { + ArcWakeup(UnsafeArc), // shared mutex to synchronize on + LocalWakeup(*mut Mutex), // synchronize on the task-local mutex +} + +impl TaskHandle { + // Signal that this handle should be woken up. The `can_resched` + // argument indicates whether the current task could possibly be + // rescheduled or not. This does not have a lot of meaning for the + // native case, but for an M:N case it indicates whether a context + // switch can happen or not. + pub fn wake(self, can_resched: bool) { + match self.inner { + Green(task, handle) => { + // If we have a local scheduler, then use that to run the + // blocked task, otherwise we can use the handle to send the + // task back to its home. + if rt::in_green_task_context() { + if can_resched { + task.wake().map(Scheduler::run_task); + } else { + let mut s: ~Scheduler = Local::take(); + s.enqueue_blocked_task(task); + Local::put(s); + } + } else { + let task = match task.wake() { + Some(task) => task, None => return + }; + // XXX: this is not an easy section of code to refactor. + // If this handle is owned by the Task (which it + // should be), then this would be a use-after-free + // because once the task is pushed onto the message + // queue, the handle is gone. + // + // Currently the handle is instead owned by the + // Port/Chan pair, which means that because a + // channel is invoking this method the handle will + // continue to stay alive for the entire duration + // of this method. This will require thought when + // moving the handle into the task. + unsafe { (*handle).send(TaskFromFriend(task)) } + } + } + + // Note that there are no use-after-free races in this code. In + // the arc-case, we own the lock, and in the local case, we're + // using a lock so it's guranteed that they aren't running while + // we hold the lock. + Native(ArcWakeup(lock)) => { + unsafe { + let lock = lock.get(); + (*lock).lock(); + (*lock).signal(); + (*lock).unlock(); + } + } + Native(LocalWakeup(lock)) => { + unsafe { + (*lock).lock(); + (*lock).signal(); + (*lock).unlock(); + } + } + } + } + + // Trashes handle to this task. This ensures that necessary memory is + // deallocated, and there may be some extra assertions as well. + pub fn trash(self) { + match self.inner { + Green(task, _) => task.assert_already_awake(), + Native(..) => {} + } + } +} + +// This structure is an abstraction of what should be stored in the local +// task itself. This data is currently stored inside of each channel, but +// this should rather be stored in each task (and channels will still +// continue to lazily initialize this data). + +pub struct TaskData { + priv handle: Option, + priv lock: Mutex, +} + +impl TaskData { + pub fn new() -> TaskData { + TaskData { + handle: None, + lock: unsafe { Mutex::empty() }, + } + } +} + +impl Drop for TaskData { + fn drop(&mut self) { + unsafe { self.lock.destroy() } + } +} + +// Now this is the really fun part. This is where all the M:N/1:1-agnostic +// along with recv/select-agnostic blocking information goes. A "blocking +// context" is really just a stack-allocated structure (which is probably +// fine to be a stack-trait-object). +// +// This has some particularly strange interfaces, but the reason for all +// this is to support selection/recv/1:1/M:N all in one bundle. + +pub struct BlockingContext<'a> { + priv inner: BlockingRepr<'a> +} + +enum BlockingRepr<'a> { + GreenBlock(rt::BlockedTask, &'a mut Scheduler), + NativeBlock(Option>), +} + +impl<'a> BlockingContext<'a> { + // Creates one blocking context. The data provided should in theory be + // acquired from the local task, but it is instead acquired from the + // channel currently. + // + // This function will call `f` with a blocking context, plus the data + // that it is given. This function will then return whether this task + // should actually go to sleep or not. If `true` is returned, then this + // function does not return until someone calls `wake()` on the task. + // If `false` is returned, then this function immediately returns. + // + // # Safety note + // + // Note that this stack closure may not be run on the same stack as when + // this function was called. This means that the environment of this + // stack closure could be unsafely aliased. This is currently prevented + // through the guarantee that this function will never return before `f` + // finishes executing. + pub fn one(data: &mut TaskData, + f: |BlockingContext, &mut TaskData| -> bool) { + if rt::in_green_task_context() { + let sched: ~Scheduler = Local::take(); + sched.deschedule_running_task_and_then(|sched, task| { + let ctx = BlockingContext { inner: GreenBlock(task, sched) }; + // no need to do something on success/failure other than + // returning because the `block` function for a BlockingContext + // takes care of reawakening itself if the blocking procedure + // fails. If this function is successful, then we're already + // blocked, and if it fails, the task will already be + // rescheduled. + f(ctx, data); + }); + } else { + unsafe { data.lock.lock(); } + let ctx = BlockingContext { inner: NativeBlock(None) }; + if f(ctx, data) { + unsafe { data.lock.wait(); } + } + unsafe { data.lock.unlock(); } + } + } + + // Creates many blocking contexts. The intended use case for this + // function is selection over a number of ports. This will create `amt` + // blocking contexts, yielding them to `f` in turn. If `f` returns + // false, then this function aborts and returns immediately. If `f` + // repeatedly returns `true` `amt` times, then this function will block. + pub fn many(amt: uint, f: |BlockingContext| -> bool) { + if rt::in_green_task_context() { + let sched: ~Scheduler = Local::take(); + sched.deschedule_running_task_and_then(|sched, task| { + for handle in task.make_selectable(amt) { + let ctx = BlockingContext { + inner: GreenBlock(handle, sched) + }; + // see comment above in `one` for why no further action is + // necessary here + if !f(ctx) { break } + } + }); + } else { + // In the native case, our decision to block must be shared + // amongst all of the channels. It may be possible to + // stack-allocate this mutex (instead of putting it in an + // UnsafeArc box), but for now in order to prevent + // use-after-free trivially we place this into a box and then + // pass that around. + unsafe { + let mtx = UnsafeArc::new(Mutex::new()); + (*mtx.get()).lock(); + let success = range(0, amt).all(|_| { + f(BlockingContext { + inner: NativeBlock(Some(mtx.clone())) + }) + }); + if success { + (*mtx.get()).wait(); + } + (*mtx.get()).unlock(); + } + } + } + + // This function will consume this BlockingContext, and optionally block + // if according to the atomic `decision` function. The semantics of this + // functions are: + // + // * `slot` is required to be a `None`-slot (which is owned by the + // channel) + // * The `slot` will be filled in with a blocked version of the current + // task (with `wake`-ability if this function is successful). + // * If the `decision` function returns true, then this function + // immediately returns having relinquished ownership of the task. + // * If the `decision` function returns false, then the `slot` is reset + // to `None` and the task is re-scheduled if necessary (remember that + // the task will not resume executing before the outer `one` or + // `many` function has returned. This function is expected to have a + // release memory fence in order for the modifications of `to_wake` to be + // visible to other tasks. Code which attempts to read `to_wake` should + // have an acquiring memory fence to guarantee that this write is + // visible. + // + // This function will return whether the blocking occurred or not. + pub fn block(self, + data: &mut TaskData, + slot: &mut Option, + decision: || -> bool) -> bool { + assert!(slot.is_none()); + match self.inner { + GreenBlock(task, sched) => { + if data.handle.is_none() { + data.handle = Some(sched.make_handle()); + } + let handle = data.handle.get_mut_ref() as *mut SchedHandle; + *slot = Some(TaskHandle { inner: Green(task, handle) }); + + if !decision() { + match slot.take_unwrap().inner { + Green(task, _) => sched.enqueue_blocked_task(task), + Native(..) => unreachable!() + } + false + } else { + true + } + } + NativeBlock(shared) => { + *slot = Some(TaskHandle { + inner: Native(match shared { + Some(arc) => ArcWakeup(arc), + None => LocalWakeup(&mut data.lock as *mut Mutex), + }) + }); + + if !decision() { + *slot = None; + false + } else { + true + } + } + } + } +} + +// Agnostic method of forcing a yield of the current task +pub fn yield_now() { + if rt::in_green_task_context() { + let sched: ~Scheduler = Local::take(); + sched.yield_now(); + } else { + Thread::yield_now(); + } +} + +// Agnostic method of "maybe yielding" in order to provide fairness +pub fn maybe_yield() { + if rt::in_green_task_context() { + let sched: ~Scheduler = Local::take(); + sched.maybe_yield(); + } else { + // the OS decides fairness, nothing for us to do. + } +} diff --git a/src/libstd/comm/mod.rs b/src/libstd/comm/mod.rs new file mode 100644 index 0000000000000..4cbc6c7cbb7ba --- /dev/null +++ b/src/libstd/comm/mod.rs @@ -0,0 +1,1376 @@ +// Copyright 2013 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +//! Rust Communication Primitives +//! +//! Rust makes it very difficult to share data among tasks to prevent race +//! conditions and to improve parallelism, but there is often a need for +//! communication between concurrent tasks. The primitives defined in this +//! module are the building blocks for synchronization in rust. +//! +//! This module currently provides three main types: +//! +//! * `Chan` +//! * `Port` +//! * `SharedChan` +//! +//! The `Chan` and `SharedChan` types are used to send data to a `Port`. A +//! `SharedChan` is clone-able such that many tasks can send simultaneously to +//! one receiving port. These communication primitives are *task blocking*, not +//! *thread blocking*. This means that if one task is blocked on a channel, +//! other tasks can continue to make progress. +//! +//! Rust channels can be used as if they have an infinite internal buffer. What +//! this means is that the `send` operation will never block. `Port`s, on the +//! other hand, will block the task if there is no data to be received. +//! +//! ## Failure Propagation +//! +//! In addition to being a core primitive for communicating in rust, channels +//! and ports are the points at which failure is propagated among tasks. +//! Whenever the one half of channel is closed, the other half will have its +//! next operation `fail!`. The purpose of this is to allow propagation of +//! failure among tasks that are linked to one another via channels. +//! +//! There are methods on all of `Chan`, `SharedChan`, and `Port` to perform +//! their respective operations without failing, however. +//! +//! ## Outside the Runtime +//! +//! All channels and ports work seamlessly inside and outside of the rust +//! runtime. This means that code may use channels to communicate information +//! inside and outside of the runtime. For example, if rust were embedded as an +//! FFI module in another application, the rust runtime would probably be +//! running in its own external thread pool. Channels created can communicate +//! from the native application threads to the rust threads through the use of +//! native mutexes and condition variables. +//! +//! What this means is that if a native thread is using a channel, execution +//! will be blocked accordingly by blocking the OS thread. +//! +//! # Example +//! +//! ```rust +//! // Create a simple streaming channel +//! let (port, chan) = Chan::new(); +//! do spawn { +//! chan.send(10); +//! } +//! assert_eq!(port.recv(), 10); +//! +//! // Create a shared channel which can be sent along from many tasks +//! let (port, chan) = SharedChan::new(); +//! for i in range(0, 10) { +//! let chan = chan.clone(); +//! do spawn { +//! chan.send(i); +//! } +//! } +//! +//! for _ in range(0, 10) { +//! let j = port.recv(); +//! assert!(0 <= j && j < 10); +//! } +//! +//! // The call to recv() will fail!() because the channel has already hung +//! // up (or been deallocated) +//! let (port, chan) = Chan::new(); +//! drop(chan); +//! port.recv(); +//! ``` + +// A description of how Rust's channel implementation works +// +// Channels are supposed to be the basic building block for all other +// concurrent primitives that are used in Rust. As a result, the channel type +// needs to be highly optimized, flexible, and broad enough for use everywhere. +// +// The choice of implementation of all channels is to be built on lock-free data +// structures. The channels themselves are then consequently also lock-free data +// structures. As always with lock-free code, this is a very "here be dragons" +// territory, especially because I'm unaware of any academic papers which have +// gone into great length about channels of these flavors. +// +// ## Flavors of channels +// +// Rust channels come in two flavors: streams and shared channels. A stream has +// one sender and one receiver while a shared channel could have multiple +// senders. This choice heavily influences the design of the protocol set +// forth for both senders/receivers. +// +// ## Concurrent queues +// +// The basic idea of Rust's Chan/Port types is that send() never blocks, but +// recv() obviously blocks. This means that under the hood there must be some +// shared and concurrent queue holding all of the actual data. +// +// With two flavors of channels, two flavors of queues are also used. We have +// chosen to use queues from a well-known author which are abbreviated as SPSC +// and MPSC (single producer, single consumer and multiple producer, single +// consumer). SPSC queues are used for streams while MPSC queues are used for +// shared channels. +// +// ### SPSC optimizations +// +// The SPSC queue found online is essentially a linked list of nodes where one +// half of the nodes are the "queue of data" and the other half of nodes are a +// cache of unused nodes. The unused nodes are used such that an allocation is +// not required on every push() and a free doesn't need to happen on every +// pop(). +// +// As found online, however, the cache of nodes is of an infinite size. This +// means that if a channel at one point in its life had 50k items in the queue, +// then the queue will always have the capacity for 50k items. I believed that +// this was an unnecessary limitation of the implementation, so I have altered +// the queue to optionally have a bound on the cache size. +// +// By default, streams will have an unbounded SPSC queue with a small-ish cache +// size. The hope is that the cache is still large enough to have very fast +// send() operations while not too large such that millions of channels can +// coexist at once. +// +// ### MPSC optimizations +// +// Right now the MPSC queue has not been optimized. Like the SPSC queue, it uses +// a linked list under the hood to earn its unboundedness, but I have not put +// forth much effort into having a cache of nodes similar to the SPSC queue. +// +// For now, I believe that this is "ok" because shared channels are not the most +// common type, but soon we may wish to revisit this queue choice and determine +// another candidate for backend storage of shared channels. +// +// ## Overview of the Implementation +// +// Now that there's a little background on the concurrent queues used, it's +// worth going into much more detail about the channels themselves. The basic +// pseudocode for a send/recv are: +// +// +// send(t) recv() +// queue.push(t) return if queue.pop() +// if increment() == -1 deschedule { +// wakeup() if decrement() > 0 +// cancel_deschedule() +// } +// queue.pop() +// +// As mentioned before, there are no locks in this implementation, only atomic +// instructions are used. +// +// ### The internal atomic counter +// +// Every channel/port/shared channel have a shared counter with their +// counterparts to keep track of the size of the queue. This counter is used to +// abort descheduling by the receiver and to know when to wake up on the sending +// side. +// +// As seen in the pseudocode, senders will increment this count and receivers +// will decrement the count. The theory behind this is that if a sender sees a +// -1 count, it will wake up the receiver, and if the receiver sees a 1+ count, +// then it doesn't need to block. +// +// The recv() method has a beginning call to pop(), and if successful, it needs +// to decrement the count. It is a crucial implementation detail that this +// decrement does *not* happen to the shared counter. If this were the case, +// then it would be possible for the counter to be very negative when there were +// no receivers waiting, in which case the senders would have to determine when +// it was actually appropriate to wake up a receiver. +// +// Instead, the "steal count" is kept track of separately (not atomically +// because it's only used by ports), and then the decrement() call when +// descheduling will lump in all of the recent steals into one large decrement. +// +// The implication of this is that if a sender sees a -1 count, then there's +// guaranteed to be a waiter waiting! +// +// ## Native Implementation +// +// A major goal of these channels is to work seamlessly on and off the runtime. +// All of the previous race conditions have been worded in terms of +// scheduler-isms (which is obviously not available without the runtime). +// +// For now, native usage of channels (off the runtime) will fall back onto +// mutexes/cond vars for descheduling/atomic decisions. The no-contention path +// is still entirely lock-free, the "deschedule" blocks above are surrounded by +// a mutex and the "wakeup" blocks involve grabbing a mutex and signaling on a +// condition variable. +// +// ## Select +// +// Being able to support selection over channels has greatly influenced this +// design, and not only does selection need to work inside the runtime, but also +// outside the runtime. +// +// The implementation is fairly straightforward. The goal of select() is not to +// return some data, but only to return which channel can receive data without +// blocking. The implementation is essentially the entire blocking procedure +// followed by an increment as soon as its woken up. The cancellation procedure +// involves an increment and swapping out of to_wake to acquire ownership of the +// task to unblock. +// +// Sadly this current implementation requires multiple allocations, so I have +// seen the throughput of select() be much worse than it should be. I do not +// believe that there is anything fundamental which needs to change about these +// channels, however, in order to support a more efficient select(). +// +// # Conclusion +// +// And now that you've seen all the races that I found and attempted to fix, +// here's the code for you to find some more! + +use cast; +use clone::Clone; +use container::Container; +use int; +use iter::Iterator; +use kinds::Send; +use ops::Drop; +use option::{Option, Some, None}; +use rt::thread::Thread; +use unstable::atomics::{AtomicInt, AtomicBool, SeqCst, Relaxed}; +use vec::{ImmutableVector, OwnedVector}; + +use spsc = rt::spsc_queue; +use mpsc = rt::mpsc_queue; + +use self::imp::{TaskHandle, TaskData, BlockingContext}; +pub use self::select::Select; + +macro_rules! test ( + { fn $name:ident() $b:block $($a:attr)*} => ( + mod $name { + #[allow(unused_imports)]; + + use util; + use super::super::*; + use prelude::*; + + fn f() $b + + $($a)* #[test] fn uv() { f() } + $($a)* #[test] + #[ignore(cfg(windows))] // FIXME(#11003) + fn native() { + use unstable::run_in_bare_thread; + run_in_bare_thread(f); + } + } + ) +) + +mod imp; +mod select; + +/////////////////////////////////////////////////////////////////////////////// +// Helper type to abstract ports for channels and shared channels +/////////////////////////////////////////////////////////////////////////////// + +enum Consumer { + SPSC(spsc::Consumer), + MPSC(mpsc::Consumer), +} + +impl Consumer{ + unsafe fn packet(&self) -> *mut Packet { + match *self { + SPSC(ref c) => c.packet(), + MPSC(ref c) => c.packet(), + } + } +} + +/////////////////////////////////////////////////////////////////////////////// +// Public structs +/////////////////////////////////////////////////////////////////////////////// + +/// The receiving-half of Rust's channel type. This half can only be owned by +/// one task +pub struct Port { + priv queue: Consumer, +} + +/// An iterator over messages received on a port, this iterator will block +/// whenever `next` is called, waiting for a new message, and `None` will be +/// returned when the corresponding channel has hung up. +pub struct PortIterator<'a, T> { + priv port: &'a Port +} + +/// The sending-half of Rust's channel type. This half can only be owned by one +/// task +pub struct Chan { + priv queue: spsc::Producer, +} + +/// The sending-half of Rust's channel type. This half can be shared among many +/// tasks by creating copies of itself through the `clone` method. +pub struct SharedChan { + priv queue: mpsc::Producer, +} + +/////////////////////////////////////////////////////////////////////////////// +// Internal struct definitions +/////////////////////////////////////////////////////////////////////////////// + +struct Packet { + cnt: AtomicInt, // How many items are on this channel + steals: int, // How many times has a port received without blocking? + to_wake: Option, // Task to wake up + + data: TaskData, + + // This lock is used to wake up native threads blocked in select. The + // `lock` field is not used because the thread blocking in select must + // block on only one mutex. + //selection_lock: Option>, + + // The number of channels which are currently using this packet. This is + // used to reference count shared channels. + channels: AtomicInt, + + selecting: AtomicBool, + selection_id: uint, + select_next: *mut Packet, + select_prev: *mut Packet, +} + +/////////////////////////////////////////////////////////////////////////////// +// All implementations -- the fun part +/////////////////////////////////////////////////////////////////////////////// + +static DISCONNECTED: int = int::min_value; +static RESCHED_FREQ: int = 200; + +impl Packet { + fn new() -> Packet { + Packet { + cnt: AtomicInt::new(0), + steals: 0, + to_wake: None, + data: TaskData::new(), + channels: AtomicInt::new(1), + + selecting: AtomicBool::new(false), + selection_id: 0, + select_next: 0 as *mut Packet, + select_prev: 0 as *mut Packet, + } + } + + // Increments the channel size count, preserving the disconnected state if + // the other end has disconnected. + fn increment(&mut self) -> int { + match self.cnt.fetch_add(1, SeqCst) { + DISCONNECTED => { + // see the comment in 'try' for a shared channel for why this + // window of "not disconnected" is "ok". + self.cnt.store(DISCONNECTED, SeqCst); + DISCONNECTED + } + n => n + } + } + + // Decrements the reference count of the channel, returning whether the task + // should block or not. This assumes that the task is ready to sleep in that + // the `to_wake` field has already been filled in. Once this decrement + // happens, the task could wake up on the other end. + // + // From an implementation perspective, this is also when our "steal count" + // gets merged into the "channel count". Our steal count is reset to 0 after + // this function completes. + // + // As with increment(), this preserves the disconnected state if the + // channel is disconnected. + fn decrement(&mut self) -> bool { + let steals = self.steals; + self.steals = 0; + match self.cnt.fetch_sub(1 + steals, SeqCst) { + DISCONNECTED => { + self.cnt.store(DISCONNECTED, SeqCst); + false + } + n => { + assert!(n >= 0); + n - steals <= 0 + } + } + } + + // Helper function for select, tests whether this port can receive without + // blocking (obviously not an atomic decision). + fn can_recv(&self) -> bool { + let cnt = self.cnt.load(SeqCst); + cnt == DISCONNECTED || cnt - self.steals > 0 + } + + // This function must have had at least an acquire fence before it to be + // properly called. + fn wakeup(&mut self, can_resched: bool) { + self.to_wake.take_unwrap().wake(can_resched); + self.selecting.store(false, Relaxed); + } + + // Aborts the selection process for a port. This happens as part of select() + // once the task has reawoken. This will place the channel back into a + // consistent state which is ready to be received from again. + // + // The method of doing this is a little subtle. These channels have the + // invariant that if -1 is seen, then to_wake is always Some(..) and should + // be woken up. This aborting process at least needs to add 1 to the + // reference count, but that is not guaranteed to make the count positive + // (our steal count subtraction could mean that after the addition the + // channel count is still negative). + // + // In order to get around this, we force our channel count to go above 0 by + // adding a large number >= 1 to it. This way no sender will see -1 unless + // we are indeed blocking. This "extra lump" we took out of the channel + // becomes our steal count (which will get re-factored into the count on the + // next blocking recv) + // + // The return value of this method is whether there is data on this channel + // to receive or not. + fn abort_selection(&mut self, take_to_wake: bool) -> bool { + // make sure steals + 1 makes the count go non-negative + let steals = { + let cnt = self.cnt.load(SeqCst); + if cnt < 0 && cnt != DISCONNECTED {-cnt} else {0} + }; + let prev = self.cnt.fetch_add(steals + 1, SeqCst); + + // If we were previously disconnected, then we know for sure that there + // is no task in to_wake, so just keep going + if prev == DISCONNECTED { + assert!(self.to_wake.is_none()); + self.cnt.store(DISCONNECTED, SeqCst); + self.selecting.store(false, SeqCst); + true // there is data, that data is that we're disconnected + } else { + let cur = prev + steals + 1; + assert!(cur >= 0); + + // If the previous count was negative, then we just made things go + // positive, hence we passed the -1 boundary and we're responsible + // for removing the to_wake() field and trashing it. + if prev < 0 { + if take_to_wake { + self.to_wake.take_unwrap().trash(); + } else { + assert!(self.to_wake.is_none()); + } + + // We woke ourselves up, we're responsible for cancelling + assert!(self.selecting.load(Relaxed)); + self.selecting.store(false, Relaxed); + } + assert_eq!(self.steals, 0); + self.steals = steals; + + // if we were previously positive, then there's surely data to + // receive + prev >= 0 + } + } + + // Decrement the reference count on a channel. This is called whenever a + // Chan is dropped and may end up waking up a receiver. It's the receiver's + // responsibility on the other end to figure out that we've disconnected. + unsafe fn drop_chan(&mut self) { + match self.channels.fetch_sub(1, SeqCst) { + 1 => { + match self.cnt.swap(DISCONNECTED, SeqCst) { + -1 => { self.wakeup(false); } + DISCONNECTED => {} + n => { assert!(n >= 0); } + } + } + n if n > 1 => {}, + n => fail!("bad number of channels left {}", n), + } + } +} + +impl Drop for Packet { + fn drop(&mut self) { + unsafe { + // Note that this load is not only an assert for correctness about + // disconnection, but also a proper fence before the read of + // `to_wake`, so this assert cannot be removed with also removing + // the `to_wake` assert. + assert_eq!(self.cnt.load(SeqCst), DISCONNECTED); + assert!(self.to_wake.is_none()); + assert_eq!(self.channels.load(SeqCst), 0); + } + } +} + +impl Chan { + /// Creates a new port/channel pair. All data send on the channel returned + /// will become available on the port as well. See the documentation of + /// `Port` and `Chan` to see what's possible with them. + pub fn new() -> (Port, Chan) { + // arbitrary 128 size cache -- this is just a max cache size, not a + // maximum buffer size + let (c, p) = spsc::queue(128, Packet::new()); + let c = SPSC(c); + (Port { queue: c }, Chan { queue: p }) + } + + /// Sends a value along this channel to be received by the corresponding + /// port. + /// + /// Rust channels are infinitely buffered so this method will never block. + /// This method may trigger a rescheduling, however, in order to wake up a + /// blocked receiver (if one is present). If no scheduling is desired, then + /// the `send_deferred` guarantees that there will be no reschedulings. + /// + /// # Failure + /// + /// This function will fail if the other end of the channel has hung up. + /// This means that if the corresponding port has fallen out of scope, this + /// function will trigger a fail message saying that a message is being sent + /// on a closed channel. + /// + /// Note that if this function does *not* fail, it does not mean that the + /// data will be successfully received. All sends are placed into a queue, + /// so it is possible for a send to succeed (the other end is alive), but + /// then the other end could immediately disconnect. + /// + /// The purpose of this functionality is to propagate failure among tasks. + /// If failure is not desired, then consider using the `try_send` method + pub fn send(&self, t: T) { + if !self.try_send(t) { + fail!("sending on a closed channel"); + } + } + + /// This function is equivalent in the semantics of `send`, but it + /// guarantees that a rescheduling will never occur when this method is + /// called. + pub fn send_deferred(&self, t: T) { + if !self.try_send_deferred(t) { + fail!("sending on a closed channel"); + } + } + + /// Attempts to send a value on this channel, returning whether it was + /// successfully sent. + /// + /// A successful send occurs when it is determined that the other end of the + /// channel has not hung up already. An unsuccessful send would be one where + /// the corresponding port has already been deallocated. Note that a return + /// value of `false` means that the data will never be received, but a + /// return value of `true` does *not* mean that the data will be received. + /// It is possible for the corresponding port to hang up immediately after + /// this function returns `true`. + /// + /// Like `send`, this method will never block. If the failure of send cannot + /// be tolerated, then this method should be used instead. + pub fn try_send(&self, t: T) -> bool { self.try(t, true) } + + /// This function is equivalent in the semantics of `try_send`, but it + /// guarantees that a rescheduling will never occur when this method is + /// called. + pub fn try_send_deferred(&self, t: T) -> bool { self.try(t, false) } + + fn try(&self, t: T, can_resched: bool) -> bool { + unsafe { + let this = cast::transmute_mut(self); + this.queue.push(t); + let packet = this.queue.packet(); + match (*packet).increment() { + // As described above, -1 == wakeup + -1 => { (*packet).wakeup(can_resched); true } + // Also as above, SPSC queues must be >= -2 + -2 => true, + // We succeeded if we sent data + DISCONNECTED => this.queue.is_empty(), + // In order to prevent starvation of other tasks in situations + // where a task sends repeatedly without ever receiving, we + // occassionally yield instead of doing a send immediately. + // Only doing this if we're doing a rescheduling send, otherwise + // the caller is expecting not to context switch. + // + // Note that we don't unconditionally attempt to yield because + // the TLS overhead can be a bit much. + n => { + assert!(n >= 0); + if can_resched && n > 0 && n % RESCHED_FREQ == 0 { + imp::maybe_yield(); + } + true + } + } + } + } +} + +#[unsafe_destructor] +impl Drop for Chan { + fn drop(&mut self) { + unsafe { (*self.queue.packet()).drop_chan(); } + } +} + +impl SharedChan { + /// Creates a new shared channel and port pair. The purpose of a shared + /// channel is to be cloneable such that many tasks can send data at the + /// same time. All data sent on any channel will become available on the + /// provided port as well. + pub fn new() -> (Port, SharedChan) { + let (c, p) = mpsc::queue(Packet::new()); + let c = MPSC(c); + (Port { queue: c }, SharedChan { queue: p }) + } + + /// Equivalent method to `send` on the `Chan` type (using the same + /// semantics) + pub fn send(&self, t: T) { + if !self.try_send(t) { + fail!("sending on a closed channel"); + } + } + + /// This function is equivalent in the semantics of `send`, but it + /// guarantees that a rescheduling will never occur when this method is + /// called. + pub fn send_deferred(&self, t: T) { + if !self.try_send_deferred(t) { + fail!("sending on a closed channel"); + } + } + + /// Equivalent method to `try_send` on the `Chan` type (using the same + /// semantics) + pub fn try_send(&self, t: T) -> bool { self.try(t, true) } + + /// This function is equivalent in the semantics of `try_send`, but it + /// guarantees that a rescheduling will never occur when this method is + /// called. + pub fn try_send_deferred(&self, t: T) -> bool { self.try(t, false) } + + fn try(&self, t: T, can_resched: bool) -> bool { + unsafe { + // Note that the multiple sender case is a little tricker + // semantically than the single sender case. The logic for + // incrementing is "add and if disconnected store disconnected". + // This could end up leading some senders to believe that there + // wasn't a disconnect if in fact there was a disconnect. This means + // that while one thread is attempting to re-store the disconnected + // states, other threads could walk through merrily incrementing + // this very-negative disconnected count. To prevent senders from + // spuriously attempting to send when the channels is actually + // disconnected, the count has a ranged check here. + // + // This is also done for another reason. Remember that the return + // value of this function is: + // + // `true` == the data *may* be received, this essentially has no + // meaning + // `false` == the data will *never* be received, this has a lot of + // meaning + // + // In the SPSC case, we have a check of 'queue.is_empty()' to see + // whether the data was actually received, but this same condition + // means nothing in a multi-producer context. As a result, this + // preflight check serves as the definitive "this will never be + // received". Once we get beyond this check, we have permanently + // entered the realm of "this may be received" + let packet = self.queue.packet(); + if (*packet).cnt.load(Relaxed) < DISCONNECTED + 1024 { + return false + } + + let this = cast::transmute_mut(self); + this.queue.push(t); + + match (*packet).increment() { + DISCONNECTED => {} // oh well, we tried + -1 => { (*packet).wakeup(can_resched); } + n => { + if can_resched && n > 0 && n % RESCHED_FREQ == 0 { + imp::maybe_yield(); + } + } + } + true + } + } +} + +impl Clone for SharedChan { + fn clone(&self) -> SharedChan { + unsafe { (*self.queue.packet()).channels.fetch_add(1, SeqCst); } + SharedChan { queue: self.queue.clone() } + } +} + +#[unsafe_destructor] +impl Drop for SharedChan { + fn drop(&mut self) { + unsafe { (*self.queue.packet()).drop_chan(); } + } +} + +impl Port { + /// Blocks waiting for a value on this port + /// + /// This function will block if necessary to wait for a corresponding send + /// on the channel from its paired `Chan` structure. This port will be woken + /// up when data is ready, and the data will be returned. + /// + /// # Failure + /// + /// Similar to channels, this method will trigger a task failure if the + /// other end of the channel has hung up (been deallocated). The purpose of + /// this is to propagate failure among tasks. + /// + /// If failure is not desired, then there are two options: + /// + /// * If blocking is still desired, the `recv_opt` method will return `None` + /// when the other end hangs up + /// + /// * If blocking is not desired, then the `try_recv` method will attempt to + /// peek at a value on this port. + pub fn recv(&self) -> T { + match self.recv_opt() { + Some(t) => t, + None => fail!("receiving on a closed channel"), + } + } + + /// Attempts to return a pending value on this port without blocking + /// + /// This method will never block the caller in order to wait for data to + /// become available. Instead, this will always return immediately with a + /// possible option of pending data on the channel. + /// + /// This is useful for a flavor of "optimistic check" before deciding to + /// block on a port. + /// + /// This function cannot fail. + pub fn try_recv(&self) -> Option { + self.try_recv_inc(true) + } + + fn try_recv_inc(&self, increment: bool) -> Option { + // This is a "best effort" situation, so if a queue is inconsistent just + // don't worry about it. + let this = unsafe { cast::transmute_mut(self) }; + let ret = match this.queue { + SPSC(ref mut queue) => queue.pop(), + MPSC(ref mut queue) => match queue.pop() { + mpsc::Data(t) => Some(t), + mpsc::Empty => None, + + // This is a bit of an interesting case. The channel is + // reported as having data available, but our pop() has + // failed due to the queue being in an inconsistent state. + // This means that there is some pusher somewhere which has + // yet to complete, but we are guaranteed that a pop will + // eventually succeed. In this case, we spin in a yield loop + // because the remote sender should finish their enqueue + // operation "very quickly". + // + // Note that this yield loop does *not* attempt to do a green + // yield (regardless of the context), but *always* performs an + // OS-thread yield. The reasoning for this is that the pusher in + // question which is causing the inconsistent state is + // guaranteed to *not* be a blocked task (green tasks can't get + // pre-empted), so it must be on a different OS thread. Also, + // `try_recv` is normally a "guaranteed no rescheduling" context + // in a green-thread situation. By yielding control of the + // thread, we will hopefully allow time for the remote task on + // the other OS thread to make progress. + // + // Avoiding this yield loop would require a different queue + // abstraction which provides the guarantee that after M + // pushes have succeeded, at least M pops will succeed. The + // current queues guarantee that if there are N active + // pushes, you can pop N times once all N have finished. + mpsc::Inconsistent => { + let data; + loop { + Thread::yield_now(); + match queue.pop() { + mpsc::Data(t) => { data = t; break } + mpsc::Empty => fail!("inconsistent => empty"), + mpsc::Inconsistent => {} + } + } + Some(data) + } + } + }; + if increment && ret.is_some() { + unsafe { (*this.queue.packet()).steals += 1; } + } + return ret; + } + + /// Attempt to wait for a value on this port, but does not fail if the + /// corresponding channel has hung up. + /// + /// This implementation of iterators for ports will always block if there is + /// not data available on the port, but it will not fail in the case that + /// the channel has been deallocated. + /// + /// In other words, this function has the same semantics as the `recv` + /// method except for the failure aspect. + /// + /// If the channel has hung up, then `None` is returned. Otherwise `Some` of + /// the value found on the port is returned. + pub fn recv_opt(&self) -> Option { + // optimistic preflight check (scheduling is expensive) + match self.try_recv() { None => {}, data => return data } + + let packet; + let this; + unsafe { + this = cast::transmute_mut(self); + packet = this.queue.packet(); + BlockingContext::one(&mut (*packet).data, |ctx, data| { + ctx.block(data, &mut (*packet).to_wake, || (*packet).decrement()) + }); + } + + let data = self.try_recv_inc(false); + if data.is_none() && + unsafe { (*packet).cnt.load(SeqCst) } != DISCONNECTED { + fail!("bug: woke up too soon"); + } + return data; + } + + /// Returns an iterator which will block waiting for messages, but never + /// `fail!`. It will return `None` when the channel has hung up. + pub fn iter<'a>(&'a self) -> PortIterator<'a, T> { + PortIterator { port: self } + } +} + +impl<'a, T: Send> Iterator for PortIterator<'a, T> { + fn next(&mut self) -> Option { self.port.recv_opt() } +} + +#[unsafe_destructor] +impl Drop for Port { + fn drop(&mut self) { + // All we need to do is store that we're disconnected. If the channel + // half has already disconnected, then we'll just deallocate everything + // when the shared packet is deallocated. + unsafe { + (*self.queue.packet()).cnt.store(DISCONNECTED, SeqCst); + } + } +} + +#[cfg(test)] +mod test { + use prelude::*; + + use task; + use rt::thread::Thread; + use super::*; + use rt::test::*; + + test!(fn smoke() { + let (p, c) = Chan::new(); + c.send(1); + assert_eq!(p.recv(), 1); + }) + + test!(fn drop_full() { + let (_p, c) = Chan::new(); + c.send(~1); + }) + + test!(fn drop_full_shared() { + let (_p, c) = SharedChan::new(); + c.send(~1); + }) + + test!(fn smoke_shared() { + let (p, c) = SharedChan::new(); + c.send(1); + assert_eq!(p.recv(), 1); + let c = c.clone(); + c.send(1); + assert_eq!(p.recv(), 1); + }) + + #[test] + fn smoke_threads() { + let (p, c) = Chan::new(); + do task::spawn_sched(task::SingleThreaded) { + c.send(1); + } + assert_eq!(p.recv(), 1); + } + + #[test] #[should_fail] + fn smoke_port_gone() { + let (p, c) = Chan::new(); + drop(p); + c.send(1); + } + + #[test] #[should_fail] + fn smoke_shared_port_gone() { + let (p, c) = SharedChan::new(); + drop(p); + c.send(1); + } + + #[test] #[should_fail] + fn smoke_shared_port_gone2() { + let (p, c) = SharedChan::new(); + drop(p); + let c2 = c.clone(); + drop(c); + c2.send(1); + } + + #[test] #[should_fail] + fn port_gone_concurrent() { + let (p, c) = Chan::new(); + do task::spawn_sched(task::SingleThreaded) { + p.recv(); + } + loop { c.send(1) } + } + + #[test] #[should_fail] + fn port_gone_concurrent_shared() { + let (p, c) = SharedChan::new(); + let c1 = c.clone(); + do task::spawn_sched(task::SingleThreaded) { + p.recv(); + } + loop { + c.send(1); + c1.send(1); + } + } + + #[test] #[should_fail] + fn smoke_chan_gone() { + let (p, c) = Chan::::new(); + drop(c); + p.recv(); + } + + #[test] #[should_fail] + fn smoke_chan_gone_shared() { + let (p, c) = SharedChan::<()>::new(); + let c2 = c.clone(); + drop(c); + drop(c2); + p.recv(); + } + + #[test] #[should_fail] + fn chan_gone_concurrent() { + let (p, c) = Chan::new(); + do task::spawn_sched(task::SingleThreaded) { + c.send(1); + c.send(1); + } + loop { p.recv(); } + } + + #[test] + fn stress() { + let (p, c) = Chan::new(); + do task::spawn_sched(task::SingleThreaded) { + for _ in range(0, 10000) { c.send(1); } + } + for _ in range(0, 10000) { + assert_eq!(p.recv(), 1); + } + } + + #[test] + fn stress_shared() { + static AMT: uint = 10000; + static NTHREADS: uint = 8; + let (p, c) = SharedChan::::new(); + let (p1, c1) = Chan::new(); + + do spawn { + for _ in range(0, AMT * NTHREADS) { + assert_eq!(p.recv(), 1); + } + assert_eq!(p.try_recv(), None); + c1.send(()); + } + + for _ in range(0, NTHREADS) { + let c = c.clone(); + do task::spawn_sched(task::SingleThreaded) { + for _ in range(0, AMT) { c.send(1); } + } + } + p1.recv(); + + } + + #[test] + #[ignore(cfg(windows))] // FIXME(#11003) + fn send_from_outside_runtime() { + let (p, c) = Chan::::new(); + let (p1, c1) = Chan::new(); + do spawn { + c1.send(()); + for _ in range(0, 40) { + assert_eq!(p.recv(), 1); + } + } + p1.recv(); + let t = do Thread::start { + for _ in range(0, 40) { + c.send(1); + } + }; + t.join(); + } + + #[test] + #[ignore(cfg(windows))] // FIXME(#11003) + fn recv_from_outside_runtime() { + let (p, c) = Chan::::new(); + let t = do Thread::start { + for _ in range(0, 40) { + assert_eq!(p.recv(), 1); + } + }; + for _ in range(0, 40) { + c.send(1); + } + t.join(); + } + + #[test] + #[ignore(cfg(windows))] // FIXME(#11003) + fn no_runtime() { + let (p1, c1) = Chan::::new(); + let (p2, c2) = Chan::::new(); + let t1 = do Thread::start { + assert_eq!(p1.recv(), 1); + c2.send(2); + }; + let t2 = do Thread::start { + c1.send(1); + assert_eq!(p2.recv(), 2); + }; + t1.join(); + t2.join(); + } + + #[test] + fn oneshot_single_thread_close_port_first() { + // Simple test of closing without sending + do run_in_newsched_task { + let (port, _chan) = Chan::::new(); + { let _p = port; } + } + } + + #[test] + fn oneshot_single_thread_close_chan_first() { + // Simple test of closing without sending + do run_in_newsched_task { + let (_port, chan) = Chan::::new(); + { let _c = chan; } + } + } + + #[test] #[should_fail] + fn oneshot_single_thread_send_port_close() { + // Testing that the sender cleans up the payload if receiver is closed + let (port, chan) = Chan::<~int>::new(); + { let _p = port; } + chan.send(~0); + } + + #[test] + fn oneshot_single_thread_recv_chan_close() { + // Receiving on a closed chan will fail + do run_in_newsched_task { + let res = do spawntask_try { + let (port, chan) = Chan::<~int>::new(); + { let _c = chan; } + port.recv(); + }; + // What is our res? + assert!(res.is_err()); + } + } + + #[test] + fn oneshot_single_thread_send_then_recv() { + do run_in_newsched_task { + let (port, chan) = Chan::<~int>::new(); + chan.send(~10); + assert!(port.recv() == ~10); + } + } + + #[test] + fn oneshot_single_thread_try_send_open() { + do run_in_newsched_task { + let (port, chan) = Chan::::new(); + assert!(chan.try_send(10)); + assert!(port.recv() == 10); + } + } + + #[test] + fn oneshot_single_thread_try_send_closed() { + do run_in_newsched_task { + let (port, chan) = Chan::::new(); + { let _p = port; } + assert!(!chan.try_send(10)); + } + } + + #[test] + fn oneshot_single_thread_try_recv_open() { + do run_in_newsched_task { + let (port, chan) = Chan::::new(); + chan.send(10); + assert!(port.try_recv() == Some(10)); + } + } + + #[test] + fn oneshot_single_thread_try_recv_closed() { + do run_in_newsched_task { + let (port, chan) = Chan::::new(); + { let _c = chan; } + assert!(port.recv_opt() == None); + } + } + + #[test] + fn oneshot_single_thread_peek_data() { + do run_in_newsched_task { + let (port, chan) = Chan::::new(); + assert!(port.try_recv().is_none()); + chan.send(10); + assert!(port.try_recv().is_some()); + } + } + + #[test] + fn oneshot_single_thread_peek_close() { + do run_in_newsched_task { + let (port, chan) = Chan::::new(); + { let _c = chan; } + assert!(port.try_recv().is_none()); + assert!(port.try_recv().is_none()); + } + } + + #[test] + fn oneshot_single_thread_peek_open() { + do run_in_newsched_task { + let (port, _) = Chan::::new(); + assert!(port.try_recv().is_none()); + } + } + + #[test] + fn oneshot_multi_task_recv_then_send() { + do run_in_newsched_task { + let (port, chan) = Chan::<~int>::new(); + do spawntask { + assert!(port.recv() == ~10); + } + + chan.send(~10); + } + } + + #[test] + fn oneshot_multi_task_recv_then_close() { + do run_in_newsched_task { + let (port, chan) = Chan::<~int>::new(); + do spawntask_later { + let _chan = chan; + } + let res = do spawntask_try { + assert!(port.recv() == ~10); + }; + assert!(res.is_err()); + } + } + + #[test] + fn oneshot_multi_thread_close_stress() { + stress_factor().times(|| { + do run_in_newsched_task { + let (port, chan) = Chan::::new(); + let thread = do spawntask_thread { + let _p = port; + }; + let _chan = chan; + thread.join(); + } + }) + } + + #[test] + fn oneshot_multi_thread_send_close_stress() { + stress_factor().times(|| { + let (port, chan) = Chan::::new(); + do spawn { + let _p = port; + } + do task::try { + chan.send(1); + }; + }) + } + + #[test] + fn oneshot_multi_thread_recv_close_stress() { + stress_factor().times(|| { + let (port, chan) = Chan::::new(); + do spawn { + let port = port; + let res = do task::try { + port.recv(); + }; + assert!(res.is_err()); + }; + do spawn { + let chan = chan; + do spawn { + let _chan = chan; + } + }; + }) + } + + #[test] + fn oneshot_multi_thread_send_recv_stress() { + stress_factor().times(|| { + let (port, chan) = Chan::<~int>::new(); + do spawn { + chan.send(~10); + } + do spawn { + assert!(port.recv() == ~10); + } + }) + } + + #[test] + fn stream_send_recv_stress() { + stress_factor().times(|| { + let (port, chan) = Chan::<~int>::new(); + + send(chan, 0); + recv(port, 0); + + fn send(chan: Chan<~int>, i: int) { + if i == 10 { return } + + do spawntask_random { + chan.send(~i); + send(chan, i + 1); + } + } + + fn recv(port: Port<~int>, i: int) { + if i == 10 { return } + + do spawntask_random { + assert!(port.recv() == ~i); + recv(port, i + 1); + }; + } + }) + } + + #[test] + fn recv_a_lot() { + // Regression test that we don't run out of stack in scheduler context + do run_in_newsched_task { + let (port, chan) = Chan::new(); + 10000.times(|| { chan.send(()) }); + 10000.times(|| { port.recv() }); + } + } + + #[test] + fn shared_chan_stress() { + do run_in_mt_newsched_task { + let (port, chan) = SharedChan::new(); + let total = stress_factor() + 100; + total.times(|| { + let chan_clone = chan.clone(); + do spawntask_random { + chan_clone.send(()); + } + }); + + total.times(|| { + port.recv(); + }); + } + } + + #[test] + fn test_nested_recv_iter() { + let (port, chan) = Chan::::new(); + let (total_port, total_chan) = Chan::::new(); + + do spawn { + let mut acc = 0; + for x in port.iter() { + acc += x; + } + total_chan.send(acc); + } + + chan.send(3); + chan.send(1); + chan.send(2); + drop(chan); + assert_eq!(total_port.recv(), 6); + } + + #[test] + fn test_recv_iter_break() { + let (port, chan) = Chan::::new(); + let (count_port, count_chan) = Chan::::new(); + + do spawn { + let mut count = 0; + for x in port.iter() { + if count >= 3 { + break; + } else { + count += x; + } + } + count_chan.send(count); + } + + chan.send(2); + chan.send(2); + chan.send(2); + chan.try_send(2); + drop(chan); + assert_eq!(count_port.recv(), 4); + } +} diff --git a/src/libstd/comm/select.rs b/src/libstd/comm/select.rs new file mode 100644 index 0000000000000..4d6b540f2a5cb --- /dev/null +++ b/src/libstd/comm/select.rs @@ -0,0 +1,503 @@ +// Copyright 2013 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +//! Selection over an array of ports +//! +//! This module contains the implementation machinery necessary for selecting +//! over a number of ports. One large goal of this module is to provide an +//! efficient interface to selecting over any port of any type. +//! +//! This is achieved through an architecture of a "port set" in which ports are +//! added to a set and then the entire set is waited on at once. The set can be +//! waited on multiple times to prevent re-adding each port to the set. +//! +//! Usage of this module is currently encouraged to go through the use of the +//! `select!` macro. This macro allows naturally binding of variables to the +//! received values of ports in a much more natural syntax then usage of the +//! `Select` structure directly. +//! +//! # Example +//! +//! ```rust +//! let (mut p1, c1) = Chan::new(); +//! let (mut p2, c2) = Chan::new(); +//! +//! c1.send(1); +//! c2.send(2); +//! +//! select! ( +//! val = p1.recv() => { +//! assert_eq!(val, 1); +//! } +//! val = p2.recv() => { +//! assert_eq!(val, 2); +//! } +//! ) + +#[allow(dead_code)]; + +use cast; +use iter::Iterator; +use kinds::Send; +use ops::Drop; +use option::{Some, None, Option}; +use ptr::RawPtr; +use super::imp::BlockingContext; +use super::{Packet, Port, imp}; +use uint; +use unstable::atomics::{Relaxed, SeqCst}; + +macro_rules! select { + ( + $name1:pat = $port1:ident.$meth1:ident() => $code1:expr, + $($name:pat = $port:ident.$meth:ident() => $code:expr),* + ) => ({ + use std::comm::Select; + let sel = Select::new(); + let mut $port1 = sel.add(&mut $port1); + $( let mut $port = sel.add(&mut $port); )* + let ret = sel.wait(); + if ret == $port1.id { let $name1 = $port1.$meth1(); $code1 } + $( else if ret == $port.id { let $name = $port.$meth(); $code } )* + else { unreachable!() } + }) +} + +/// The "port set" of the select interface. This structure is used to manage a +/// set of ports which are being selected over. +#[no_freeze] +#[no_send] +pub struct Select { + priv head: *mut Packet, + priv tail: *mut Packet, + priv next_id: uint, +} + +/// A handle to a port which is currently a member of a `Select` set of ports. +/// This handle is used to keep the port in the set as well as interact with the +/// underlying port. +pub struct Handle<'port, T> { + id: uint, + priv selector: &'port Select, + priv port: &'port mut Port, +} + +struct PacketIterator { priv cur: *mut Packet } + +impl Select { + /// Creates a new selection structure. This set is initially empty and + /// `wait` will fail!() if called. + /// + /// Usage of this struct directly can sometimes be burdensome, and usage is + /// rather much easier through the `select!` macro. + pub fn new() -> Select { + Select { + head: 0 as *mut Packet, + tail: 0 as *mut Packet, + next_id: 1, + } + } + + /// Adds a new port to this set, returning a handle which is then used to + /// receive on the port. + /// + /// Note that this port parameter takes `&mut Port` instead of `&Port`. None + /// of the methods of receiving on a port require `&mut self`, but `&mut` is + /// used here in order to have the compiler guarantee that the same port is + /// not added to this set more than once. + /// + /// When the returned handle falls out of scope, the port will be removed + /// from this set. While the handle is in this set, usage of the port can be + /// done through the `Handle`'s receiving methods. + pub fn add<'a, T: Send>(&'a self, port: &'a mut Port) -> Handle<'a, T> { + let this = unsafe { cast::transmute_mut(self) }; + let id = this.next_id; + this.next_id += 1; + unsafe { + let packet = port.queue.packet(); + assert!(!(*packet).selecting.load(Relaxed)); + assert_eq!((*packet).selection_id, 0); + (*packet).selection_id = id; + if this.head.is_null() { + this.head = packet; + this.tail = packet; + } else { + (*packet).select_prev = this.tail; + assert!((*packet).select_next.is_null()); + (*this.tail).select_next = packet; + this.tail = packet; + } + } + Handle { id: id, selector: this, port: port } + } + + /// Waits for an event on this port set. The returned valus is *not* and + /// index, but rather an id. This id can be queried against any active + /// `Handle` structures (each one has a public `id` field). The handle with + /// the matching `id` will have some sort of event available on it. The + /// event could either be that data is available or the corresponding + /// channel has been closed. + pub fn wait(&self) -> uint { + // Note that this is currently an inefficient implementation. We in + // theory have knowledge about all ports in the set ahead of time, so + // this method shouldn't really have to iterate over all of them yet + // again. The idea with this "port set" interface is to get the + // interface right this time around, and later this implementation can + // be optimized. + // + // This implementation can be summarized by: + // + // fn select(ports) { + // if any port ready { return ready index } + // deschedule { + // block on all ports + // } + // unblock on all ports + // return ready index + // } + // + // Most notably, the iterations over all of the ports shouldn't be + // necessary. + unsafe { + let mut amt = 0; + for p in self.iter() { + assert!(!(*p).selecting.load(Relaxed)); + amt += 1; + if (*p).can_recv() { + return (*p).selection_id; + } + } + assert!(amt > 0); + + let mut ready_index = amt; + let mut ready_id = uint::max_value; + let mut iter = self.iter().enumerate(); + + // Acquire a number of blocking contexts, and block on each one + // sequentially until one fails. If one fails, then abort + // immediately so we can go unblock on all the other ports. + BlockingContext::many(amt, |ctx| { + let (i, packet) = iter.next().unwrap(); + (*packet).selecting.store(true, SeqCst); + if !ctx.block(&mut (*packet).data, + &mut (*packet).to_wake, + || (*packet).decrement()) { + (*packet).abort_selection(false); + (*packet).selecting.store(false, SeqCst); + ready_index = i; + ready_id = (*packet).selection_id; + false + } else { + true + } + }); + + // Abort the selection process on each port. If the abort process + // returns `true`, then that means that the port is ready to receive + // some data. Note that this also means that the port may have yet + // to have fully read the `to_wake` field and woken us up (although + // the wakeup is guaranteed to fail). + // + // This situation happens in the window of where a sender invokes + // increment(), sees -1, and then decides to wake up the task. After + // all this is done, the sending thread will set `selecting` to + // `false`. Until this is done, we cannot return. If we were to + // return, then a sender could wake up a port which has gone back to + // sleep after this call to `select`. + // + // Note that it is a "fairly small window" in which an increment() + // views that it should wake a thread up until the `selecting` bit + // is set to false. For now, the implementation currently just spins + // in a yield loop. This is very distasteful, but this + // implementation is already nowhere near what it should ideally be. + // A rewrite should focus on avoiding a yield loop, and for now this + // implementation is tying us over to a more efficient "don't + // iterate over everything every time" implementation. + for packet in self.iter().take(ready_index) { + if (*packet).abort_selection(true) { + ready_id = (*packet).selection_id; + while (*packet).selecting.load(Relaxed) { + imp::yield_now(); + } + } + } + + // Sanity check for now to make sure that everyone is turned off. + for packet in self.iter() { + assert!(!(*packet).selecting.load(Relaxed)); + } + + assert!(ready_id != uint::max_value); + return ready_id; + } + } + + unsafe fn remove(&self, packet: *mut Packet) { + let this = cast::transmute_mut(self); + assert!(!(*packet).selecting.load(Relaxed)); + if (*packet).select_prev.is_null() { + assert_eq!(packet, this.head); + this.head = (*packet).select_next; + } else { + (*(*packet).select_prev).select_next = (*packet).select_next; + } + if (*packet).select_next.is_null() { + assert_eq!(packet, this.tail); + this.tail = (*packet).select_prev; + } else { + (*(*packet).select_next).select_prev = (*packet).select_prev; + } + (*packet).select_next = 0 as *mut Packet; + (*packet).select_prev = 0 as *mut Packet; + (*packet).selection_id = 0; + } + + fn iter(&self) -> PacketIterator { PacketIterator { cur: self.head } } +} + +impl<'port, T: Send> Handle<'port, T> { + /// Receive a value on the underlying port. Has the same semantics as + /// `Port.recv` + pub fn recv(&mut self) -> T { self.port.recv() } + /// Block to receive a value on the underlying port, returning `Some` on + /// success or `None` if the channel disconnects. This function has the same + /// semantics as `Port.recv_opt` + pub fn recv_opt(&mut self) -> Option { self.port.recv_opt() } + /// Immediately attempt to receive a value on a port, this function will + /// never block. Has the same semantics as `Port.try_recv`. + pub fn try_recv(&mut self) -> Option { self.port.try_recv() } +} + +#[unsafe_destructor] +impl Drop for Select { + fn drop(&mut self) { + assert!(self.head.is_null()); + assert!(self.tail.is_null()); + } +} + +#[unsafe_destructor] +impl<'port, T: Send> Drop for Handle<'port, T> { + fn drop(&mut self) { + unsafe { self.selector.remove(self.port.queue.packet()) } + } +} + +impl Iterator<*mut Packet> for PacketIterator { + fn next(&mut self) -> Option<*mut Packet> { + if self.cur.is_null() { + None + } else { + let ret = Some(self.cur); + unsafe { self.cur = (*self.cur).select_next; } + ret + } + } +} + +#[cfg(test)] +mod test { + use super::super::*; + use prelude::*; + + test!(fn smoke() { + let (mut p1, c1) = Chan::::new(); + let (mut p2, c2) = Chan::::new(); + c1.send(1); + select! ( + foo = p1.recv() => { assert_eq!(foo, 1); }, + _bar = p2.recv() => { fail!() } + ) + c2.send(2); + select! ( + _foo = p1.recv() => { fail!() }, + bar = p2.recv() => { assert_eq!(bar, 2) } + ) + drop(c1); + select! ( + foo = p1.recv_opt() => { assert_eq!(foo, None); }, + _bar = p2.recv() => { fail!() } + ) + drop(c2); + select! ( + bar = p2.recv_opt() => { assert_eq!(bar, None); }, + ) + }) + + test!(fn smoke2() { + let (mut p1, _c1) = Chan::::new(); + let (mut p2, _c2) = Chan::::new(); + let (mut p3, _c3) = Chan::::new(); + let (mut p4, _c4) = Chan::::new(); + let (mut p5, c5) = Chan::::new(); + c5.send(4); + select! ( + _foo = p1.recv() => { fail!("1") }, + _foo = p2.recv() => { fail!("2") }, + _foo = p3.recv() => { fail!("3") }, + _foo = p4.recv() => { fail!("4") }, + foo = p5.recv() => { assert_eq!(foo, 4); } + ) + }) + + test!(fn closed() { + let (mut p1, _c1) = Chan::::new(); + let (mut p2, c2) = Chan::::new(); + drop(c2); + + select! ( + _a1 = p1.recv_opt() => { fail!() }, + a2 = p2.recv_opt() => { assert_eq!(a2, None); } + ) + }) + + #[test] + fn unblocks() { + use std::io::timer; + + let (mut p1, c1) = Chan::::new(); + let (mut p2, _c2) = Chan::::new(); + let (p3, c3) = Chan::::new(); + + do spawn { + timer::sleep(3); + c1.send(1); + p3.recv(); + timer::sleep(3); + } + + select! ( + a = p1.recv() => { assert_eq!(a, 1); }, + _b = p2.recv() => { fail!() } + ) + c3.send(1); + select! ( + a = p1.recv_opt() => { assert_eq!(a, None); }, + _b = p2.recv() => { fail!() } + ) + } + + #[test] + fn both_ready() { + use std::io::timer; + + let (mut p1, c1) = Chan::::new(); + let (mut p2, c2) = Chan::::new(); + let (p3, c3) = Chan::<()>::new(); + + do spawn { + timer::sleep(3); + c1.send(1); + c2.send(2); + p3.recv(); + } + + select! ( + a = p1.recv() => { assert_eq!(a, 1); }, + a = p2.recv() => { assert_eq!(a, 2); } + ) + select! ( + a = p1.recv() => { assert_eq!(a, 1); }, + a = p2.recv() => { assert_eq!(a, 2); } + ) + c3.send(()); + } + + #[test] + fn stress() { + static AMT: int = 10000; + let (mut p1, c1) = Chan::::new(); + let (mut p2, c2) = Chan::::new(); + let (p3, c3) = Chan::<()>::new(); + + do spawn { + for i in range(0, AMT) { + if i % 2 == 0 { + c1.send(i); + } else { + c2.send(i); + } + p3.recv(); + } + } + + for i in range(0, AMT) { + select! ( + i1 = p1.recv() => { assert!(i % 2 == 0 && i == i1); }, + i2 = p2.recv() => { assert!(i % 2 == 1 && i == i2); } + ) + c3.send(()); + } + } + + #[test] + #[ignore(cfg(windows))] // FIXME(#11003) + fn stress_native() { + use std::rt::thread::Thread; + use std::unstable::run_in_bare_thread; + static AMT: int = 10000; + + do run_in_bare_thread { + let (mut p1, c1) = Chan::::new(); + let (mut p2, c2) = Chan::::new(); + let (p3, c3) = Chan::<()>::new(); + + let t = do Thread::start { + for i in range(0, AMT) { + if i % 2 == 0 { + c1.send(i); + } else { + c2.send(i); + } + p3.recv(); + } + }; + + for i in range(0, AMT) { + select! ( + i1 = p1.recv() => { assert!(i % 2 == 0 && i == i1); }, + i2 = p2.recv() => { assert!(i % 2 == 1 && i == i2); } + ) + c3.send(()); + } + t.join(); + } + } + + #[test] + #[ignore(cfg(windows))] // FIXME(#11003) + fn native_both_ready() { + use std::rt::thread::Thread; + use std::unstable::run_in_bare_thread; + + do run_in_bare_thread { + let (mut p1, c1) = Chan::::new(); + let (mut p2, c2) = Chan::::new(); + let (p3, c3) = Chan::<()>::new(); + + let t = do Thread::start { + c1.send(1); + c2.send(2); + p3.recv(); + }; + + select! ( + a = p1.recv() => { assert_eq!(a, 1); }, + b = p2.recv() => { assert_eq!(b, 2); } + ) + select! ( + a = p1.recv() => { assert_eq!(a, 1); }, + b = p2.recv() => { assert_eq!(b, 2); } + ) + c3.send(()); + t.join(); + } + } +} diff --git a/src/libstd/io/comm_adapters.rs b/src/libstd/io/comm_adapters.rs index b3e5a9a0c86f1..7f94af8307eae 100644 --- a/src/libstd/io/comm_adapters.rs +++ b/src/libstd/io/comm_adapters.rs @@ -10,7 +10,7 @@ use prelude::*; -use comm::{GenericPort, GenericChan, GenericSmartChan}; +use comm::{Port, Chan}; use cmp; use io; use option::{None, Option, Some}; @@ -30,15 +30,15 @@ use vec::{bytes, CopyableVector, MutableVector, ImmutableVector}; /// None => println!("At the end of the stream!") /// } /// ``` -pub struct PortReader

{ +pub struct PortReader { priv buf: Option<~[u8]>, // A buffer of bytes received but not consumed. priv pos: uint, // How many of the buffered bytes have already be consumed. - priv port: P, // The port to pull data from. + priv port: Port<~[u8]>, // The port to pull data from. priv closed: bool, // Whether the pipe this port connects to has been closed. } -impl> PortReader

{ - pub fn new(port: P) -> PortReader

{ +impl PortReader { + pub fn new(port: Port<~[u8]>) -> PortReader

{ PortReader { buf: None, pos: 0, @@ -48,7 +48,7 @@ impl> PortReader

{ } } -impl> Reader for PortReader

{ +impl Reader for PortReader { fn read(&mut self, buf: &mut [u8]) -> Option { let mut num_read = 0; loop { @@ -67,7 +67,7 @@ impl> Reader for PortReader

{ break; } self.pos = 0; - self.buf = self.port.try_recv(); + self.buf = self.port.recv_opt(); self.closed = self.buf.is_none(); } if self.closed && num_read == 0 { @@ -89,17 +89,17 @@ impl> Reader for PortReader

{ /// let writer = ChanWriter::new(chan); /// writer.write("hello, world".as_bytes()); /// ``` -pub struct ChanWriter { - chan: C, +pub struct ChanWriter { + chan: Chan<~[u8]>, } -impl> ChanWriter { +impl ChanWriter { pub fn new(chan: C) -> ChanWriter { ChanWriter { chan: chan } } } -impl> Writer for ChanWriter { +impl Writer for ChanWriter { fn write(&mut self, buf: &[u8]) { if !self.chan.try_send(buf.to_owned()) { io::io_error::cond.raise(io::IoError { @@ -111,28 +111,6 @@ impl> Writer for ChanWriter { } } -pub struct ReaderPort; - -impl ReaderPort { - pub fn new(_reader: R) -> ReaderPort { fail!() } -} - -impl GenericPort<~[u8]> for ReaderPort { - fn recv(&self) -> ~[u8] { fail!() } - - fn try_recv(&self) -> Option<~[u8]> { fail!() } -} - -pub struct WriterChan; - -impl WriterChan { - pub fn new(_writer: W) -> WriterChan { fail!() } -} - -impl GenericChan<~[u8]> for WriterChan { - fn send(&self, _x: ~[u8]) { fail!() } -} - #[cfg(test)] mod test { @@ -144,7 +122,7 @@ mod test { #[test] fn test_port_reader() { - let (port, chan) = comm::stream(); + let (port, chan) = Chan::new(); do task::spawn { chan.send(~[1u8, 2u8]); chan.send(~[]); @@ -199,7 +177,7 @@ mod test { #[test] fn test_chan_writer() { - let (port, chan) = comm::stream(); + let (port, chan) = Chan::new(); let mut writer = ChanWriter::new(chan); writer.write_be_u32(42); diff --git a/src/libstd/io/mod.rs b/src/libstd/io/mod.rs index c0bdc2a201435..2e9056a6aee48 100644 --- a/src/libstd/io/mod.rs +++ b/src/libstd/io/mod.rs @@ -318,9 +318,6 @@ mod option; /// Basic stream compression. XXX: Belongs with other flate code pub mod flate; -/// Interop between byte streams and pipes. Not sure where it belongs -pub mod comm_adapters; - /// Extension traits pub mod extensions; diff --git a/src/libstd/io/net/tcp.rs b/src/libstd/io/net/tcp.rs index 3c7582db7b85d..a6230ede7e348 100644 --- a/src/libstd/io/net/tcp.rs +++ b/src/libstd/io/net/tcp.rs @@ -151,7 +151,6 @@ mod test { use io::net::ip::{Ipv4Addr, SocketAddr}; use io::*; use prelude::*; - use rt::comm::oneshot; #[test] #[ignore] fn bind_error() { @@ -195,7 +194,7 @@ mod test { fn smoke_test_ip4() { do run_in_mt_newsched_task { let addr = next_test_ip4(); - let (port, chan) = oneshot(); + let (port, chan) = Chan::new(); do spawntask { let mut acceptor = TcpListener::bind(addr).listen(); @@ -206,11 +205,9 @@ mod test { assert!(buf[0] == 99); } - do spawntask { - port.recv(); - let mut stream = TcpStream::connect(addr); - stream.write([99]); - } + port.recv(); + let mut stream = TcpStream::connect(addr); + stream.write([99]); } } @@ -218,7 +215,7 @@ mod test { fn smoke_test_ip6() { do run_in_mt_newsched_task { let addr = next_test_ip6(); - let (port, chan) = oneshot(); + let (port, chan) = Chan::new(); do spawntask { let mut acceptor = TcpListener::bind(addr).listen(); @@ -229,11 +226,9 @@ mod test { assert!(buf[0] == 99); } - do spawntask { - port.recv(); - let mut stream = TcpStream::connect(addr); - stream.write([99]); - } + port.recv(); + let mut stream = TcpStream::connect(addr); + stream.write([99]); } } @@ -241,7 +236,7 @@ mod test { fn read_eof_ip4() { do run_in_mt_newsched_task { let addr = next_test_ip4(); - let (port, chan) = oneshot(); + let (port, chan) = Chan::new(); do spawntask { let mut acceptor = TcpListener::bind(addr).listen(); @@ -252,11 +247,9 @@ mod test { assert!(nread.is_none()); } - do spawntask { - port.recv(); - let _stream = TcpStream::connect(addr); - // Close - } + port.recv(); + let _stream = TcpStream::connect(addr); + // Close } } @@ -264,7 +257,7 @@ mod test { fn read_eof_ip6() { do run_in_mt_newsched_task { let addr = next_test_ip6(); - let (port, chan) = oneshot(); + let (port, chan) = Chan::new(); do spawntask { let mut acceptor = TcpListener::bind(addr).listen(); @@ -275,11 +268,9 @@ mod test { assert!(nread.is_none()); } - do spawntask { - port.recv(); - let _stream = TcpStream::connect(addr); - // Close - } + port.recv(); + let _stream = TcpStream::connect(addr); + // Close } } @@ -287,7 +278,7 @@ mod test { fn read_eof_twice_ip4() { do run_in_mt_newsched_task { let addr = next_test_ip4(); - let (port, chan) = oneshot(); + let (port, chan) = Chan::new(); do spawntask { let mut acceptor = TcpListener::bind(addr).listen(); @@ -308,11 +299,9 @@ mod test { }) } - do spawntask { - port.recv(); - let _stream = TcpStream::connect(addr); - // Close - } + port.recv(); + let _stream = TcpStream::connect(addr); + // Close } } @@ -320,7 +309,7 @@ mod test { fn read_eof_twice_ip6() { do run_in_mt_newsched_task { let addr = next_test_ip6(); - let (port, chan) = oneshot(); + let (port, chan) = Chan::new(); do spawntask { let mut acceptor = TcpListener::bind(addr).listen(); @@ -341,11 +330,9 @@ mod test { }) } - do spawntask { - port.recv(); - let _stream = TcpStream::connect(addr); - // Close - } + port.recv(); + let _stream = TcpStream::connect(addr); + // Close } } @@ -353,7 +340,7 @@ mod test { fn write_close_ip4() { do run_in_mt_newsched_task { let addr = next_test_ip4(); - let (port, chan) = oneshot(); + let (port, chan) = Chan::new(); do spawntask { let mut acceptor = TcpListener::bind(addr).listen(); @@ -377,11 +364,9 @@ mod test { } } - do spawntask { - port.recv(); - let _stream = TcpStream::connect(addr); - // Close - } + port.recv(); + let _stream = TcpStream::connect(addr); + // Close } } @@ -389,7 +374,7 @@ mod test { fn write_close_ip6() { do run_in_mt_newsched_task { let addr = next_test_ip6(); - let (port, chan) = oneshot(); + let (port, chan) = Chan::new(); do spawntask { let mut acceptor = TcpListener::bind(addr).listen(); @@ -413,11 +398,9 @@ mod test { } } - do spawntask { - port.recv(); - let _stream = TcpStream::connect(addr); - // Close - } + port.recv(); + let _stream = TcpStream::connect(addr); + // Close } } @@ -426,7 +409,7 @@ mod test { do run_in_mt_newsched_task { let addr = next_test_ip4(); let max = 10; - let (port, chan) = oneshot(); + let (port, chan) = Chan::new(); do spawntask { let mut acceptor = TcpListener::bind(addr).listen(); @@ -438,13 +421,11 @@ mod test { } } - do spawntask { - port.recv(); - max.times(|| { - let mut stream = TcpStream::connect(addr); - stream.write([99]); - }); - } + port.recv(); + max.times(|| { + let mut stream = TcpStream::connect(addr); + stream.write([99]); + }); } } @@ -453,7 +434,7 @@ mod test { do run_in_mt_newsched_task { let addr = next_test_ip6(); let max = 10; - let (port, chan) = oneshot(); + let (port, chan) = Chan::new(); do spawntask { let mut acceptor = TcpListener::bind(addr).listen(); @@ -465,13 +446,11 @@ mod test { } } - do spawntask { - port.recv(); - max.times(|| { - let mut stream = TcpStream::connect(addr); - stream.write([99]); - }); - } + port.recv(); + max.times(|| { + let mut stream = TcpStream::connect(addr); + stream.write([99]); + }); } } @@ -480,7 +459,7 @@ mod test { do run_in_mt_newsched_task { let addr = next_test_ip4(); static MAX: int = 10; - let (port, chan) = oneshot(); + let (port, chan) = Chan::new(); do spawntask { let mut acceptor = TcpListener::bind(addr).listen(); @@ -520,7 +499,7 @@ mod test { do run_in_mt_newsched_task { let addr = next_test_ip6(); static MAX: int = 10; - let (port, chan) = oneshot(); + let (port, chan) = Chan::new(); do spawntask { let mut acceptor = TcpListener::bind(addr).listen(); @@ -560,7 +539,7 @@ mod test { do run_in_mt_newsched_task { let addr = next_test_ip4(); static MAX: int = 10; - let (port, chan) = oneshot(); + let (port, chan) = Chan::new(); do spawntask { let mut acceptor = TcpListener::bind(addr).listen(); @@ -599,7 +578,7 @@ mod test { do run_in_mt_newsched_task { let addr = next_test_ip6(); static MAX: int = 10; - let (port, chan) = oneshot(); + let (port, chan) = Chan::new(); do spawntask { let mut acceptor = TcpListener::bind(addr).listen(); @@ -653,7 +632,7 @@ mod test { #[cfg(test)] fn peer_name(addr: SocketAddr) { do run_in_mt_newsched_task { - let (port, chan) = oneshot(); + let (port, chan) = Chan::new(); do spawntask { let mut acceptor = TcpListener::bind(addr).listen(); @@ -662,20 +641,18 @@ mod test { acceptor.accept(); } - do spawntask { - port.recv(); - let stream = TcpStream::connect(addr); + port.recv(); + let stream = TcpStream::connect(addr); - assert!(stream.is_some()); - let mut stream = stream.unwrap(); + assert!(stream.is_some()); + let mut stream = stream.unwrap(); - // Make sure peer_name gives us the - // address/port of the peer we've - // connected to. - let peer_name = stream.peer_name(); - assert!(peer_name.is_some()); - assert_eq!(addr, peer_name.unwrap()); - } + // Make sure peer_name gives us the + // address/port of the peer we've + // connected to. + let peer_name = stream.peer_name(); + assert!(peer_name.is_some()); + assert_eq!(addr, peer_name.unwrap()); } } diff --git a/src/libstd/io/net/udp.rs b/src/libstd/io/net/udp.rs index 87cf59aba3bf7..1e56f964bea52 100644 --- a/src/libstd/io/net/udp.rs +++ b/src/libstd/io/net/udp.rs @@ -107,8 +107,7 @@ mod test { use rt::test::*; use io::net::ip::{Ipv4Addr, SocketAddr}; use io::*; - use option::{Some, None}; - use rt::comm::oneshot; + use prelude::*; #[test] #[ignore] fn bind_error() { @@ -131,7 +130,7 @@ mod test { do run_in_mt_newsched_task { let server_ip = next_test_ip4(); let client_ip = next_test_ip4(); - let (port, chan) = oneshot(); + let (port, chan) = Chan::new(); do spawntask { match UdpSocket::bind(server_ip) { @@ -151,14 +150,12 @@ mod test { } } - do spawntask { - match UdpSocket::bind(client_ip) { - Some(ref mut client) => { - port.recv(); - client.sendto([99], server_ip) - } - None => fail!() + match UdpSocket::bind(client_ip) { + Some(ref mut client) => { + port.recv(); + client.sendto([99], server_ip) } + None => fail!() } } } @@ -168,7 +165,7 @@ mod test { do run_in_mt_newsched_task { let server_ip = next_test_ip6(); let client_ip = next_test_ip6(); - let (port, chan) = oneshot(); + let (port, chan) = Chan::new(); do spawntask { match UdpSocket::bind(server_ip) { @@ -188,14 +185,12 @@ mod test { } } - do spawntask { - match UdpSocket::bind(client_ip) { - Some(ref mut client) => { - port.recv(); - client.sendto([99], server_ip) - } - None => fail!() + match UdpSocket::bind(client_ip) { + Some(ref mut client) => { + port.recv(); + client.sendto([99], server_ip) } + None => fail!() } } } @@ -205,7 +200,7 @@ mod test { do run_in_mt_newsched_task { let server_ip = next_test_ip4(); let client_ip = next_test_ip4(); - let (port, chan) = oneshot(); + let (port, chan) = Chan::new(); do spawntask { match UdpSocket::bind(server_ip) { @@ -226,16 +221,14 @@ mod test { } } - do spawntask { - match UdpSocket::bind(client_ip) { - Some(client) => { - let client = ~client; - let mut stream = client.connect(server_ip); - port.recv(); - stream.write([99]); - } - None => fail!() + match UdpSocket::bind(client_ip) { + Some(client) => { + let client = ~client; + let mut stream = client.connect(server_ip); + port.recv(); + stream.write([99]); } + None => fail!() } } } @@ -245,7 +238,7 @@ mod test { do run_in_mt_newsched_task { let server_ip = next_test_ip6(); let client_ip = next_test_ip6(); - let (port, chan) = oneshot(); + let (port, chan) = Chan::new(); do spawntask { match UdpSocket::bind(server_ip) { @@ -266,16 +259,14 @@ mod test { } } - do spawntask { - match UdpSocket::bind(client_ip) { - Some(client) => { - let client = ~client; - let mut stream = client.connect(server_ip); - port.recv(); - stream.write([99]); - } - None => fail!() + match UdpSocket::bind(client_ip) { + Some(client) => { + let client = ~client; + let mut stream = client.connect(server_ip); + port.recv(); + stream.write([99]); } + None => fail!() } } } diff --git a/src/libstd/io/net/unix.rs b/src/libstd/io/net/unix.rs index c1f75465d9ceb..49770b80060b8 100644 --- a/src/libstd/io/net/unix.rs +++ b/src/libstd/io/net/unix.rs @@ -152,14 +152,13 @@ mod tests { use super::*; use rt::test::*; use io::*; - use rt::comm::oneshot; fn smalltest(server: proc(UnixStream), client: proc(UnixStream)) { do run_in_mt_newsched_task { let path1 = next_test_unix(); let path2 = path1.clone(); - let (port, chan) = oneshot(); let (client, server) = (client, server); + let (port, chan) = Chan::new(); do spawntask { let mut acceptor = UnixListener::bind(&path1).listen(); @@ -167,10 +166,8 @@ mod tests { server(acceptor.accept().unwrap()); } - do spawntask { - port.recv(); - client(UnixStream::connect(&path2).unwrap()); - } + port.recv(); + client(UnixStream::connect(&path2).unwrap()); } } @@ -251,7 +248,7 @@ mod tests { let times = 10; let path1 = next_test_unix(); let path2 = path1.clone(); - let (port, chan) = oneshot(); + let (port, chan) = Chan::new(); do spawntask { let mut acceptor = UnixListener::bind(&path1).listen(); @@ -264,13 +261,11 @@ mod tests { }) } - do spawntask { - port.recv(); - times.times(|| { - let mut stream = UnixStream::connect(&path2); - stream.write([100]); - }) - } + port.recv(); + times.times(|| { + let mut stream = UnixStream::connect(&path2); + stream.write([100]); + }) } } diff --git a/src/libstd/io/signal.rs b/src/libstd/io/signal.rs index 3b6c6013dd2e6..c568a19dfa233 100644 --- a/src/libstd/io/signal.rs +++ b/src/libstd/io/signal.rs @@ -20,7 +20,7 @@ definitions for a number of signals. */ use clone::Clone; -use comm::{Port, SharedChan, stream}; +use comm::{Port, SharedChan}; use container::{Map, MutableMap}; use hashmap; use io::io_error; @@ -93,9 +93,9 @@ impl Listener { /// Creates a new listener for signals. Once created, signals are bound via /// the `register` method (otherwise nothing will ever be received) pub fn new() -> Listener { - let (port, chan) = stream(); + let (port, chan) = SharedChan::new(); Listener { - chan: SharedChan::new(chan), + chan: chan, port: port, handles: hashmap::HashMap::new(), } @@ -149,7 +149,6 @@ mod test { use libc; use io::timer; use super::{Listener, Interrupt}; - use comm::{GenericPort, Peekable}; // kill is only available on Unixes #[cfg(unix)] @@ -198,9 +197,7 @@ mod test { s2.unregister(Interrupt); sigint(); timer::sleep(10); - if s2.port.peek() { - fail!("Unexpected {:?}", s2.port.recv()); - } + assert!(s2.port.try_recv().is_none()); } #[cfg(windows)] diff --git a/src/libstd/io/timer.rs b/src/libstd/io/timer.rs index 202e02d55d0e6..5fb64ab3d09db 100644 --- a/src/libstd/io/timer.rs +++ b/src/libstd/io/timer.rs @@ -38,7 +38,7 @@ loop { */ -use comm::{Port, PortOne}; +use comm::Port; use option::{Option, Some, None}; use result::{Ok, Err}; use io::io_error; @@ -86,7 +86,7 @@ impl Timer { /// Note that this invalidates any previous port which has been created by /// this timer, and that the returned port will be invalidated once the /// timer is destroyed (when it falls out of scope). - pub fn oneshot(&mut self, msecs: u64) -> PortOne<()> { + pub fn oneshot(&mut self, msecs: u64) -> Port<()> { self.obj.oneshot(msecs) } diff --git a/src/libstd/lib.rs b/src/libstd/lib.rs index 3ac91a67d7ec0..53e26e435b7df 100644 --- a/src/libstd/lib.rs +++ b/src/libstd/lib.rs @@ -156,7 +156,6 @@ pub mod trie; pub mod task; pub mod comm; -pub mod select; pub mod local_data; @@ -203,15 +202,16 @@ pub mod rt; mod std { pub use clone; pub use cmp; + pub use comm; pub use condition; pub use fmt; + pub use io; pub use kinds; pub use local_data; pub use logging; pub use logging; pub use option; pub use os; - pub use io; pub use rt; pub use str; pub use to_bytes; diff --git a/src/libstd/prelude.rs b/src/libstd/prelude.rs index 83439d4c90305..201259679cc6f 100644 --- a/src/libstd/prelude.rs +++ b/src/libstd/prelude.rs @@ -84,7 +84,7 @@ pub use vec::{OwnedVector, OwnedCopyableVector,OwnedEqVector, MutableVector}; pub use vec::{Vector, VectorVector, CopyableVector, ImmutableVector}; // Reexported runtime types -pub use comm::{stream, Port, Chan, GenericChan, GenericSmartChan, GenericPort, Peekable}; +pub use comm::{Port, Chan, SharedChan}; pub use task::spawn; /// Disposes of a value. diff --git a/src/libstd/rand/os.rs b/src/libstd/rand/os.rs index 5558b8b33487b..1eaf1a29fa894 100644 --- a/src/libstd/rand/os.rs +++ b/src/libstd/rand/os.rs @@ -135,8 +135,10 @@ impl Drop for OSRng { #[cfg(test)] mod test { + use prelude::*; use super::*; use rand::Rng; + use task; #[test] fn test_os_rng() { @@ -151,16 +153,10 @@ mod test { #[test] fn test_os_rng_tasks() { - use task; - use comm; - use comm::{GenericChan, GenericPort}; - use option::{None, Some}; - use iter::{Iterator, range}; - use vec::{ImmutableVector, OwnedVector}; let mut chans = ~[]; for _ in range(0, 20) { - let (p, c) = comm::stream(); + let (p, c) = Chan::new(); chans.push(c); do task::spawn { // wait until all the tasks are ready to go. diff --git a/src/libstd/rt/comm.rs b/src/libstd/rt/comm.rs deleted file mode 100644 index 2fa349942928e..0000000000000 --- a/src/libstd/rt/comm.rs +++ /dev/null @@ -1,1141 +0,0 @@ -// Copyright 2013 The Rust Project Developers. See the COPYRIGHT -// file at the top-level directory of this distribution and at -// http://rust-lang.org/COPYRIGHT. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -//! Ports and channels. - -use option::*; -use cast; -use ops::Drop; -use rt::kill::BlockedTask; -use kinds::Send; -use rt; -use rt::sched::Scheduler; -use rt::local::Local; -use rt::select::{SelectInner, SelectPortInner}; -use select::{Select, SelectPort}; -use unstable::atomics::{AtomicUint, AtomicOption, Acquire, Relaxed, SeqCst}; -use unstable::sync::UnsafeArc; -use util; -use util::Void; -use comm::{GenericChan, GenericSmartChan, GenericPort, Peekable, SendDeferred}; -use cell::RefCell; -use clone::Clone; -use tuple::ImmutableTuple; - -/// A combined refcount / BlockedTask-as-uint pointer. -/// -/// Can be equal to the following values: -/// -/// * 2 - both endpoints are alive -/// * 1 - either the sender or the receiver is dead, determined by context -/// * - A pointer to a blocked Task (see BlockedTask::cast_{to,from}_uint) -type State = uint; - -static STATE_BOTH: State = 2; -static STATE_ONE: State = 1; - -/// The heap-allocated structure shared between two endpoints. -struct Packet { - state: AtomicUint, - payload: Option, -} - -// A one-shot channel. -pub struct ChanOne { - priv void_packet: *mut Void, - priv suppress_finalize: bool -} - -/// A one-shot port. -pub struct PortOne { - priv void_packet: *mut Void, - priv suppress_finalize: bool -} - -pub fn oneshot() -> (PortOne, ChanOne) { - let packet: ~Packet = ~Packet { - state: AtomicUint::new(STATE_BOTH), - payload: None - }; - - unsafe { - let packet: *mut Void = cast::transmute(packet); - let port = PortOne { - void_packet: packet, - suppress_finalize: false - }; - let chan = ChanOne { - void_packet: packet, - suppress_finalize: false - }; - return (port, chan); - } -} - -impl ChanOne { - #[inline] - fn packet(&self) -> *mut Packet { - unsafe { - let p: *mut ~Packet = cast::transmute(&self.void_packet); - let p: *mut Packet = &mut **p; - return p; - } - } - - /// Send a message on the one-shot channel. If a receiver task is blocked - /// waiting for the message, will wake it up and reschedule to it. - pub fn send(self, val: T) { - self.try_send(val); - } - - /// As `send`, but also returns whether or not the receiver endpoint is still open. - pub fn try_send(self, val: T) -> bool { - self.try_send_inner(val, true) - } - - /// Send a message without immediately rescheduling to a blocked receiver. - /// This can be useful in contexts where rescheduling is forbidden, or to - /// optimize for when the sender expects to still have useful work to do. - pub fn send_deferred(self, val: T) { - self.try_send_deferred(val); - } - - /// As `send_deferred` and `try_send` together. - pub fn try_send_deferred(self, val: T) -> bool { - self.try_send_inner(val, false) - } - - // 'do_resched' configures whether the scheduler immediately switches to - // the receiving task, or leaves the sending task still running. - fn try_send_inner(mut self, val: T, do_resched: bool) -> bool { - if do_resched { - rtassert!(!rt::in_sched_context()); - } - - // In order to prevent starvation of other tasks in situations - // where a task sends repeatedly without ever receiving, we - // occassionally yield instead of doing a send immediately. - // Only doing this if we're doing a rescheduling send, - // otherwise the caller is expecting not to context switch. - if do_resched { - // XXX: This TLS hit should be combined with other uses of the scheduler below - let sched: ~Scheduler = Local::take(); - sched.maybe_yield(); - } - - let mut recvr_active = true; - let packet = self.packet(); - - unsafe { - - // Install the payload - rtassert!((*packet).payload.is_none()); - (*packet).payload = Some(val); - - // Atomically swap out the old state to figure out what - // the port's up to, issuing a release barrier to prevent - // reordering of the payload write. This also issues an - // acquire barrier that keeps the subsequent access of the - // ~Task pointer from being reordered. - let oldstate = (*packet).state.swap(STATE_ONE, SeqCst); - - // Suppress the synchronizing actions in the finalizer. We're - // done with the packet. NB: In case of do_resched, this *must* - // happen before waking up a blocked task (or be unkillable), - // because we might get a kill signal during the reschedule. - self.suppress_finalize = true; - - match oldstate { - STATE_BOTH => { - // Port is not waiting yet. Nothing to do - } - STATE_ONE => { - // Port has closed. Need to clean up. - let _packet: ~Packet = cast::transmute(self.void_packet); - recvr_active = false; - } - task_as_state => { - // Port is blocked. Wake it up. - let recvr = BlockedTask::cast_from_uint(task_as_state); - if do_resched { - recvr.wake().map(|woken_task| { - Scheduler::run_task(woken_task); - }); - } else { - let mut sched = Local::borrow(None::); - sched.get().enqueue_blocked_task(recvr); - } - } - } - } - - return recvr_active; - } -} - -impl PortOne { - fn packet(&self) -> *mut Packet { - unsafe { - let p: *mut ~Packet = cast::transmute(&self.void_packet); - let p: *mut Packet = &mut **p; - return p; - } - } - - /// Wait for a message on the one-shot port. Fails if the send end is closed. - pub fn recv(self) -> T { - match self.try_recv() { - Some(val) => val, - None => { - fail!("receiving on closed channel"); - } - } - } - - /// As `recv`, but returns `None` if the send end is closed rather than failing. - pub fn try_recv(mut self) -> Option { - // Optimistic check. If data was sent already, we don't even need to block. - // No release barrier needed here; we're not handing off our task pointer yet. - if !self.optimistic_check() { - // No data available yet. - // Switch to the scheduler to put the ~Task into the Packet state. - let sched: ~Scheduler = Local::take(); - sched.deschedule_running_task_and_then(|sched, task| { - self.block_on(sched, task); - }) - } - - // Task resumes. - self.recv_ready() - } -} - -impl SelectInner for PortOne { - #[inline] #[cfg(not(test))] - fn optimistic_check(&mut self) -> bool { - unsafe { (*self.packet()).state.load(Acquire) == STATE_ONE } - } - - #[inline] #[cfg(test)] - fn optimistic_check(&mut self) -> bool { - // The optimistic check is never necessary for correctness. For testing - // purposes, making it randomly return false simulates a racing sender. - use rand::{Rand}; - let mut sched = Local::borrow(None::); - let actually_check = Rand::rand(&mut sched.get().rng); - if actually_check { - unsafe { (*self.packet()).state.load(Acquire) == STATE_ONE } - } else { - false - } - } - - fn block_on(&mut self, sched: &mut Scheduler, task: BlockedTask) -> bool { - unsafe { - // Atomically swap the task pointer into the Packet state, issuing - // an acquire barrier to prevent reordering of the subsequent read - // of the payload. Also issues a release barrier to prevent - // reordering of any previous writes to the task structure. - let task_as_state = task.cast_to_uint(); - let oldstate = (*self.packet()).state.swap(task_as_state, SeqCst); - match oldstate { - STATE_BOTH => { - // Data has not been sent. Now we're blocked. - rtdebug!("non-rendezvous recv"); - false - } - STATE_ONE => { - // Re-record that we are the only owner of the packet. - // No barrier needed, even if the task gets reawoken - // on a different core -- this is analogous to writing a - // payload; a barrier in enqueueing the task protects it. - // NB(#8132). This *must* occur before the enqueue below. - // FIXME(#6842, #8130) This is usually only needed for the - // assertion in recv_ready, except in the case of select(). - // This won't actually ever have cacheline contention, but - // maybe should be optimized out with a cfg(test) anyway? - (*self.packet()).state.store(STATE_ONE, Relaxed); - - rtdebug!("rendezvous recv"); - - // Channel is closed. Switch back and check the data. - // NB: We have to drop back into the scheduler event loop here - // instead of switching immediately back or we could end up - // triggering infinite recursion on the scheduler's stack. - let recvr = BlockedTask::cast_from_uint(task_as_state); - sched.enqueue_blocked_task(recvr); - true - } - _ => rtabort!("can't block_on; a task is already blocked") - } - } - } - - // This is the only select trait function that's not also used in recv. - fn unblock_from(&mut self) -> bool { - let packet = self.packet(); - unsafe { - // In case the data is available, the acquire barrier here matches - // the release barrier the sender used to release the payload. - match (*packet).state.load(Acquire) { - // Impossible. We removed STATE_BOTH when blocking on it, and - // no self-respecting sender would put it back. - STATE_BOTH => rtabort!("refcount already 2 in unblock_from"), - // Here, a sender already tried to wake us up. Perhaps they - // even succeeded! Data is available. - STATE_ONE => true, - // Still registered as blocked. Need to "unblock" the pointer. - task_as_state => { - // In the window between the load and the CAS, a sender - // might take the pointer and set the refcount to ONE. If - // that happens, we shouldn't clobber that with BOTH! - // Acquire barrier again for the same reason as above. - match (*packet).state.compare_and_swap(task_as_state, STATE_BOTH, - Acquire) { - STATE_BOTH => rtabort!("refcount became 2 in unblock_from"), - STATE_ONE => true, // Lost the race. Data available. - same_ptr => { - // We successfully unblocked our task pointer. - rtassert!(task_as_state == same_ptr); - let handle = BlockedTask::cast_from_uint(task_as_state); - // Because we are already awake, the handle we - // gave to this port shall already be empty. - handle.assert_already_awake(); - false - } - } - } - } - } - } -} - -impl Select for PortOne { } - -impl SelectPortInner for PortOne { - fn recv_ready(mut self) -> Option { - let packet = self.packet(); - - // No further memory barrier is needed here to access the - // payload. Some scenarios: - // - // 1) We encountered STATE_ONE above - the atomic_xchg was the acq barrier. We're fine. - // 2) We encountered STATE_BOTH above and blocked. The sending task then ran us - // and ran on its thread. The sending task issued a read barrier when taking the - // pointer to the receiving task. - // 3) We encountered STATE_BOTH above and blocked, but the receiving task (this task) - // is pinned to some other scheduler, so the sending task had to give us to - // a different scheduler for resuming. That send synchronized memory. - unsafe { - // See corresponding store() above in block_on for rationale. - // FIXME(#8130) This can happen only in test builds. - // This load is not required for correctness and may be compiled out. - rtassert!((*packet).state.load(Relaxed) == STATE_ONE); - - let payload = (*packet).payload.take(); - - // The sender has closed up shop. Drop the packet. - let _packet: ~Packet = cast::transmute(self.void_packet); - // Suppress the synchronizing actions in the finalizer. We're done with the packet. - self.suppress_finalize = true; - return payload; - } - } -} - -impl SelectPort for PortOne { } - -impl Peekable for PortOne { - fn peek(&self) -> bool { - unsafe { - let packet: *mut Packet = self.packet(); - let oldstate = (*packet).state.load(SeqCst); - match oldstate { - STATE_BOTH => false, - STATE_ONE => (*packet).payload.is_some(), - _ => rtabort!("peeked on a blocked task") - } - } - } -} - -#[unsafe_destructor] -impl Drop for ChanOne { - fn drop(&mut self) { - if self.suppress_finalize { return } - - unsafe { - let oldstate = (*self.packet()).state.swap(STATE_ONE, SeqCst); - match oldstate { - STATE_BOTH => { - // Port still active. It will destroy the Packet. - }, - STATE_ONE => { - let _packet: ~Packet = cast::transmute(self.void_packet); - }, - task_as_state => { - // The port is blocked waiting for a message we will never send. Wake it. - rtassert!((*self.packet()).payload.is_none()); - let recvr = BlockedTask::cast_from_uint(task_as_state); - recvr.wake().map(|woken_task| { - Scheduler::run_task(woken_task); - }); - } - } - } - } -} - -#[unsafe_destructor] -impl Drop for PortOne { - fn drop(&mut self) { - if self.suppress_finalize { return } - - unsafe { - let oldstate = (*self.packet()).state.swap(STATE_ONE, SeqCst); - match oldstate { - STATE_BOTH => { - // Chan still active. It will destroy the packet. - }, - STATE_ONE => { - let _packet: ~Packet = cast::transmute(self.void_packet); - } - task_as_state => { - // This case occurs during unwinding, when the blocked - // receiver was killed awake. The task can't still be - // blocked (we are it), but we need to free the handle. - let recvr = BlockedTask::cast_from_uint(task_as_state); - recvr.assert_already_awake(); - } - } - } - } -} - -struct StreamPayload { - val: T, - next: PortOne> -} - -type StreamChanOne = ChanOne>; -type StreamPortOne = PortOne>; - -/// A channel with unbounded size. -pub struct Chan { - // FIXME #5372. Using RefCell because we don't take &mut self - next: RefCell> -} - -/// An port with unbounded size. -pub struct Port { - // FIXME #5372. Using RefCell because we don't take &mut self - next: RefCell>> -} - -pub fn stream() -> (Port, Chan) { - let (pone, cone) = oneshot(); - let port = Port { next: RefCell::new(Some(pone)) }; - let chan = Chan { next: RefCell::new(cone) }; - return (port, chan); -} - -impl Chan { - fn try_send_inner(&self, val: T, do_resched: bool) -> bool { - let (next_pone, mut cone) = oneshot(); - let mut b = self.next.borrow_mut(); - util::swap(&mut cone, b.get()); - cone.try_send_inner(StreamPayload { val: val, next: next_pone }, do_resched) - } -} - -impl GenericChan for Chan { - fn send(&self, val: T) { - self.try_send(val); - } -} - -impl GenericSmartChan for Chan { - fn try_send(&self, val: T) -> bool { - self.try_send_inner(val, true) - } -} - -impl SendDeferred for Chan { - fn send_deferred(&self, val: T) { - self.try_send_deferred(val); - } - fn try_send_deferred(&self, val: T) -> bool { - self.try_send_inner(val, false) - } -} - -impl GenericPort for Port { - fn recv(&self) -> T { - match self.try_recv() { - Some(val) => val, - None => { - fail!("receiving on closed channel"); - } - } - } - - fn try_recv(&self) -> Option { - let mut b = self.next.borrow_mut(); - b.get().take().map_default(None, |pone| { - match pone.try_recv() { - Some(StreamPayload { val, next }) => { - *b.get() = Some(next); - Some(val) - } - None => None - } - }) - } -} - -impl Peekable for Port { - fn peek(&self) -> bool { - self.next.with_mut(|p| p.get_mut_ref().peek()) - } -} - -// XXX: Kind of gross. A Port should be selectable so you can make an array -// of them, but a &Port should also be selectable so you can select2 on it -// alongside a PortOne without passing the port by value in recv_ready. - -impl<'a, T: Send> SelectInner for &'a Port { - #[inline] - fn optimistic_check(&mut self) -> bool { - self.next.with_mut(|pone| { pone.get_mut_ref().optimistic_check() }) - } - - #[inline] - fn block_on(&mut self, sched: &mut Scheduler, task: BlockedTask) -> bool { - let mut b = self.next.borrow_mut(); - b.get().get_mut_ref().block_on(sched, task) - } - - #[inline] - fn unblock_from(&mut self) -> bool { - self.next.with_mut(|pone| { pone.get_mut_ref().unblock_from() }) - } -} - -impl<'a, T: Send> Select for &'a Port { } - -impl SelectInner for Port { - #[inline] - fn optimistic_check(&mut self) -> bool { - (&*self).optimistic_check() - } - - #[inline] - fn block_on(&mut self, sched: &mut Scheduler, task: BlockedTask) -> bool { - (&*self).block_on(sched, task) - } - - #[inline] - fn unblock_from(&mut self) -> bool { - (&*self).unblock_from() - } -} - -impl Select for Port { } - -impl<'a, T: Send> SelectPortInner for &'a Port { - fn recv_ready(self) -> Option { - let mut b = self.next.borrow_mut(); - match b.get().take_unwrap().recv_ready() { - Some(StreamPayload { val, next }) => { - *b.get() = Some(next); - Some(val) - } - None => None - } - } -} - -impl<'a, T: Send> SelectPort for &'a Port { } - -pub struct SharedChan { - // Just like Chan, but a shared AtomicOption - priv next: UnsafeArc>> -} - -impl SharedChan { - pub fn new(chan: Chan) -> SharedChan { - let next = chan.next.unwrap(); - let next = AtomicOption::new(~next); - SharedChan { next: UnsafeArc::new(next) } - } -} - -impl SharedChan { - fn try_send_inner(&self, val: T, do_resched: bool) -> bool { - unsafe { - let (next_pone, next_cone) = oneshot(); - let cone = (*self.next.get()).swap(~next_cone, SeqCst); - cone.unwrap().try_send_inner(StreamPayload { val: val, next: next_pone }, - do_resched) - } - } -} - -impl GenericChan for SharedChan { - fn send(&self, val: T) { - self.try_send(val); - } -} - -impl GenericSmartChan for SharedChan { - fn try_send(&self, val: T) -> bool { - self.try_send_inner(val, true) - } -} - -impl SendDeferred for SharedChan { - fn send_deferred(&self, val: T) { - self.try_send_deferred(val); - } - fn try_send_deferred(&self, val: T) -> bool { - self.try_send_inner(val, false) - } -} - -impl Clone for SharedChan { - fn clone(&self) -> SharedChan { - SharedChan { - next: self.next.clone() - } - } -} - -pub struct SharedPort { - // The next port on which we will receive the next port on which we will receive T - priv next_link: UnsafeArc>>> -} - -impl SharedPort { - pub fn new(port: Port) -> SharedPort { - // Put the data port into a new link pipe - let next_data_port = port.next.unwrap().unwrap(); - let (next_link_port, next_link_chan) = oneshot(); - next_link_chan.send(next_data_port); - let next_link = AtomicOption::new(~next_link_port); - SharedPort { next_link: UnsafeArc::new(next_link) } - } -} - -impl GenericPort for SharedPort { - fn recv(&self) -> T { - match self.try_recv() { - Some(val) => val, - None => { - fail!("receiving on a closed channel"); - } - } - } - - fn try_recv(&self) -> Option { - unsafe { - let (next_link_port, next_link_chan) = oneshot(); - let link_port = (*self.next_link.get()).swap(~next_link_port, SeqCst); - let link_port = link_port.unwrap(); - let data_port = link_port.recv(); - let (next_data_port, res) = match data_port.try_recv() { - Some(StreamPayload { val, next }) => { - (next, Some(val)) - } - None => { - let (next_data_port, _) = oneshot(); - (next_data_port, None) - } - }; - next_link_chan.send(next_data_port); - return res; - } - } -} - -impl Clone for SharedPort { - fn clone(&self) -> SharedPort { - SharedPort { - next_link: self.next_link.clone() - } - } -} - -// FIXME #7760: Need better name -type MegaPipe = (SharedPort, SharedChan); - -pub fn megapipe() -> MegaPipe { - let (port, chan) = stream(); - (SharedPort::new(port), SharedChan::new(chan)) -} - -impl GenericChan for MegaPipe { - fn send(&self, val: T) { - self.second_ref().send(val) - } -} - -impl GenericSmartChan for MegaPipe { - fn try_send(&self, val: T) -> bool { - self.second_ref().try_send(val) - } -} - -impl GenericPort for MegaPipe { - fn recv(&self) -> T { - self.first_ref().recv() - } - - fn try_recv(&self) -> Option { - self.first_ref().try_recv() - } -} - -impl SendDeferred for MegaPipe { - fn send_deferred(&self, val: T) { - self.second_ref().send_deferred(val) - } - fn try_send_deferred(&self, val: T) -> bool { - self.second_ref().try_send_deferred(val) - } -} - -#[cfg(test)] -mod test { - use super::*; - use option::*; - use rt::test::*; - use num::Times; - use rt::util; - - #[test] - fn oneshot_single_thread_close_port_first() { - // Simple test of closing without sending - do run_in_newsched_task { - let (port, _chan) = oneshot::(); - { let _p = port; } - } - } - - #[test] - fn oneshot_single_thread_close_chan_first() { - // Simple test of closing without sending - do run_in_newsched_task { - let (_port, chan) = oneshot::(); - { let _c = chan; } - } - } - - #[test] - fn oneshot_single_thread_send_port_close() { - // Testing that the sender cleans up the payload if receiver is closed - do run_in_newsched_task { - let (port, chan) = oneshot::<~int>(); - { let _p = port; } - chan.send(~0); - } - } - - #[test] - fn oneshot_single_thread_recv_chan_close() { - // Receiving on a closed chan will fail - do run_in_newsched_task { - let res = do spawntask_try { - let (port, chan) = oneshot::<~int>(); - { let _c = chan; } - port.recv(); - }; - // What is our res? - rtdebug!("res is: {:?}", res.is_err()); - assert!(res.is_err()); - } - } - - #[test] - fn oneshot_single_thread_send_then_recv() { - do run_in_newsched_task { - let (port, chan) = oneshot::<~int>(); - chan.send(~10); - assert!(port.recv() == ~10); - } - } - - #[test] - fn oneshot_single_thread_try_send_open() { - do run_in_newsched_task { - let (port, chan) = oneshot::(); - assert!(chan.try_send(10)); - assert!(port.recv() == 10); - } - } - - #[test] - fn oneshot_single_thread_try_send_closed() { - do run_in_newsched_task { - let (port, chan) = oneshot::(); - { let _p = port; } - assert!(!chan.try_send(10)); - } - } - - #[test] - fn oneshot_single_thread_try_recv_open() { - do run_in_newsched_task { - let (port, chan) = oneshot::(); - chan.send(10); - assert!(port.try_recv() == Some(10)); - } - } - - #[test] - fn oneshot_single_thread_try_recv_closed() { - do run_in_newsched_task { - let (port, chan) = oneshot::(); - { let _c = chan; } - assert!(port.try_recv() == None); - } - } - - #[test] - fn oneshot_single_thread_peek_data() { - do run_in_newsched_task { - let (port, chan) = oneshot::(); - assert!(!port.peek()); - chan.send(10); - assert!(port.peek()); - } - } - - #[test] - fn oneshot_single_thread_peek_close() { - do run_in_newsched_task { - let (port, chan) = oneshot::(); - { let _c = chan; } - assert!(!port.peek()); - assert!(!port.peek()); - } - } - - #[test] - fn oneshot_single_thread_peek_open() { - do run_in_newsched_task { - let (port, _) = oneshot::(); - assert!(!port.peek()); - } - } - - #[test] - fn oneshot_multi_task_recv_then_send() { - do run_in_newsched_task { - let (port, chan) = oneshot::<~int>(); - do spawntask { - assert!(port.recv() == ~10); - } - - chan.send(~10); - } - } - - #[test] - fn oneshot_multi_task_recv_then_close() { - do run_in_newsched_task { - let (port, chan) = oneshot::<~int>(); - do spawntask_later { - let _ = chan; - } - let res = do spawntask_try { - assert!(port.recv() == ~10); - }; - assert!(res.is_err()); - } - } - - #[test] - fn oneshot_multi_thread_close_stress() { - if util::limit_thread_creation_due_to_osx_and_valgrind() { return; } - stress_factor().times(|| { - do run_in_newsched_task { - let (port, chan) = oneshot::(); - let thread = do spawntask_thread { - let _ = port; - }; - let _chan = chan; - thread.join(); - } - }) - } - - #[test] - fn oneshot_multi_thread_send_close_stress() { - if util::limit_thread_creation_due_to_osx_and_valgrind() { return; } - stress_factor().times(|| { - do run_in_newsched_task { - let (port, chan) = oneshot::(); - let thread1 = do spawntask_thread { - let _ = port; - }; - let thread2 = do spawntask_thread { - chan.send(1); - }; - thread1.join(); - thread2.join(); - } - }) - } - - #[test] - fn oneshot_multi_thread_recv_close_stress() { - if util::limit_thread_creation_due_to_osx_and_valgrind() { return; } - stress_factor().times(|| { - do run_in_newsched_task { - let (port, chan) = oneshot::(); - let thread1 = do spawntask_thread { - let port = port; - let res = do spawntask_try { - port.recv(); - }; - assert!(res.is_err()); - }; - let thread2 = do spawntask_thread { - let chan = chan; - do spawntask { - let _ = chan; - } - }; - thread1.join(); - thread2.join(); - } - }) - } - - #[test] - fn oneshot_multi_thread_send_recv_stress() { - if util::limit_thread_creation_due_to_osx_and_valgrind() { return; } - stress_factor().times(|| { - do run_in_newsched_task { - let (port, chan) = oneshot::<~int>(); - let thread1 = do spawntask_thread { - chan.send(~10); - }; - let thread2 = do spawntask_thread { - assert!(port.recv() == ~10); - }; - thread1.join(); - thread2.join(); - } - }) - } - - #[test] - fn stream_send_recv_stress() { - if util::limit_thread_creation_due_to_osx_and_valgrind() { return; } - stress_factor().times(|| { - do run_in_mt_newsched_task { - let (port, chan) = stream::<~int>(); - - send(chan, 0); - recv(port, 0); - - fn send(chan: Chan<~int>, i: int) { - if i == 10 { return } - - do spawntask_random { - chan.send(~i); - send(chan, i + 1); - } - } - - fn recv(port: Port<~int>, i: int) { - if i == 10 { return } - - do spawntask_random { - assert!(port.recv() == ~i); - recv(port, i + 1); - }; - } - } - }) - } - - #[test] - fn recv_a_lot() { - // Regression test that we don't run out of stack in scheduler context - do run_in_newsched_task { - let (port, chan) = stream(); - 10000.times(|| { chan.send(()) }); - 10000.times(|| { port.recv() }); - } - } - - #[test] - fn shared_chan_stress() { - if util::limit_thread_creation_due_to_osx_and_valgrind() { return; } - do run_in_mt_newsched_task { - let (port, chan) = stream(); - let chan = SharedChan::new(chan); - let total = stress_factor() + 100; - total.times(|| { - let chan_clone = chan.clone(); - do spawntask_random { - chan_clone.send(()); - } - }); - - total.times(|| { - port.recv(); - }); - } - } - - #[test] - fn shared_port_stress() { - if util::limit_thread_creation_due_to_osx_and_valgrind() { return; } - do run_in_mt_newsched_task { - let (end_port, end_chan) = stream(); - let (port, chan) = stream(); - let end_chan = SharedChan::new(end_chan); - let port = SharedPort::new(port); - let total = stress_factor() + 100; - total.times(|| { - let end_chan_clone = end_chan.clone(); - let port_clone = port.clone(); - do spawntask_random { - port_clone.recv(); - end_chan_clone.send(()); - } - }); - - total.times(|| { - chan.send(()); - }); - - total.times(|| { - end_port.recv(); - }); - } - } - - #[test] - fn shared_port_close_simple() { - do run_in_mt_newsched_task { - let (port, chan) = stream::<()>(); - let port = SharedPort::new(port); - { let _chan = chan; } - assert!(port.try_recv().is_none()); - } - } - - #[test] - fn shared_port_close() { - do run_in_mt_newsched_task { - let (end_port, end_chan) = stream::(); - let (port, chan) = stream::<()>(); - let end_chan = SharedChan::new(end_chan); - let port = SharedPort::new(port); - let chan = SharedChan::new(chan); - let send_total = 10; - let recv_total = 20; - do spawntask_random { - send_total.times(|| { - let chan_clone = chan.clone(); - do spawntask_random { - chan_clone.send(()); - } - }); - } - let end_chan_clone = end_chan.clone(); - do spawntask_random { - recv_total.times(|| { - let port_clone = port.clone(); - let end_chan_clone = end_chan_clone.clone(); - do spawntask_random { - let recvd = port_clone.try_recv().is_some(); - end_chan_clone.send(recvd); - } - }); - } - - let mut recvd = 0; - recv_total.times(|| { - recvd += if end_port.recv() { 1 } else { 0 }; - }); - - assert!(recvd == send_total); - } - } - - #[test] - fn megapipe_stress() { - use rand; - use rand::Rng; - - if util::limit_thread_creation_due_to_osx_and_valgrind() { return; } - - do run_in_mt_newsched_task { - let (end_port, end_chan) = stream::<()>(); - let end_chan = SharedChan::new(end_chan); - let pipe = megapipe(); - let total = stress_factor() + 10; - let mut rng = rand::rng(); - total.times(|| { - let msgs = rng.gen_range(0u, 10); - let pipe_clone = pipe.clone(); - let end_chan_clone = end_chan.clone(); - do spawntask_random { - msgs.times(|| { - pipe_clone.send(()); - }); - msgs.times(|| { - pipe_clone.recv(); - }); - } - - end_chan_clone.send(()); - }); - - total.times(|| { - end_port.recv(); - }); - } - } - - #[test] - fn send_deferred() { - use unstable::sync::atomic; - - // Tests no-rescheduling of send_deferred on all types of channels. - do run_in_newsched_task { - let (pone, cone) = oneshot(); - let (pstream, cstream) = stream(); - let (pshared, cshared) = stream(); - let cshared = SharedChan::new(cshared); - let mp = megapipe(); - - do spawntask { pone.recv(); } - do spawntask { pstream.recv(); } - do spawntask { pshared.recv(); } - let p_mp = mp.clone(); - do spawntask { p_mp.recv(); } - - unsafe { - let _guard = atomic(); - cone.send_deferred(()); - cstream.send_deferred(()); - cshared.send_deferred(()); - mp.send_deferred(()); - } - } - } - -} diff --git a/src/libstd/rt/kill.rs b/src/libstd/rt/kill.rs index e3f9cd09632b3..f4f128cf5aac1 100644 --- a/src/libstd/rt/kill.rs +++ b/src/libstd/rt/kill.rs @@ -153,8 +153,9 @@ There are two known issues with the current scheme for exit code propagation. use cast; use option::{Option, Some, None}; use prelude::*; +use iter; +use task::TaskResult; use rt::task::Task; -use rt::task::UnwindResult; use unstable::atomics::{AtomicUint, SeqCst}; use unstable::sync::UnsafeArc; @@ -169,11 +170,21 @@ pub enum BlockedTask { pub struct Death { // Action to be done with the exit code. If set, also makes the task wait // until all its watched children exit before collecting the status. - on_exit: Option, + on_exit: Option, // nesting level counter for unstable::atomically calls (0 == can deschedule). priv wont_sleep: int, } +pub struct BlockedTaskIterator { + priv inner: UnsafeArc, +} + +impl Iterator for BlockedTaskIterator { + fn next(&mut self) -> Option { + Some(Shared(self.inner.clone())) + } +} + impl BlockedTask { /// Returns Some if the task was successfully woken; None if already killed. pub fn wake(self) -> Option<~Task> { @@ -194,19 +205,17 @@ impl BlockedTask { } /// Converts one blocked task handle to a list of many handles to the same. - pub fn make_selectable(self, num_handles: uint) -> ~[BlockedTask] { - let handles = match self { + pub fn make_selectable(self, num_handles: uint) + -> iter::Take + { + let arc = match self { Owned(task) => { - let flag = unsafe { - AtomicUint::new(cast::transmute(task)) - }; - UnsafeArc::newN(flag, num_handles) + let flag = unsafe { AtomicUint::new(cast::transmute(task)) }; + UnsafeArc::new(flag) } - Shared(arc) => arc.cloneN(num_handles), + Shared(arc) => arc.clone(), }; - // Even if the task was unkillable before, we use 'Killable' because - // multiple pipes will have handles. It does not really mean killable. - handles.move_iter().map(|x| Shared(x)).collect() + BlockedTaskIterator{ inner: arc }.take(num_handles) } // This assertion has two flavours because the wake involves an atomic op. @@ -254,10 +263,10 @@ impl Death { } /// Collect failure exit codes from children and propagate them to a parent. - pub fn collect_failure(&mut self, result: UnwindResult) { + pub fn collect_failure(&mut self, result: TaskResult) { match self.on_exit.take() { + Some(f) => f(result), None => {} - Some(on_exit) => on_exit(result), } } diff --git a/src/libstd/rt/local_ptr.rs b/src/libstd/rt/local_ptr.rs index 66fe9742121ff..925aa802ad5c2 100644 --- a/src/libstd/rt/local_ptr.rs +++ b/src/libstd/rt/local_ptr.rs @@ -77,10 +77,9 @@ pub unsafe fn borrow() -> Borrowed { /// it wherever possible. #[cfg(not(windows), not(target_os = "android"))] pub mod compiled { - #[cfg(not(test))] - use libc::c_void; use cast; use option::{Option, Some, None}; + #[cfg(not(test))] use libc::c_void; #[cfg(test)] pub use realstd::rt::shouldnt_be_public::RT_TLS_PTR; diff --git a/src/libstd/rt/message_queue.rs b/src/libstd/rt/message_queue.rs deleted file mode 100644 index 10e457368f007..0000000000000 --- a/src/libstd/rt/message_queue.rs +++ /dev/null @@ -1,55 +0,0 @@ -// Copyright 2013 The Rust Project Developers. See the COPYRIGHT -// file at the top-level directory of this distribution and at -// http://rust-lang.org/COPYRIGHT. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -//! A concurrent queue that supports multiple producers and a -//! single consumer. - -use kinds::Send; -use vec::OwnedVector; -use option::Option; -use clone::Clone; -use rt::mpsc_queue::Queue; - -pub struct MessageQueue { - priv queue: Queue -} - -impl MessageQueue { - pub fn new() -> MessageQueue { - MessageQueue { - queue: Queue::new() - } - } - - #[inline] - pub fn push(&mut self, value: T) { - self.queue.push(value) - } - - #[inline] - pub fn pop(&mut self) -> Option { - self.queue.pop() - } - - /// A pop that may sometimes miss enqueued elements, but is much faster - /// to give up without doing any synchronization - #[inline] - pub fn casual_pop(&mut self) -> Option { - self.queue.pop() - } -} - -impl Clone for MessageQueue { - fn clone(&self) -> MessageQueue { - MessageQueue { - queue: self.queue.clone() - } - } -} diff --git a/src/libstd/rt/mod.rs b/src/libstd/rt/mod.rs index ce8d1ab1983a1..5d2179e8b9693 100644 --- a/src/libstd/rt/mod.rs +++ b/src/libstd/rt/mod.rs @@ -65,7 +65,7 @@ use ptr::RawPtr; use rt::local::Local; use rt::sched::{Scheduler, Shutdown}; use rt::sleeper_list::SleeperList; -use rt::task::UnwindResult; +use task::TaskResult; use rt::task::{Task, SchedTask, GreenTask, Sched}; use send_str::SendStrStatic; use unstable::atomics::{AtomicInt, AtomicBool, SeqCst}; @@ -91,8 +91,6 @@ pub use self::kill::BlockedTask; // XXX: these probably shouldn't be public... #[doc(hidden)] pub mod shouldnt_be_public { - pub use super::select::SelectInner; - pub use super::select::{SelectInner, SelectPortInner}; pub use super::local_ptr::native::maybe_tls_key; #[cfg(not(windows), not(target_os = "android"))] pub use super::local_ptr::compiled::RT_TLS_PTR; @@ -123,11 +121,11 @@ pub mod rtio; /// or task-local storage. pub mod local; -/// A parallel queue. -pub mod message_queue; - /// A mostly lock-free multi-producer, single consumer queue. -mod mpsc_queue; +pub mod mpsc_queue; + +/// A lock-free single-producer, single consumer queue. +pub mod spsc_queue; /// A lock-free multi-producer, multi-consumer bounded queue. mod mpmc_bounded_queue; @@ -169,11 +167,6 @@ pub mod rc; /// scheduler and task context pub mod tube; -/// Simple reimplementation of std::comm -pub mod comm; - -mod select; - /// The runtime needs to be able to put a pointer into thread-local storage. mod local_ptr; @@ -349,7 +342,7 @@ fn run_(main: proc(), use_main_sched: bool) -> int { // When the main task exits, after all the tasks in the main // task tree, shut down the schedulers and set the exit code. let handles = handles; - let on_exit: proc(UnwindResult) = proc(exit_success) { + let on_exit: proc(TaskResult) = proc(exit_success) { unsafe { assert!(!(*exited_already.get()).swap(true, SeqCst), "the runtime already exited"); @@ -361,7 +354,7 @@ fn run_(main: proc(), use_main_sched: bool) -> int { } unsafe { - let exit_code = if exit_success.is_success() { + let exit_code = if exit_success.is_ok() { use rt::util; // If we're exiting successfully, then return the global diff --git a/src/libstd/rt/mpmc_bounded_queue.rs b/src/libstd/rt/mpmc_bounded_queue.rs index 7f607fcf12a63..1e04e5eb78d59 100644 --- a/src/libstd/rt/mpmc_bounded_queue.rs +++ b/src/libstd/rt/mpmc_bounded_queue.rs @@ -1,5 +1,4 @@ -/* Multi-producer/multi-consumer bounded queue - * Copyright (c) 2010-2011 Dmitry Vyukov. All rights reserved. +/* Copyright (c) 2010-2011 Dmitry Vyukov. All rights reserved. * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * @@ -163,7 +162,6 @@ mod tests { use prelude::*; use option::*; use task; - use comm; use super::Queue; #[test] @@ -174,10 +172,9 @@ mod tests { assert_eq!(None, q.pop()); for _ in range(0, nthreads) { - let (port, chan) = comm::stream(); - chan.send(q.clone()); + let q = q.clone(); do task::spawn_sched(task::SingleThreaded) { - let mut q = port.recv(); + let mut q = q; for i in range(0, nmsgs) { assert!(q.push(i)); } @@ -186,12 +183,11 @@ mod tests { let mut completion_ports = ~[]; for _ in range(0, nthreads) { - let (completion_port, completion_chan) = comm::stream(); + let (completion_port, completion_chan) = Chan::new(); completion_ports.push(completion_port); - let (port, chan) = comm::stream(); - chan.send(q.clone()); + let q = q.clone(); do task::spawn_sched(task::SingleThreaded) { - let mut q = port.recv(); + let mut q = q; let mut i = 0u; loop { match q.pop() { @@ -206,7 +202,7 @@ mod tests { } } - for completion_port in completion_ports.iter() { + for completion_port in completion_ports.mut_iter() { assert_eq!(nmsgs, completion_port.recv()); } } diff --git a/src/libstd/rt/mpsc_queue.rs b/src/libstd/rt/mpsc_queue.rs index 4f39a1df4fa53..d575028af7043 100644 --- a/src/libstd/rt/mpsc_queue.rs +++ b/src/libstd/rt/mpsc_queue.rs @@ -1,5 +1,4 @@ -/* Multi-producer/single-consumer queue - * Copyright (c) 2010-2011 Dmitry Vyukov. All rights reserved. +/* Copyright (c) 2010-2011 Dmitry Vyukov. All rights reserved. * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * @@ -27,163 +26,177 @@ */ //! A mostly lock-free multi-producer, single consumer queue. -// http://www.1024cores.net/home/lock-free-algorithms/queues/intrusive-mpsc-node-based-queue -use unstable::sync::UnsafeArc; -use unstable::atomics::{AtomicPtr,Relaxed,Release,Acquire}; -use ptr::{mut_null, to_mut_unsafe_ptr}; +// http://www.1024cores.net/home/lock-free-algorithms +// /queues/non-intrusive-mpsc-node-based-queue + use cast; -use option::*; use clone::Clone; use kinds::Send; +use ops::Drop; +use option::{Option, None, Some}; +use unstable::atomics::{AtomicPtr, Release, Acquire, AcqRel, Relaxed}; +use unstable::sync::UnsafeArc; + +pub enum PopResult { + /// Some data has been popped + Data(T), + /// The queue is empty + Empty, + /// The queue is in an inconsistent state. Popping data should succeed, but + /// some pushers have yet to make enough progress in order allow a pop to + /// succeed. It is recommended that a pop() occur "in the near future" in + /// order to see if the sender has made progress or not + Inconsistent, +} struct Node { next: AtomicPtr>, value: Option, } -impl Node { - fn empty() -> Node { - Node{next: AtomicPtr::new(mut_null()), value: None} - } - - fn with_value(value: T) -> Node { - Node{next: AtomicPtr::new(mut_null()), value: Some(value)} - } -} - -struct State { - pad0: [u8, ..64], +struct State { head: AtomicPtr>, - pad1: [u8, ..64], - stub: Node, - pad2: [u8, ..64], tail: *mut Node, - pad3: [u8, ..64], + packet: P, } -struct Queue { - priv state: UnsafeArc>, +pub struct Consumer { + priv state: UnsafeArc>, } -impl Clone for Queue { - fn clone(&self) -> Queue { - Queue { - state: self.state.clone() - } - } +pub struct Producer { + priv state: UnsafeArc>, } -impl State { - pub fn new() -> State { - State{ - pad0: [0, ..64], - head: AtomicPtr::new(mut_null()), - pad1: [0, ..64], - stub: Node::::empty(), - pad2: [0, ..64], - tail: mut_null(), - pad3: [0, ..64], - } +impl Clone for Producer { + fn clone(&self) -> Producer { + Producer { state: self.state.clone() } } +} - fn init(&mut self) { - let stub = self.get_stub_unsafe(); - self.head.store(stub, Relaxed); - self.tail = stub; +pub fn queue(p: P) -> (Consumer, Producer) { + unsafe { + let (a, b) = UnsafeArc::new2(State::new(p)); + (Consumer { state: a }, Producer { state: b }) } +} - fn get_stub_unsafe(&mut self) -> *mut Node { - to_mut_unsafe_ptr(&mut self.stub) +impl Node { + unsafe fn new(v: Option) -> *mut Node { + cast::transmute(~Node { + next: AtomicPtr::new(0 as *mut Node), + value: v, + }) } +} - fn push(&mut self, value: T) { - unsafe { - let node = cast::transmute(~Node::with_value(value)); - self.push_node(node); +impl State { + pub unsafe fn new(p: P) -> State { + let stub = Node::new(None); + State { + head: AtomicPtr::new(stub), + tail: stub, + packet: p, } } - fn push_node(&mut self, node: *mut Node) { - unsafe { - (*node).next.store(mut_null(), Release); - let prev = self.head.swap(node, Relaxed); - (*prev).next.store(node, Release); - } + unsafe fn push(&mut self, t: T) { + let n = Node::new(Some(t)); + let prev = self.head.swap(n, AcqRel); + (*prev).next.store(n, Release); } - fn pop(&mut self) -> Option { - unsafe { - let mut tail = self.tail; - let mut next = (*tail).next.load(Acquire); - let stub = self.get_stub_unsafe(); - if tail == stub { - if mut_null() == next { - return None - } - self.tail = next; - tail = next; - next = (*next).next.load(Acquire); - } - if next != mut_null() { - let tail: ~Node = cast::transmute(tail); - self.tail = next; - return tail.value - } - let head = self.head.load(Relaxed); - if tail != head { - return None - } - self.push_node(stub); - next = (*tail).next.load(Acquire); - if next != mut_null() { - let tail: ~Node = cast::transmute(tail); - self.tail = next; - return tail.value - } + unsafe fn pop(&mut self) -> PopResult { + let tail = self.tail; + let next = (*tail).next.load(Acquire); + + if !next.is_null() { + self.tail = next; + assert!((*tail).value.is_none()); + assert!((*next).value.is_some()); + let ret = (*next).value.take_unwrap(); + let _: ~Node = cast::transmute(tail); + return Data(ret); } - None + + if self.head.load(Acquire) == tail {Empty} else {Inconsistent} + } + + unsafe fn is_empty(&mut self) -> bool { + return (*self.tail).next.load(Acquire).is_null(); } } -impl Queue { - pub fn new() -> Queue { +#[unsafe_destructor] +impl Drop for State { + fn drop(&mut self) { unsafe { - let q = Queue{state: UnsafeArc::new(State::new())}; - (*q.state.get()).init(); - q + let mut cur = self.tail; + while !cur.is_null() { + let next = (*cur).next.load(Relaxed); + let _: ~Node = cast::transmute(cur); + cur = next; + } } } +} +impl Producer { pub fn push(&mut self, value: T) { unsafe { (*self.state.get()).push(value) } } + pub fn is_empty(&self) -> bool { + unsafe{ (*self.state.get()).is_empty() } + } + pub unsafe fn packet(&self) -> *mut P { + &mut (*self.state.get()).packet as *mut P + } +} - pub fn pop(&mut self) -> Option { - unsafe{ (*self.state.get()).pop() } +impl Consumer { + pub fn pop(&mut self) -> PopResult { + unsafe { (*self.state.get()).pop() } + } + pub fn casual_pop(&mut self) -> Option { + match self.pop() { + Data(t) => Some(t), + Empty | Inconsistent => None, + } + } + pub unsafe fn packet(&self) -> *mut P { + &mut (*self.state.get()).packet as *mut P } } #[cfg(test)] mod tests { use prelude::*; - use option::*; + use task; - use comm; - use super::Queue; + use super::{queue, Data, Empty, Inconsistent}; + + #[test] + fn test_full() { + let (_, mut p) = queue(()); + p.push(~1); + p.push(~2); + } #[test] fn test() { let nthreads = 8u; let nmsgs = 1000u; - let mut q = Queue::new(); - assert_eq!(None, q.pop()); + let (mut c, p) = queue(()); + match c.pop() { + Empty => {} + Inconsistent | Data(..) => fail!() + } for _ in range(0, nthreads) { - let (port, chan) = comm::stream(); - chan.send(q.clone()); + let q = p.clone(); do task::spawn_sched(task::SingleThreaded) { - let mut q = port.recv(); + let mut q = q; for i in range(0, nmsgs) { q.push(i); } @@ -191,13 +204,10 @@ mod tests { } let mut i = 0u; - loop { - match q.pop() { - None => {}, - Some(_) => { - i += 1; - if i == nthreads*nmsgs { break } - } + while i < nthreads * nmsgs { + match c.pop() { + Empty | Inconsistent => {}, + Data(_) => { i += 1 } } } } diff --git a/src/libstd/rt/rtio.rs b/src/libstd/rt/rtio.rs index 557d9c998caea..b54231421e396 100644 --- a/src/libstd/rt/rtio.rs +++ b/src/libstd/rt/rtio.rs @@ -10,7 +10,7 @@ use c_str::CString; use cast; -use comm::{SharedChan, PortOne, Port}; +use comm::{SharedChan, Port}; use libc::c_int; use libc; use ops::Drop; @@ -222,7 +222,7 @@ pub trait RtioUdpSocket : RtioSocket { pub trait RtioTimer { fn sleep(&mut self, msecs: u64); - fn oneshot(&mut self, msecs: u64) -> PortOne<()>; + fn oneshot(&mut self, msecs: u64) -> Port<()>; fn period(&mut self, msecs: u64) -> Port<()>; } diff --git a/src/libstd/rt/sched.rs b/src/libstd/rt/sched.rs index fa17efc833bd0..ac3aeb5a4bb3d 100644 --- a/src/libstd/rt/sched.rs +++ b/src/libstd/rt/sched.rs @@ -17,7 +17,6 @@ use super::stack::{StackPool}; use super::rtio::EventLoop; use super::context::Context; use super::task::{Task, AnySched, Sched}; -use super::message_queue::MessageQueue; use rt::kill::BlockedTask; use rt::deque; use rt::local_ptr; @@ -29,6 +28,7 @@ use iter::range; use unstable::mutex::Mutex; use vec::{OwnedVector}; +use mpsc = super::mpsc_queue; /// A scheduler is responsible for coordinating the execution of Tasks /// on a single thread. The scheduler runs inside a slightly modified @@ -47,7 +47,9 @@ pub struct Scheduler { /// The queue of incoming messages from other schedulers. /// These are enqueued by SchedHandles after which a remote callback /// is triggered to handle the message. - message_queue: MessageQueue, + message_queue: mpsc::Consumer, + /// Producer used to clone sched handles from + message_producer: mpsc::Producer, /// A shared list of sleeping schedulers. We'll use this to wake /// up schedulers when pushing work onto the work queue. sleeper_list: SleeperList, @@ -104,7 +106,7 @@ enum EffortLevel { GiveItYourBest } -static MAX_YIELD_CHECKS: uint = 200; +static MAX_YIELD_CHECKS: uint = 20000; fn reset_yield_check(rng: &mut XorShiftRng) -> uint { let r: uint = Rand::rand(rng); @@ -135,9 +137,11 @@ impl Scheduler { friend: Option) -> Scheduler { + let (consumer, producer) = mpsc::queue(()); let mut sched = Scheduler { sleeper_list: sleeper_list, - message_queue: MessageQueue::new(), + message_queue: consumer, + message_producer: producer, sleepy: false, no_sleep: false, event_loop: event_loop, @@ -218,7 +222,7 @@ impl Scheduler { // Should not have any messages let message = stask.sched.get_mut_ref().message_queue.pop(); - rtassert!(message.is_none()); + rtassert!(match message { mpsc::Empty => true, _ => false }); stask.destroyed = true; } @@ -315,10 +319,27 @@ impl Scheduler { fn interpret_message_queue(mut ~self, effort: EffortLevel) -> Option<~Scheduler> { let msg = if effort == DontTryTooHard { - // Do a cheap check that may miss messages self.message_queue.casual_pop() } else { - self.message_queue.pop() + // When popping our message queue, we could see an "inconsistent" + // state which means that we *should* be able to pop data, but we + // are unable to at this time. Our options are: + // + // 1. Spin waiting for data + // 2. Ignore this and pretend we didn't find a message + // + // If we choose route 1, then if the pusher in question is currently + // pre-empted, we're going to take up our entire time slice just + // spinning on this queue. If we choose route 2, then the pusher in + // question is still guaranteed to make a send() on its async + // handle, so we will guaranteed wake up and see its message at some + // point. + // + // I have chosen to take route #2. + match self.message_queue.pop() { + mpsc::Data(t) => Some(t), + mpsc::Empty | mpsc::Inconsistent => None + } }; match msg { @@ -793,7 +814,7 @@ impl Scheduler { return SchedHandle { remote: remote, - queue: self.message_queue.clone(), + queue: self.message_producer.clone(), sched_id: self.sched_id() }; } @@ -813,7 +834,7 @@ pub enum SchedMessage { pub struct SchedHandle { priv remote: ~RemoteCallback, - priv queue: MessageQueue, + priv queue: mpsc::Producer, sched_id: uint } @@ -915,17 +936,17 @@ fn new_sched_rng() -> XorShiftRng { #[cfg(test)] mod test { use prelude::*; - use rt::test::*; - use unstable::run_in_bare_thread; + use borrow::to_uint; - use rt::sched::{Scheduler}; use rt::deque::BufferPool; - use rt::thread::Thread; - use rt::task::{Task, Sched}; use rt::basic; + use rt::sched::{Scheduler}; + use rt::task::{Task, Sched}; + use rt::test::*; + use rt::thread::Thread; use rt::util; - use option::{Some}; - use rt::task::UnwindResult; + use task::TaskResult; + use unstable::run_in_bare_thread; #[test] fn trivial_run_in_newsched_task_test() { @@ -1010,8 +1031,8 @@ mod test { assert!(Task::on_appropriate_sched()); }; - let on_exit: proc(UnwindResult) = proc(exit_status) { - rtassert!(exit_status.is_success()) + let on_exit: proc(TaskResult) = proc(exit_status) { + rtassert!(exit_status.is_ok()) }; task.death.on_exit = Some(on_exit); @@ -1027,7 +1048,6 @@ mod test { use rt::sleeper_list::SleeperList; use rt::sched::Shutdown; use borrow; - use rt::comm::*; do run_in_bare_thread { @@ -1089,7 +1109,7 @@ mod test { rtdebug!("task4 id: **{}**", borrow::to_uint(task4)); // Signal from the special task that we are done. - let (port, chan) = oneshot::<()>(); + let (port, chan) = Chan::<()>::new(); let normal_task = ~do Task::new_root(&mut normal_sched.stack_pool, None) { rtdebug!("*about to submit task2*"); @@ -1160,10 +1180,8 @@ mod test { #[test] fn handle() { - use rt::comm::*; - do run_in_bare_thread { - let (port, chan) = oneshot::<()>(); + let (port, chan) = Chan::new(); let thread_one = do Thread::start { let chan = chan; @@ -1230,7 +1248,6 @@ mod test { #[test] fn multithreading() { - use rt::comm::*; use num::Times; use vec::OwnedVector; use container::Container; @@ -1238,7 +1255,7 @@ mod test { do run_in_mt_newsched_task { let mut ports = ~[]; 10.times(|| { - let (port, chan) = oneshot(); + let (port, chan) = Chan::new(); do spawntask_later { chan.send(()); } @@ -1253,21 +1270,17 @@ mod test { #[test] fn thread_ring() { - use rt::comm::*; - use comm::{GenericPort, GenericChan}; - do run_in_mt_newsched_task { - let (end_port, end_chan) = oneshot(); + let (end_port, end_chan) = Chan::new(); let n_tasks = 10; let token = 2000; - let (p, ch1) = stream(); - let mut p = p; + let (mut p, ch1) = Chan::new(); ch1.send((token, end_chan)); let mut i = 2; while i <= n_tasks { - let (next_p, ch) = stream(); + let (next_p, ch) = Chan::new(); let imm_i = i; let imm_p = p; do spawntask_random { @@ -1276,23 +1289,23 @@ mod test { p = next_p; i += 1; } - let imm_p = p; - let imm_ch = ch1; + let p = p; do spawntask_random { - roundtrip(1, n_tasks, &imm_p, &imm_ch); + roundtrip(1, n_tasks, &p, &ch1); } end_port.recv(); } fn roundtrip(id: int, n_tasks: int, - p: &Port<(int, ChanOne<()>)>, ch: &Chan<(int, ChanOne<()>)>) { + p: &Port<(int, Chan<()>)>, + ch: &Chan<(int, Chan<()>)>) { while (true) { match p.recv() { (1, end_chan) => { - debug!("{}\n", id); - end_chan.send(()); - return; + debug!("{}\n", id); + end_chan.send(()); + return; } (token, end_chan) => { debug!("thread: {} got token: {}", id, token); @@ -1331,16 +1344,14 @@ mod test { // FIXME: #9407: xfail-test fn dont_starve_1() { - use rt::comm::oneshot; - stress_factor().times(|| { do run_in_mt_newsched_task { - let (port, chan) = oneshot(); + let (port, chan) = Chan::new(); // This task should not be able to starve the sender; // The sender should get stolen to another thread. do spawntask { - while !port.peek() { } + while port.try_recv().is_none() { } } chan.send(()); @@ -1350,17 +1361,15 @@ mod test { #[test] fn dont_starve_2() { - use rt::comm::oneshot; - stress_factor().times(|| { do run_in_newsched_task { - let (port, chan) = oneshot(); - let (_port2, chan2) = stream(); + let (port, chan) = Chan::new(); + let (_port2, chan2) = Chan::new(); // This task should not be able to starve the other task. // The sends should eventually yield. do spawntask { - while !port.peek() { + while port.try_recv().is_none() { chan2.send(()); } } diff --git a/src/libstd/rt/select.rs b/src/libstd/rt/select.rs deleted file mode 100644 index 6cde0a1f2169f..0000000000000 --- a/src/libstd/rt/select.rs +++ /dev/null @@ -1,29 +0,0 @@ -// Copyright 2013 The Rust Project Developers. See the COPYRIGHT -// file at the top-level directory of this distribution and at -// http://rust-lang.org/COPYRIGHT. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -//! Module for private, abstraction-leaking select traits. Wrapped in std::select. - -use rt::kill::BlockedTask; -use rt::sched::Scheduler; -use option::Option; - -pub trait SelectInner { - // Returns true if data was available. - fn optimistic_check(&mut self) -> bool; - // Returns true if data was available. If so, shall also wake() the task. - fn block_on(&mut self, &mut Scheduler, BlockedTask) -> bool; - // Returns true if data was available. - fn unblock_from(&mut self) -> bool; -} - -pub trait SelectPortInner { - fn recv_ready(self) -> Option; -} - diff --git a/src/libstd/rt/spsc_queue.rs b/src/libstd/rt/spsc_queue.rs new file mode 100644 index 0000000000000..f14533d726a78 --- /dev/null +++ b/src/libstd/rt/spsc_queue.rs @@ -0,0 +1,296 @@ +/* Copyright (c) 2010-2011 Dmitry Vyukov. All rights reserved. + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY DMITRY VYUKOV "AS IS" AND ANY EXPRESS OR IMPLIED + * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT + * SHALL DMITRY VYUKOV OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE + * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * The views and conclusions contained in the software and documentation are + * those of the authors and should not be interpreted as representing official + * policies, either expressed or implied, of Dmitry Vyukov. + */ + +// http://www.1024cores.net/home/lock-free-algorithms/queues/unbounded-spsc-queue +use cast; +use kinds::Send; +use ops::Drop; +use option::{Some, None, Option}; +use unstable::atomics::{AtomicPtr, Relaxed, AtomicUint, Acquire, Release}; +use unstable::sync::UnsafeArc; + +// Node within the linked list queue of messages to send +struct Node { + // XXX: this could be an uninitialized T if we're careful enough, and + // that would reduce memory usage (and be a bit faster). + // is it worth it? + value: Option, // nullable for re-use of nodes + next: AtomicPtr>, // next node in the queue +} + +// The producer/consumer halves both need access to the `tail` field, and if +// they both have access to that we may as well just give them both access +// to this whole structure. +struct State { + // consumer fields + tail: *mut Node, // where to pop from + tail_prev: AtomicPtr>, // where to pop from + + // producer fields + head: *mut Node, // where to push to + first: *mut Node, // where to get new nodes from + tail_copy: *mut Node, // between first/tail + + // Cache maintenance fields. Additions and subtractions are stored + // separately in order to allow them to use nonatomic addition/subtraction. + cache_bound: uint, + cache_additions: AtomicUint, + cache_subtractions: AtomicUint, + + packet: P, +} + +pub struct Producer { + priv state: UnsafeArc>, +} + +pub struct Consumer { + priv state: UnsafeArc>, +} + +pub fn queue(bound: uint, + p: P) -> (Consumer, Producer) +{ + let n1 = Node::new(); + let n2 = Node::new(); + unsafe { (*n1).next.store(n2, Relaxed) } + let state = State { + tail: n2, + tail_prev: AtomicPtr::new(n1), + head: n2, + first: n1, + tail_copy: n1, + cache_bound: bound, + cache_additions: AtomicUint::new(0), + cache_subtractions: AtomicUint::new(0), + packet: p, + }; + let (arc1, arc2) = UnsafeArc::new2(state); + (Consumer { state: arc1 }, Producer { state: arc2 }) +} + +impl Node { + fn new() -> *mut Node { + unsafe { + cast::transmute(~Node { + value: None, + next: AtomicPtr::new(0 as *mut Node), + }) + } + } +} + +impl Producer { + pub fn push(&mut self, t: T) { + unsafe { (*self.state.get()).push(t) } + } + pub fn is_empty(&self) -> bool { + unsafe { (*self.state.get()).is_empty() } + } + pub unsafe fn packet(&self) -> *mut P { + &mut (*self.state.get()).packet as *mut P + } +} + +impl Consumer { + pub fn pop(&mut self) -> Option { + unsafe { (*self.state.get()).pop() } + } + pub unsafe fn packet(&self) -> *mut P { + &mut (*self.state.get()).packet as *mut P + } +} + +impl State { + // remember that there is only one thread executing `push` (and only one + // thread executing `pop`) + unsafe fn push(&mut self, t: T) { + // Acquire a node (which either uses a cached one or allocates a new + // one), and then append this to the 'head' node. + let n = self.alloc(); + assert!((*n).value.is_none()); + (*n).value = Some(t); + (*n).next.store(0 as *mut Node, Relaxed); + (*self.head).next.store(n, Release); + self.head = n; + } + + unsafe fn alloc(&mut self) -> *mut Node { + // First try to see if we can consume the 'first' node for our uses. + // We try to avoid as many atomic instructions as possible here, so + // the addition to cache_subtractions is not atomic (plus we're the + // only one subtracting from the cache). + if self.first != self.tail_copy { + if self.cache_bound > 0 { + let b = self.cache_subtractions.load(Relaxed); + self.cache_subtractions.store(b + 1, Relaxed); + } + let ret = self.first; + self.first = (*ret).next.load(Relaxed); + return ret; + } + // If the above fails, then update our copy of the tail and try + // again. + self.tail_copy = self.tail_prev.load(Acquire); + if self.first != self.tail_copy { + if self.cache_bound > 0 { + let b = self.cache_subtractions.load(Relaxed); + self.cache_subtractions.store(b + 1, Relaxed); + } + let ret = self.first; + self.first = (*ret).next.load(Relaxed); + return ret; + } + // If all of that fails, then we have to allocate a new node + // (there's nothing in the node cache). + Node::new() + } + + // remember that there is only one thread executing `pop` (and only one + // thread executing `push`) + unsafe fn pop(&mut self) -> Option { + // The `tail` node is not actually a used node, but rather a + // sentinel from where we should start popping from. Hence, look at + // tail's next field and see if we can use it. If we do a pop, then + // the current tail node is a candidate for going into the cache. + let tail = self.tail; + let next = (*tail).next.load(Acquire); + if next.is_null() { return None } + assert!((*next).value.is_some()); + let ret = (*next).value.take(); + + self.tail = next; + if self.cache_bound == 0 { + self.tail_prev.store(tail, Release); + } else { + // XXX: this is dubious with overflow. + let additions = self.cache_additions.load(Relaxed); + let subtractions = self.cache_subtractions.load(Relaxed); + let size = additions - subtractions; + + if size < self.cache_bound { + self.tail_prev.store(tail, Release); + self.cache_additions.store(additions + 1, Relaxed); + } else { + (*self.tail_prev.load(Relaxed)).next.store(next, Relaxed); + // We have successfully erased all references to 'tail', so + // now we can safely drop it. + let _: ~Node = cast::transmute(tail); + } + } + return ret; + } + + unsafe fn is_empty(&self) -> bool { + let tail = self.tail; + let next = (*tail).next.load(Acquire); + return next.is_null(); + } +} + +#[unsafe_destructor] +impl Drop for State { + fn drop(&mut self) { + unsafe { + let mut cur = self.first; + while !cur.is_null() { + let next = (*cur).next.load(Relaxed); + let _n: ~Node = cast::transmute(cur); + cur = next; + } + } + } +} + +#[cfg(test)] +mod test { + use prelude::*; + use super::queue; + use task; + + #[test] + fn smoke() { + let (mut c, mut p) = queue(0, ()); + p.push(1); + p.push(2); + assert_eq!(c.pop(), Some(1)); + assert_eq!(c.pop(), Some(2)); + assert_eq!(c.pop(), None); + p.push(3); + p.push(4); + assert_eq!(c.pop(), Some(3)); + assert_eq!(c.pop(), Some(4)); + assert_eq!(c.pop(), None); + } + + #[test] + fn drop_full() { + let (_, mut p) = queue(0, ()); + p.push(~1); + p.push(~2); + } + + #[test] + fn smoke_bound() { + let (mut c, mut p) = queue(1, ()); + p.push(1); + p.push(2); + assert_eq!(c.pop(), Some(1)); + assert_eq!(c.pop(), Some(2)); + assert_eq!(c.pop(), None); + p.push(3); + p.push(4); + assert_eq!(c.pop(), Some(3)); + assert_eq!(c.pop(), Some(4)); + assert_eq!(c.pop(), None); + } + + #[test] + fn stress() { + stress_bound(0); + stress_bound(1); + + fn stress_bound(bound: uint) { + let (c, mut p) = queue(bound, ()); + do task::spawn_sched(task::SingleThreaded) { + let mut c = c; + for _ in range(0, 100000) { + loop { + match c.pop() { + Some(1) => break, + Some(_) => fail!(), + None => {} + } + } + } + } + for _ in range(0, 100000) { + p.push(1); + } + } + } +} diff --git a/src/libstd/rt/task.rs b/src/libstd/rt/task.rs index 86cc895eb27db..62e012f9f4120 100644 --- a/src/libstd/rt/task.rs +++ b/src/libstd/rt/task.rs @@ -20,21 +20,22 @@ use prelude::*; use borrow; use cast::transmute; use cleanup; +use io::Writer; use libc::{c_void, uintptr_t, c_char, size_t}; use local_data; use option::{Option, Some, None}; use rt::borrowck::BorrowRecord; use rt::borrowck; -use rt::context::Context; use rt::context; +use rt::context::Context; use rt::env; -use io::Writer; use rt::kill::Death; use rt::local::Local; use rt::logging::StdErrLogger; use rt::sched::{Scheduler, SchedHandle}; use rt::stack::{StackSegment, StackPool}; use send_str::SendStr; +use task::TaskResult; use unstable::finally::Finally; use unstable::mutex::Mutex; @@ -91,46 +92,17 @@ pub enum SchedHome { pub struct GarbageCollector; pub struct LocalStorage(Option); -/// Represents the reason for the current unwinding process -pub enum UnwindResult { - /// The task is ending successfully - Success, - - /// The Task is failing with reason `~Any` - Failure(~Any), -} - -impl UnwindResult { - /// Returns `true` if this `UnwindResult` is a failure - #[inline] - pub fn is_failure(&self) -> bool { - match *self { - Success => false, - Failure(_) => true - } - } - - /// Returns `true` if this `UnwindResult` is a success - #[inline] - pub fn is_success(&self) -> bool { - match *self { - Success => true, - Failure(_) => false - } - } -} - pub struct Unwinder { unwinding: bool, cause: Option<~Any> } impl Unwinder { - fn to_unwind_result(&mut self) -> UnwindResult { + fn result(&mut self) -> TaskResult { if self.unwinding { - Failure(self.cause.take().unwrap()) + Err(self.cause.take().unwrap()) } else { - Success + Ok(()) } } } @@ -327,7 +299,7 @@ impl Task { // Cleanup the dynamic borrowck debugging info borrowck::clear_task_borrow_list(); - self.death.collect_failure(self.unwinder.to_unwind_result()); + self.death.collect_failure(self.unwinder.result()); self.destroyed = true; } @@ -692,6 +664,7 @@ pub fn begin_unwind(msg: M, file: &'static str, line: uint) -> ! mod test { use super::*; use rt::test::*; + use prelude::*; #[test] fn local_heap() { @@ -744,23 +717,10 @@ mod test { } } - #[test] - fn comm_oneshot() { - use comm::*; - - do run_in_newsched_task { - let (port, chan) = oneshot(); - chan.send(10); - assert!(port.recv() == 10); - } - } - #[test] fn comm_stream() { - use comm::*; - do run_in_newsched_task() { - let (port, chan) = stream(); + let (port, chan) = Chan::new(); chan.send(10); assert!(port.recv() == 10); } @@ -768,11 +728,8 @@ mod test { #[test] fn comm_shared_chan() { - use comm::*; - do run_in_newsched_task() { - let (port, chan) = stream(); - let chan = SharedChan::new(chan); + let (port, chan) = SharedChan::new(); chan.send(10); assert!(port.recv() == 10); } diff --git a/src/libstd/rt/test.rs b/src/libstd/rt/test.rs index 96b80d1112915..2b48b396c99e9 100644 --- a/src/libstd/rt/test.rs +++ b/src/libstd/rt/test.rs @@ -21,14 +21,14 @@ use rand::Rng; use rand; use result::{Result, Ok, Err}; use rt::basic; -use rt::comm::oneshot; use rt::deque::BufferPool; +use comm::Chan; use rt::new_event_loop; use rt::sched::Scheduler; use rt::sleeper_list::SleeperList; use rt::task::Task; -use rt::task::UnwindResult; use rt::thread::Thread; +use task::TaskResult; use unstable::{run_in_bare_thread}; use vec; use vec::{OwnedVector, MutableVector, ImmutableVector}; @@ -82,10 +82,10 @@ pub fn run_in_uv_task_core(f: proc()) { let mut sched = ~new_test_uv_sched(); let exit_handle = sched.make_handle(); - let on_exit: proc(UnwindResult) = proc(exit_status: UnwindResult) { + let on_exit: proc(TaskResult) = proc(exit_status: TaskResult) { let mut exit_handle = exit_handle; exit_handle.send(Shutdown); - rtassert!(exit_status.is_success()); + rtassert!(exit_status.is_ok()); }; let mut task = ~Task::new_root(&mut sched.stack_pool, None, f); task.death.on_exit = Some(on_exit); @@ -99,10 +99,10 @@ pub fn run_in_newsched_task_core(f: proc()) { let mut sched = ~new_test_sched(); let exit_handle = sched.make_handle(); - let on_exit: proc(UnwindResult) = proc(exit_status: UnwindResult) { + let on_exit: proc(TaskResult) = proc(exit_status: TaskResult) { let mut exit_handle = exit_handle; exit_handle.send(Shutdown); - rtassert!(exit_status.is_success()); + rtassert!(exit_status.is_ok()); }; let mut task = ~Task::new_root(&mut sched.stack_pool, None, f); task.death.on_exit = Some(on_exit); @@ -240,14 +240,14 @@ pub fn run_in_mt_newsched_task(f: proc()) { } let handles = handles; // Work around not being able to capture mut - let on_exit: proc(UnwindResult) = proc(exit_status: UnwindResult) { + let on_exit: proc(TaskResult) = proc(exit_status: TaskResult) { // Tell schedulers to exit let mut handles = handles; for handle in handles.mut_iter() { handle.send(Shutdown); } - rtassert!(exit_status.is_success()); + rtassert!(exit_status.is_ok()); }; let mut main_task = ~Task::new_root(&mut scheds[0].stack_pool, None, @@ -311,8 +311,8 @@ pub fn spawntask_random(f: proc()) { pub fn spawntask_try(f: proc()) -> Result<(),()> { - let (port, chan) = oneshot(); - let on_exit: proc(UnwindResult) = proc(exit_status) { + let (port, chan) = Chan::new(); + let on_exit: proc(TaskResult) = proc(exit_status) { chan.send(exit_status) }; @@ -322,7 +322,7 @@ pub fn spawntask_try(f: proc()) -> Result<(),()> { Scheduler::run_task(new_task); let exit_status = port.recv(); - if exit_status.is_success() { Ok(()) } else { Err(()) } + if exit_status.is_ok() { Ok(()) } else { Err(()) } } diff --git a/src/libstd/rt/thread.rs b/src/libstd/rt/thread.rs index 9031147f8b139..6128f310a2ebf 100644 --- a/src/libstd/rt/thread.rs +++ b/src/libstd/rt/thread.rs @@ -21,42 +21,32 @@ use kinds::Send; use libc; use ops::Drop; use option::{Option, Some, None}; -use ptr; use uint; -#[cfg(windows)] -use libc::types::os::arch::extra::{LPSECURITY_ATTRIBUTES, SIZE_T, - LPVOID, DWORD, LPDWORD, HANDLE}; - -#[cfg(windows)] type rust_thread = HANDLE; -#[cfg(unix)] type rust_thread = libc::pthread_t; -#[cfg(windows)] type rust_thread_return = DWORD; -#[cfg(unix)] type rust_thread_return = *libc::c_void; - -type StartFn = extern "C" fn(*libc::c_void) -> rust_thread_return; +type StartFn = extern "C" fn(*libc::c_void) -> imp::rust_thread_return; /// This struct represents a native thread's state. This is used to join on an /// existing thread created in the join-able state. pub struct Thread { - priv native: rust_thread, + priv native: imp::rust_thread, priv joined: bool, priv packet: ~Option, } -static DEFAULT_STACK_SIZE: libc::size_t = 1024*1024; +static DEFAULT_STACK_SIZE: libc::size_t = 1024 * 1024; // This is the starting point of rust os threads. The first thing we do // is make sure that we don't trigger __morestack (also why this has a // no_split_stack annotation), and then we extract the main function // and invoke it. #[no_split_stack] -extern fn thread_start(main: *libc::c_void) -> rust_thread_return { +extern fn thread_start(main: *libc::c_void) -> imp::rust_thread_return { use rt::context; unsafe { context::record_stack_bounds(0, uint::max_value); let f: ~proc() = cast::transmute(main); (*f)(); - cast::transmute(0 as rust_thread_return) + cast::transmute(0 as imp::rust_thread_return) } } @@ -88,7 +78,7 @@ impl Thread<()> { *cast::transmute::<&~Option, **mut Option>(&packet) }; let main: proc() = proc() unsafe { *packet2 = Some(main()); }; - let native = unsafe { native_thread_create(~main) }; + let native = unsafe { imp::create(~main) }; Thread { native: native, @@ -105,10 +95,16 @@ impl Thread<()> { /// there are detached thread still running around. pub fn spawn(main: proc()) { unsafe { - let handle = native_thread_create(~main); - native_thread_detach(handle); + let handle = imp::create(~main); + imp::detach(handle); } } + + /// Relinquishes the CPU slot that this OS-thread is currently using, + /// allowing another thread to run for awhile. + pub fn yield_now() { + unsafe { imp::yield_now(); } + } } impl Thread { @@ -116,7 +112,7 @@ impl Thread { /// calculation. pub fn join(mut self) -> T { assert!(!self.joined); - unsafe { native_thread_join(self.native) }; + unsafe { imp::join(self.native) }; self.joined = true; assert!(self.packet.is_some()); self.packet.take_unwrap() @@ -129,80 +125,119 @@ impl Drop for Thread { // This is required for correctness. If this is not done then the thread // would fill in a return box which no longer exists. if !self.joined { - unsafe { native_thread_join(self.native) }; + unsafe { imp::join(self.native) }; } } } #[cfg(windows)] -unsafe fn native_thread_create(p: ~proc()) -> rust_thread { - let arg: *mut libc::c_void = cast::transmute(p); - CreateThread(ptr::mut_null(), DEFAULT_STACK_SIZE, thread_start, - arg, 0, ptr::mut_null()) -} - -#[cfg(windows)] -unsafe fn native_thread_join(native: rust_thread) { - use libc::consts::os::extra::INFINITE; - WaitForSingleObject(native, INFINITE); -} +mod imp { + use super::DEFAULT_STACK_SIZE; + + use cast; + use libc; + use libc::types::os::arch::extra::{LPSECURITY_ATTRIBUTES, SIZE_T, BOOL, + LPVOID, DWORD, LPDWORD, HANDLE}; + use ptr; + + pub type rust_thread = HANDLE; + pub type rust_thread_return = DWORD; + + pub unsafe fn create(p: ~proc()) -> rust_thread { + let arg: *mut libc::c_void = cast::transmute(p); + CreateThread(ptr::mut_null(), DEFAULT_STACK_SIZE, super::thread_start, + arg, 0, ptr::mut_null()) + } -#[cfg(windows)] -unsafe fn native_thread_detach(native: rust_thread) { - assert!(libc::CloseHandle(native) != 0); -} + pub unsafe fn join(native: rust_thread) { + use libc::consts::os::extra::INFINITE; + WaitForSingleObject(native, INFINITE); + } -#[cfg(unix)] -unsafe fn native_thread_create(p: ~proc()) -> rust_thread { - use unstable::intrinsics; - use libc::consts::os::posix01::PTHREAD_CREATE_JOINABLE; + pub unsafe fn detach(native: rust_thread) { + assert!(libc::CloseHandle(native) != 0); + } - let mut native: libc::pthread_t = intrinsics::uninit(); - let mut attr: libc::pthread_attr_t = intrinsics::uninit(); - assert_eq!(pthread_attr_init(&mut attr), 0); - assert_eq!(pthread_attr_setstacksize(&mut attr, DEFAULT_STACK_SIZE), 0); - assert_eq!(pthread_attr_setdetachstate(&mut attr, PTHREAD_CREATE_JOINABLE), 0); + pub unsafe fn yield_now() { + // This function will return 0 if there are no other threads to execute, + // but this also means that the yield was useless so this isn't really a + // case that needs to be worried about. + SwitchToThread(); + } - let arg: *libc::c_void = cast::transmute(p); - assert_eq!(pthread_create(&mut native, &attr, thread_start, arg), 0); - native + extern "system" { + fn CreateThread(lpThreadAttributes: LPSECURITY_ATTRIBUTES, + dwStackSize: SIZE_T, + lpStartAddress: super::StartFn, + lpParameter: LPVOID, + dwCreationFlags: DWORD, + lpThreadId: LPDWORD) -> HANDLE; + fn WaitForSingleObject(hHandle: HANDLE, dwMilliseconds: DWORD) -> DWORD; + fn SwitchToThread() -> BOOL; + } } #[cfg(unix)] -unsafe fn native_thread_join(native: rust_thread) { - assert_eq!(pthread_join(native, ptr::null()), 0); -} +mod imp { + use cast; + use libc::consts::os::posix01::PTHREAD_CREATE_JOINABLE; + use libc; + use ptr; + use super::DEFAULT_STACK_SIZE; + use unstable::intrinsics; -#[cfg(unix)] -fn native_thread_detach(native: rust_thread) { - unsafe { assert_eq!(pthread_detach(native), 0) } -} + pub type rust_thread = libc::pthread_t; + pub type rust_thread_return = *libc::c_void; + + pub unsafe fn create(p: ~proc()) -> rust_thread { + let mut native: libc::pthread_t = intrinsics::uninit(); + let mut attr: libc::pthread_attr_t = intrinsics::uninit(); + assert_eq!(pthread_attr_init(&mut attr), 0); + assert_eq!(pthread_attr_setstacksize(&mut attr, DEFAULT_STACK_SIZE), 0); + assert_eq!(pthread_attr_setdetachstate(&mut attr, + PTHREAD_CREATE_JOINABLE), 0); + + let arg: *libc::c_void = cast::transmute(p); + assert_eq!(pthread_create(&mut native, &attr, + super::thread_start, arg), 0); + native + } -#[cfg(windows)] -extern "system" { - fn CreateThread(lpThreadAttributes: LPSECURITY_ATTRIBUTES, - dwStackSize: SIZE_T, - lpStartAddress: StartFn, - lpParameter: LPVOID, - dwCreationFlags: DWORD, - lpThreadId: LPDWORD) -> HANDLE; - fn WaitForSingleObject(hHandle: HANDLE, dwMilliseconds: DWORD) -> DWORD; -} + pub unsafe fn join(native: rust_thread) { + assert_eq!(pthread_join(native, ptr::null()), 0); + } -#[cfg(unix)] -extern { - fn pthread_create(native: *mut libc::pthread_t, - attr: *libc::pthread_attr_t, - f: StartFn, - value: *libc::c_void) -> libc::c_int; - fn pthread_join(native: libc::pthread_t, - value: **libc::c_void) -> libc::c_int; - fn pthread_attr_init(attr: *mut libc::pthread_attr_t) -> libc::c_int; - fn pthread_attr_setstacksize(attr: *mut libc::pthread_attr_t, - stack_size: libc::size_t) -> libc::c_int; - fn pthread_attr_setdetachstate(attr: *mut libc::pthread_attr_t, - state: libc::c_int) -> libc::c_int; - fn pthread_detach(thread: libc::pthread_t) -> libc::c_int; + pub unsafe fn detach(native: rust_thread) { + assert_eq!(pthread_detach(native), 0); + } + + #[cfg(target_os = "macos")] + #[cfg(target_os = "android")] + pub unsafe fn yield_now() { assert_eq!(sched_yield(), 0); } + + #[cfg(not(target_os = "macos"), not(target_os = "android"))] + pub unsafe fn yield_now() { assert_eq!(pthread_yield(), 0); } + + extern { + fn pthread_create(native: *mut libc::pthread_t, + attr: *libc::pthread_attr_t, + f: super::StartFn, + value: *libc::c_void) -> libc::c_int; + fn pthread_join(native: libc::pthread_t, + value: **libc::c_void) -> libc::c_int; + fn pthread_attr_init(attr: *mut libc::pthread_attr_t) -> libc::c_int; + fn pthread_attr_setstacksize(attr: *mut libc::pthread_attr_t, + stack_size: libc::size_t) -> libc::c_int; + fn pthread_attr_setdetachstate(attr: *mut libc::pthread_attr_t, + state: libc::c_int) -> libc::c_int; + fn pthread_detach(thread: libc::pthread_t) -> libc::c_int; + + #[cfg(target_os = "macos")] + #[cfg(target_os = "android")] + fn sched_yield() -> libc::c_int; + #[cfg(not(target_os = "macos"), not(target_os = "android"))] + fn pthread_yield() -> libc::c_int; + } } #[cfg(test)] diff --git a/src/libstd/run.rs b/src/libstd/run.rs index 14d49df59a4c6..70ad752ea935b 100644 --- a/src/libstd/run.rs +++ b/src/libstd/run.rs @@ -12,7 +12,7 @@ #[allow(missing_doc)]; -use comm::{stream, SharedChan}; +use comm::SharedChan; use io::Reader; use io::process::ProcessExit; use io::process; @@ -220,8 +220,7 @@ impl Process { // in parallel so we don't deadlock while blocking on one // or the other. FIXME (#2625): Surely there's a much more // clever way to do this. - let (p, ch) = stream(); - let ch = SharedChan::new(ch); + let (p, ch) = SharedChan::new(); let ch_clone = ch.clone(); do spawn { diff --git a/src/libstd/select.rs b/src/libstd/select.rs deleted file mode 100644 index cca64244db569..0000000000000 --- a/src/libstd/select.rs +++ /dev/null @@ -1,306 +0,0 @@ -// Copyright 2013 The Rust Project Developers. See the COPYRIGHT -// file at the top-level directory of this distribution and at -// http://rust-lang.org/COPYRIGHT. -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -#[allow(missing_doc)]; - -use comm; -use container::Container; -use iter::{Iterator, DoubleEndedIterator}; -use kinds::Send; -use ops::Drop; -use option::*; -use rt::local::Local; -use rt::rtio::EventLoop; -use rt::sched::Scheduler; -use rt::shouldnt_be_public::{SelectInner, SelectPortInner}; -use vec::{OwnedVector, MutableVector}; - -/// Trait for message-passing primitives that can be select()ed on. -pub trait Select : SelectInner { } - -/// Trait for message-passing primitives that can use the select2() convenience wrapper. -// (This is separate from the above trait to enable heterogeneous lists of ports -// that implement Select on different types to use select().) -pub trait SelectPort : SelectPortInner { } - -/// A helper type that throws away a value on a port. -struct PortGuard { - port: Option>, -} - -#[unsafe_destructor] -impl Drop for PortGuard { - fn drop(&mut self) { - let _ = self.port.take_unwrap().recv(); - } -} - -/// Receive a message from any one of many ports at once. Returns the index of the -/// port whose data is ready. (If multiple are ready, returns the lowest index.) -pub fn select(ports: &mut [A]) -> uint { - if ports.is_empty() { - fail!("can't select on an empty list"); - } - - for (index, port) in ports.mut_iter().enumerate() { - if port.optimistic_check() { - return index; - } - } - - // If one of the ports already contains data when we go to block on it, we - // don't bother enqueueing on the rest of them, so we shouldn't bother - // unblocking from it either. This is just for efficiency, not correctness. - // (If not, we need to unblock from all of them. Length is a placeholder.) - let mut ready_index = ports.len(); - - // XXX: We're using deschedule...and_then in an unsafe way here (see #8132), - // in that we need to continue mutating the ready_index in the environment - // after letting the task get woken up. The and_then closure needs to delay - // the task from resuming until all ports have become blocked_on. - let (p,c) = comm::oneshot(); - - { - let _guard = PortGuard { - port: Some(p), - }; - - let mut c = Some(c); - let sched: ~Scheduler = Local::take(); - sched.deschedule_running_task_and_then(|sched, task| { - let task_handles = task.make_selectable(ports.len()); - - for (index, (port, task_handle)) in - ports.mut_iter().zip(task_handles.move_iter()).enumerate() { - // If one of the ports has data by now, it will wake the handle. - if port.block_on(sched, task_handle) { - ready_index = index; - break; - } - } - - let c = c.take_unwrap(); - do sched.event_loop.callback { - c.send_deferred(()) - } - }) - } - - // Task resumes. Now unblock ourselves from all the ports we blocked on. - // If the success index wasn't reset, 'take' will just take all of them. - // Iterate in reverse so the 'earliest' index that's ready gets returned. - for (index, port) in ports.mut_slice(0, ready_index).mut_iter().enumerate().invert() { - if port.unblock_from() { - ready_index = index; - } - } - - assert!(ready_index < ports.len()); - return ready_index; -} - -/* FIXME(#5121, #7914) This all should be legal, but rust is not clever enough yet. - -impl <'a> Select for &'a mut Select { - fn optimistic_check(&mut self) -> bool { self.optimistic_check() } - fn block_on(&mut self, sched: &mut Scheduler, task: BlockedTask) -> bool { - self.block_on(sched, task) - } - fn unblock_from(&mut self) -> bool { self.unblock_from() } -} - -pub fn select2, TB, B: SelectPort>(mut a: A, mut b: B) - -> Either<(Option, B), (A, Option)> { - let result = { - let mut ports = [&mut a as &mut Select, &mut b as &mut Select]; - select(ports) - }; - match result { - 0 => Left ((a.recv_ready(), b)), - 1 => Right((a, b.recv_ready())), - x => fail!("impossible case in select2: {:?}", x) - } -} - -*/ - -#[cfg(test)] -mod test { - use super::*; - use clone::Clone; - use num::Times; - use option::*; - use rt::comm::*; - use rt::test::*; - use vec::*; - use comm::GenericChan; - use task; - use iter::{Iterator, range}; - - #[test] #[should_fail] - fn select_doesnt_get_trolled() { - select::>([]); - } - - /* non-blocking select tests */ - - #[cfg(test)] - fn select_helper(num_ports: uint, send_on_chans: &[uint]) { - // Unfortunately this does not actually test the block_on early-break - // codepath in select -- racing between the sender and the receiver in - // separate tasks is necessary to get around the optimistic check. - let (ports, chans) = unzip(range(0, num_ports).map(|_| oneshot::<()>())); - let mut dead_chans = ~[]; - let mut ports = ports; - for (i, chan) in chans.move_iter().enumerate() { - if send_on_chans.contains(&i) { - chan.send(()); - } else { - dead_chans.push(chan); - } - } - let ready_index = select(ports); - assert!(send_on_chans.contains(&ready_index)); - assert!(ports.swap_remove(ready_index).recv_ready().is_some()); - let _ = dead_chans; - - // Same thing with streams instead. - // FIXME(#7971): This should be in a macro but borrowck isn't smart enough. - let (ports, chans) = unzip(range(0, num_ports).map(|_| stream::<()>())); - let mut dead_chans = ~[]; - let mut ports = ports; - for (i, chan) in chans.move_iter().enumerate() { - if send_on_chans.contains(&i) { - chan.send(()); - } else { - dead_chans.push(chan); - } - } - let ready_index = select(ports); - assert!(send_on_chans.contains(&ready_index)); - assert!(ports.swap_remove(ready_index).recv_ready().is_some()); - let _ = dead_chans; - } - - #[test] - fn select_one() { - do run_in_uv_task { select_helper(1, [0]) } - } - - #[test] - fn select_two() { - // NB. I would like to have a test that tests the first one that is - // ready is the one that's returned, but that can't be reliably tested - // with the randomized behaviour of optimistic_check. - do run_in_uv_task { select_helper(2, [1]) } - do run_in_uv_task { select_helper(2, [0]) } - do run_in_uv_task { select_helper(2, [1,0]) } - } - - #[test] - fn select_a_lot() { - do run_in_uv_task { select_helper(12, [7,8,9]) } - } - - #[test] - fn select_stream() { - use util; - use comm::GenericChan; - - // Sends 10 buffered packets, and uses select to retrieve them all. - // Puts the port in a different spot in the vector each time. - do run_in_uv_task { - let (ports, _) = unzip(range(0u, 10).map(|_| stream::())); - let (port, chan) = stream(); - 10.times(|| { chan.send(31337); }); - let mut ports = ports; - let mut port = Some(port); - let order = [5u,0,4,3,2,6,9,8,7,1]; - for &index in order.iter() { - // put the port in the vector at any index - util::swap(port.get_mut_ref(), &mut ports[index]); - assert!(select(ports) == index); - // get it back out - util::swap(port.get_mut_ref(), &mut ports[index]); - // NB. Not recv(), because optimistic_check randomly fails. - assert!(port.get_ref().recv_ready().unwrap() == 31337); - } - } - } - - #[test] - fn select_simple() { - do run_in_uv_task { - select_helper(2, [1]) - } - } - - /* blocking select tests */ - - #[test] - fn select_blocking() { - do run_in_uv_task { - let (p1,_c) = oneshot(); - let (p2,c2) = oneshot(); - let mut ports = [p1,p2]; - - let (p3,c3) = oneshot(); - let (p4,c4) = oneshot(); - - do task::spawn { - p3.recv(); // handshake parent - c4.send(()); // normal receive - task::deschedule(); - c2.send(()); // select receive - } - - // Try to block before child sends on c2. - c3.send(()); - p4.recv(); - assert!(select(ports) == 1); - } - } - - #[test] - fn select_racing_senders() { - static NUM_CHANS: uint = 10; - - select_racing_senders_helper(~[0,1,2,3,4,5,6,7,8,9]); - select_racing_senders_helper(~[0,1,2]); - select_racing_senders_helper(~[3,4,5,6]); - select_racing_senders_helper(~[7,8,9]); - - fn select_racing_senders_helper(send_on_chans: ~[uint]) { - use rt::test::spawntask_random; - - do run_in_uv_task { - // A bit of stress, since ordinarily this is just smoke and mirrors. - 4.times(|| { - let send_on_chans = send_on_chans.clone(); - do task::spawn { - let mut ports = ~[]; - for i in range(0u, NUM_CHANS) { - let (p,c) = oneshot(); - ports.push(p); - if send_on_chans.contains(&i) { - do spawntask_random { - task::deschedule(); - c.send(()); - } - } - } - // nondeterministic result, but should succeed - select(ports); - } - }) - } - } - } -} diff --git a/src/libstd/task/mod.rs b/src/libstd/task/mod.rs index 24a24f2481887..0e56f42f5b9c0 100644 --- a/src/libstd/task/mod.rs +++ b/src/libstd/task/mod.rs @@ -55,11 +55,10 @@ use prelude::*; -use comm::{stream, Chan, GenericChan, GenericPort, Port, Peekable}; +use comm::{Chan, Port}; use result::{Result, Ok, Err}; use rt::in_green_task_context; use rt::local::Local; -use rt::task::{UnwindResult, Success, Failure}; use send_str::{SendStr, IntoSendStr}; use util; @@ -81,33 +80,6 @@ pub mod spawn; /// children tasks complete, recommend using a result future. pub type TaskResult = Result<(), ~Any>; -pub struct TaskResultPort { - priv port: Port -} - -fn to_task_result(res: UnwindResult) -> TaskResult { - match res { - Success => Ok(()), Failure(a) => Err(a), - } -} - -impl GenericPort for TaskResultPort { - #[inline] - fn recv(&self) -> TaskResult { - to_task_result(self.port.recv()) - } - - #[inline] - fn try_recv(&self) -> Option { - self.port.try_recv().map(to_task_result) - } -} - -impl Peekable for TaskResultPort { - #[inline] - fn peek(&self) -> bool { self.port.peek() } -} - /// Scheduler modes #[deriving(Eq)] pub enum SchedMode { @@ -150,7 +122,7 @@ pub struct SchedOpts { */ pub struct TaskOpts { priv watched: bool, - priv notify_chan: Option>, + priv notify_chan: Option>, name: Option, sched: SchedOpts, stack_size: Option @@ -232,7 +204,7 @@ impl TaskBuilder { /// /// # Failure /// Fails if a future_result was already set for this task. - pub fn future_result(&mut self) -> TaskResultPort { + pub fn future_result(&mut self) -> Port { // FIXME (#3725): Once linked failure and notification are // handled in the library, I can imagine implementing this by just // registering an arbitrary number of task::on_exit handlers and @@ -243,12 +215,12 @@ impl TaskBuilder { } // Construct the future and give it to the caller. - let (notify_pipe_po, notify_pipe_ch) = stream::(); + let (notify_pipe_po, notify_pipe_ch) = Chan::new(); // Reconfigure self to use a notify channel. self.opts.notify_chan = Some(notify_pipe_ch); - TaskResultPort { port: notify_pipe_po } + notify_pipe_po } /// Name the task-to-be. Currently the name is used for identification @@ -341,7 +313,7 @@ impl TaskBuilder { * Fails if a future_result was already set for this task. */ pub fn try(mut self, f: proc() -> T) -> Result { - let (po, ch) = stream::(); + let (po, ch) = Chan::new(); let result = self.future_result(); @@ -466,7 +438,7 @@ pub fn failing() -> bool { // !!! instead of exiting cleanly. This might wedge the buildbots. !!! #[cfg(test)] -fn block_forever() { let (po, _ch) = stream::<()>(); po.recv(); } +fn block_forever() { let (po, _ch) = Chan::<()>::new(); po.recv(); } #[test] fn test_unnamed_task() { @@ -528,9 +500,8 @@ fn test_send_named_task() { #[test] fn test_run_basic() { - let (po, ch) = stream::<()>(); - let builder = task(); - do builder.spawn { + let (po, ch) = Chan::new(); + do task().spawn { ch.send(()); } po.recv(); @@ -543,7 +514,7 @@ struct Wrapper { #[test] fn test_add_wrapper() { - let (po, ch) = stream::<()>(); + let (po, ch) = Chan::new(); let mut b0 = task(); do b0.add_wrapper |body| { let ch = ch; @@ -608,8 +579,7 @@ fn get_sched_id() -> int { #[test] fn test_spawn_sched() { - let (po, ch) = stream::<()>(); - let ch = SharedChan::new(ch); + let (po, ch) = SharedChan::new(); fn f(i: int, ch: SharedChan<()>) { let parent_sched_id = get_sched_id(); @@ -632,14 +602,14 @@ fn test_spawn_sched() { #[test] fn test_spawn_sched_childs_on_default_sched() { - let (po, ch) = stream(); + let (po, ch) = Chan::new(); // Assuming tests run on the default scheduler let default_id = get_sched_id(); do spawn_sched(SingleThreaded) { - let parent_sched_id = get_sched_id(); let ch = ch; + let parent_sched_id = get_sched_id(); do spawn { let child_sched_id = get_sched_id(); assert!(parent_sched_id != child_sched_id); @@ -660,8 +630,8 @@ fn test_spawn_sched_blocking() { // Testing that a task in one scheduler can block in foreign code // without affecting other schedulers 20u.times(|| { - let (start_po, start_ch) = stream(); - let (fin_po, fin_ch) = stream(); + let (start_po, start_ch) = Chan::new(); + let (fin_po, fin_ch) = Chan::new(); let mut lock = Mutex::new(); let lock2 = lock.clone(); @@ -686,14 +656,14 @@ fn test_spawn_sched_blocking() { let mut val = 20; while val > 0 { val = po.recv(); - ch.send(val - 1); + ch.try_send(val - 1); } } - let (setup_po, setup_ch) = stream(); - let (parent_po, parent_ch) = stream(); + let (setup_po, setup_ch) = Chan::new(); + let (parent_po, parent_ch) = Chan::new(); do spawn { - let (child_po, child_ch) = stream(); + let (child_po, child_ch) = Chan::new(); setup_ch.send(child_ch); pingpong(&child_po, &parent_ch); }; @@ -712,12 +682,12 @@ fn test_spawn_sched_blocking() { #[cfg(test)] fn avoid_copying_the_body(spawnfn: |v: proc()|) { - let (p, ch) = stream::(); + let (p, ch) = Chan::::new(); let x = ~1; let x_in_parent = ptr::to_unsafe_ptr(&*x) as uint; - do spawnfn || { + do spawnfn { let x_in_child = ptr::to_unsafe_ptr(&*x) as uint; ch.send(x_in_child); } diff --git a/src/libstd/task/spawn.rs b/src/libstd/task/spawn.rs index 4ab7b74d30068..1148774020a14 100644 --- a/src/libstd/task/spawn.rs +++ b/src/libstd/task/spawn.rs @@ -77,18 +77,15 @@ use prelude::*; -use comm::{GenericChan, oneshot}; +use comm::Chan; use rt::local::Local; use rt::sched::{Scheduler, Shutdown, TaskFromFriend}; use rt::task::{Task, Sched}; -use rt::task::UnwindResult; use rt::thread::Thread; use rt::{in_green_task_context, new_event_loop}; -use task::SingleThreaded; -use task::TaskOpts; +use task::{SingleThreaded, TaskOpts, TaskResult}; #[cfg(test)] use task::default_task_opts; -#[cfg(test)] use comm; #[cfg(test)] use task; pub fn spawn_raw(mut opts: TaskOpts, f: proc()) { @@ -132,7 +129,7 @@ pub fn spawn_raw(mut opts: TaskOpts, f: proc()) { // Create a task that will later be used to join with the new scheduler // thread when it is ready to terminate - let (thread_port, thread_chan) = oneshot(); + let (thread_port, thread_chan) = Chan::new(); let join_task = do Task::build_child(None) { debug!("running join task"); let thread: Thread<()> = thread_port.recv(); @@ -173,8 +170,8 @@ pub fn spawn_raw(mut opts: TaskOpts, f: proc()) { if opts.notify_chan.is_some() { let notify_chan = opts.notify_chan.take_unwrap(); - let on_exit: proc(UnwindResult) = proc(task_result) { - notify_chan.send(task_result) + let on_exit: proc(TaskResult) = proc(task_result) { + notify_chan.try_send(task_result); }; task.death.on_exit = Some(on_exit); } @@ -187,7 +184,7 @@ pub fn spawn_raw(mut opts: TaskOpts, f: proc()) { #[test] fn test_spawn_raw_simple() { - let (po, ch) = stream(); + let (po, ch) = Chan::new(); do spawn_raw(default_task_opts()) { ch.send(()); } @@ -208,7 +205,7 @@ fn test_spawn_raw_unsupervise() { #[test] fn test_spawn_raw_notify_success() { - let (notify_po, notify_ch) = comm::stream(); + let (notify_po, notify_ch) = Chan::new(); let opts = task::TaskOpts { notify_chan: Some(notify_ch), @@ -216,13 +213,13 @@ fn test_spawn_raw_notify_success() { }; do spawn_raw(opts) { } - assert!(notify_po.recv().is_success()); + assert!(notify_po.recv().is_ok()); } #[test] fn test_spawn_raw_notify_failure() { // New bindings for these - let (notify_po, notify_ch) = comm::stream(); + let (notify_po, notify_ch) = Chan::new(); let opts = task::TaskOpts { watched: false, @@ -232,5 +229,5 @@ fn test_spawn_raw_notify_failure() { do spawn_raw(opts) { fail!(); } - assert!(notify_po.recv().is_failure()); + assert!(notify_po.recv().is_err()); } diff --git a/src/libstd/unstable/mod.rs b/src/libstd/unstable/mod.rs index f8e2ea54f44f9..043d99eb1b82b 100644 --- a/src/libstd/unstable/mod.rs +++ b/src/libstd/unstable/mod.rs @@ -10,10 +10,7 @@ #[doc(hidden)]; -use comm::{GenericChan, GenericPort}; -use comm; use prelude::*; -use task; use libc::uintptr_t; pub mod dynamic_lib; @@ -38,15 +35,7 @@ a normal large stack. */ pub fn run_in_bare_thread(f: proc()) { use rt::thread::Thread; - - let (port, chan) = comm::stream(); - // FIXME #4525: Unfortunate that this creates an extra scheduler but it's - // necessary since rust_raw_thread_join is blocking - do task::spawn_sched(task::SingleThreaded) { - Thread::start(f).join(); - chan.send(()); - } - port.recv(); + Thread::start(f).join() } #[test] diff --git a/src/libstd/unstable/sync.rs b/src/libstd/unstable/sync.rs index 2dd5515bdbc8e..50fae1e0239a6 100644 --- a/src/libstd/unstable/sync.rs +++ b/src/libstd/unstable/sync.rs @@ -9,7 +9,7 @@ // except according to those terms. use cast; -use comm; +use comm::{Chan, Port}; use ptr; use option::{Option,Some,None}; use task; @@ -56,7 +56,7 @@ struct ArcData { // drops the last refcount on an arc. Unfortunately this can't be a proper // pipe protocol because the unwrapper has to access both stages at once. // FIXME(#7544): Maybe use AtomicPtr instead (to avoid xchg in take() later)? - unwrapper: AtomicOption<(comm::ChanOne<()>, comm::PortOne)>, + unwrapper: AtomicOption<(Chan<()>, Port)>, // FIXME(#3224) should be able to make this non-option to save memory data: Option, } @@ -70,7 +70,7 @@ unsafe fn new_inner(data: T, refcount: uint) -> *mut ArcData { /// A helper object used by `UnsafeArc::unwrap`. struct ChannelAndDataGuard { - channel: Option>, + channel: Option>, data: Option<~ArcData>, } @@ -92,7 +92,7 @@ impl Drop for ChannelAndDataGuard { } impl ChannelAndDataGuard { - fn unwrap(mut self) -> (comm::ChanOne, ~ArcData) { + fn unwrap(mut self) -> (Chan, ~ArcData) { (self.channel.take_unwrap(), self.data.take_unwrap()) } } @@ -167,8 +167,8 @@ impl UnsafeArc { // The ~ dtor needs to run if this code succeeds. let mut data: ~ArcData = cast::transmute(this.data); // Set up the unwrap protocol. - let (p1,c1) = comm::oneshot(); // () - let (p2,c2) = comm::oneshot(); // bool + let (p1,c1) = Chan::new(); // () + let (p2,c2) = Chan::new(); // bool // Try to put our server end in the unwrapper slot. // This needs no barrier -- it's protected by the release barrier on // the xadd, and the acquire+release barrier in the destructor's xadd. @@ -269,7 +269,7 @@ impl Drop for UnsafeArc{ // reference. In effect, being here means we're the only // *awake* task with the data. match data.unwrapper.take(Acquire) { - Some(~(message,response)) => { + Some(~(message, response)) => { // Send 'ready' and wait for a response. message.send(()); // Unkillable wait. Message guaranteed to come. @@ -508,7 +508,6 @@ impl Exclusive { #[cfg(test)] mod tests { - use comm; use option::*; use prelude::*; use super::{Exclusive, UnsafeArc, atomic}; @@ -541,10 +540,10 @@ mod tests { for _ in range(0u, num_tasks) { let total = total.clone(); - let (port, chan) = comm::stream(); + let (port, chan) = Chan::new(); futures.push(port); - do task::spawn || { + do task::spawn { for _ in range(0u, count) { total.with(|count| **count += 1); } @@ -552,7 +551,7 @@ mod tests { } }; - for f in futures.iter() { f.recv() } + for f in futures.mut_iter() { f.recv() } total.with(|total| assert!(**total == num_tasks * count)); } @@ -625,7 +624,7 @@ mod tests { // When an unwrap and a try_unwrap race, the unwrapper should always win. let x = UnsafeArc::new(~~"hello"); let x2 = x.clone(); - let (p,c) = comm::stream(); + let (p,c) = Chan::new(); do task::spawn { c.send(()); assert!(x2.unwrap() == ~~"hello"); diff --git a/src/test/auxiliary/cci_capture_clause.rs b/src/test/auxiliary/cci_capture_clause.rs index a9c3e1d2b0f86..ed896af69b41f 100644 --- a/src/test/auxiliary/cci_capture_clause.rs +++ b/src/test/auxiliary/cci_capture_clause.rs @@ -11,7 +11,7 @@ use std::task; pub fn foo(x: T) -> Port { - let (p, c) = stream(); + let (p, c) = Chan::new(); do task::spawn() { c.send(x.clone()); } diff --git a/src/test/bench/msgsend-pipes-shared.rs b/src/test/bench/msgsend-pipes-shared.rs index 2a5971be216b8..50cb00b25d4b1 100644 --- a/src/test/bench/msgsend-pipes-shared.rs +++ b/src/test/bench/msgsend-pipes-shared.rs @@ -20,7 +20,6 @@ extern mod extra; -use std::comm::{Port, Chan, SharedChan}; use std::comm; use std::os; use std::task; @@ -38,7 +37,7 @@ fn server(requests: &Port, responses: &Chan) { let mut count = 0u; let mut done = false; while !done { - match requests.try_recv() { + match requests.recv_opt() { Some(get_count) => { responses.send(count.clone()); } Some(bytes(b)) => { //error!("server: received {:?} bytes", b); @@ -53,10 +52,8 @@ fn server(requests: &Port, responses: &Chan) { } fn run(args: &[~str]) { - let (from_child, to_parent) = comm::stream(); - let (from_parent, to_child) = comm::stream(); - - let to_child = SharedChan::new(to_child); + let (from_child, to_parent) = Chan::new(); + let (from_parent, to_child) = SharedChan::new(); let size = from_str::(args[1]).unwrap(); let workers = from_str::(args[2]).unwrap(); diff --git a/src/test/bench/msgsend-pipes.rs b/src/test/bench/msgsend-pipes.rs index 1ff531324b358..3cf1a97a36e04 100644 --- a/src/test/bench/msgsend-pipes.rs +++ b/src/test/bench/msgsend-pipes.rs @@ -16,7 +16,6 @@ extern mod extra; -use std::comm::{SharedChan, Chan, stream}; use std::os; use std::task; use std::uint; @@ -33,7 +32,7 @@ fn server(requests: &Port, responses: &Chan) { let mut count: uint = 0; let mut done = false; while !done { - match requests.try_recv() { + match requests.recv_opt() { Some(get_count) => { responses.send(count.clone()); } Some(bytes(b)) => { //error!("server: received {:?} bytes", b); @@ -48,17 +47,15 @@ fn server(requests: &Port, responses: &Chan) { } fn run(args: &[~str]) { - let (from_child, to_parent) = stream(); - let (from_parent, to_child) = stream(); - let to_child = SharedChan::new(to_child); + let (from_child, to_parent) = Chan::new(); let size = from_str::(args[1]).unwrap(); let workers = from_str::(args[2]).unwrap(); let num_bytes = 100; let start = extra::time::precise_time_s(); let mut worker_results = ~[]; - for _ in range(0u, workers) { - let to_child = to_child.clone(); + let from_parent = if workers == 1 { + let (from_parent, to_child) = Chan::new(); let mut builder = task::task(); worker_results.push(builder.future_result()); do builder.spawn { @@ -68,7 +65,23 @@ fn run(args: &[~str]) { } //error!("worker {:?} exiting", i); }; - } + from_parent + } else { + let (from_parent, to_child) = SharedChan::new(); + for _ in range(0u, workers) { + let to_child = to_child.clone(); + let mut builder = task::task(); + worker_results.push(builder.future_result()); + do builder.spawn { + for _ in range(0u, size / workers) { + //error!("worker {:?}: sending {:?} bytes", i, num_bytes); + to_child.send(bytes(num_bytes)); + } + //error!("worker {:?} exiting", i); + }; + } + from_parent + }; do task::spawn || { server(&from_parent, &to_parent); } @@ -78,8 +91,8 @@ fn run(args: &[~str]) { } //error!("sending stop message"); - to_child.send(stop); - move_out(to_child); + //to_child.send(stop); + //move_out(to_child); let result = from_child.recv(); let end = extra::time::precise_time_s(); let elapsed = end - start; diff --git a/src/test/bench/rt-messaging-ping-pong.rs b/src/test/bench/rt-messaging-ping-pong.rs index 8fa26b42e8527..90d81aa7c3ee6 100644 --- a/src/test/bench/rt-messaging-ping-pong.rs +++ b/src/test/bench/rt-messaging-ping-pong.rs @@ -24,9 +24,9 @@ fn ping_pong_bench(n: uint, m: uint) { // Create pairs of tasks that pingpong back and forth. fn run_pair(n: uint) { // Create a stream A->B - let (pa,ca) = stream::<()>(); + let (pa,ca) = Chan::<()>::new(); // Create a stream B->A - let (pb,cb) = stream::<()>(); + let (pb,cb) = Chan::<()>::new(); do spawntask_later() || { let chan = ca; diff --git a/src/test/bench/rt-parfib.rs b/src/test/bench/rt-parfib.rs index e6519a7885629..ab607d9aebc75 100644 --- a/src/test/bench/rt-parfib.rs +++ b/src/test/bench/rt-parfib.rs @@ -13,7 +13,6 @@ extern mod extra; use std::os; use std::uint; use std::rt::test::spawntask_later; -use std::comm::oneshot; // A simple implementation of parfib. One subtree is found in a new // task and communicated over a oneshot pipe, the other is found @@ -24,7 +23,7 @@ fn parfib(n: uint) -> uint { return 1; } - let (port,chan) = oneshot::(); + let (port,chan) = Chan::new(); do spawntask_later { chan.send(parfib(n-1)); }; diff --git a/src/test/bench/shootout-chameneos-redux.rs b/src/test/bench/shootout-chameneos-redux.rs index 464bc664fb5b2..7801a64fcedba 100644 --- a/src/test/bench/shootout-chameneos-redux.rs +++ b/src/test/bench/shootout-chameneos-redux.rs @@ -12,7 +12,6 @@ extern mod extra; -use std::comm::{stream, SharedChan}; use std::option; use std::os; use std::task; @@ -138,10 +137,8 @@ fn creature( fn rendezvous(nn: uint, set: ~[color]) { // these ports will allow us to hear from the creatures - let (from_creatures, to_rendezvous) = stream::(); - let to_rendezvous = SharedChan::new(to_rendezvous); - let (from_creatures_log, to_rendezvous_log) = stream::<~str>(); - let to_rendezvous_log = SharedChan::new(to_rendezvous_log); + let (from_creatures, to_rendezvous) = SharedChan::::new(); + let (from_creatures_log, to_rendezvous_log) = SharedChan::<~str>::new(); // these channels will be passed to the creatures so they can talk to us @@ -154,7 +151,7 @@ fn rendezvous(nn: uint, set: ~[color]) { let col = *col; let to_rendezvous = to_rendezvous.clone(); let to_rendezvous_log = to_rendezvous_log.clone(); - let (from_rendezvous, to_creature) = stream(); + let (from_rendezvous, to_creature) = Chan::new(); do task::spawn { creature(ii, col, diff --git a/src/test/bench/shootout-k-nucleotide-pipes.rs b/src/test/bench/shootout-k-nucleotide-pipes.rs index a12eac5085284..96de609787345 100644 --- a/src/test/bench/shootout-k-nucleotide-pipes.rs +++ b/src/test/bench/shootout-k-nucleotide-pipes.rs @@ -17,7 +17,6 @@ extern mod extra; use extra::sort; use std::cmp::Ord; -use std::comm::{stream, Port, Chan}; use std::comm; use std::hashmap::HashMap; use std::option; @@ -165,7 +164,7 @@ fn main() { // initialize each sequence sorter let sizes = ~[1u,2,3,4,6,12,18]; - let mut streams = vec::from_fn(sizes.len(), |_| Some(stream::<~str>())); + let mut streams = vec::from_fn(sizes.len(), |_| Some(Chan::<~str>::new())); let mut from_child = ~[]; let to_child = sizes.iter().zip(streams.mut_iter()).map(|(sz, stream_ref)| { let sz = *sz; @@ -174,7 +173,7 @@ fn main() { from_child.push(from_child_); - let (from_parent, to_child) = comm::stream(); + let (from_parent, to_child) = Chan::new(); do spawn { make_sequence_processor(sz, &from_parent, &to_parent_); diff --git a/src/test/bench/shootout-pfib.rs b/src/test/bench/shootout-pfib.rs index da25f1e82eebb..aa060ceb0973f 100644 --- a/src/test/bench/shootout-pfib.rs +++ b/src/test/bench/shootout-pfib.rs @@ -21,7 +21,6 @@ extern mod extra; use extra::{time, getopts}; -use std::comm::{stream, SharedChan}; use std::os; use std::result::{Ok, Err}; use std::task; @@ -34,8 +33,7 @@ fn fib(n: int) -> int { } else if n <= 2 { c.send(1); } else { - let (pp, cc) = stream(); - let cc = SharedChan::new(cc); + let (pp, cc) = SharedChan::new(); let ch = cc.clone(); task::spawn(proc() pfib(&ch, n - 1)); let ch = cc.clone(); @@ -44,8 +42,7 @@ fn fib(n: int) -> int { } } - let (p, ch) = stream(); - let ch = SharedChan::new(ch); + let (p, ch) = SharedChan::new(); let _t = task::spawn(proc() pfib(&ch, n) ); p.recv() } diff --git a/src/test/bench/shootout-threadring.rs b/src/test/bench/shootout-threadring.rs index 5e09681630641..6293b6ce8669b 100644 --- a/src/test/bench/shootout-threadring.rs +++ b/src/test/bench/shootout-threadring.rs @@ -13,14 +13,14 @@ use std::os; fn start(n_tasks: int, token: int) { - let (p, ch1) = stream(); + let (p, ch1) = Chan::new(); let mut p = p; let ch1 = ch1; ch1.send(token); // XXX could not get this to work with a range closure let mut i = 2; while i <= n_tasks { - let (next_p, ch) = stream(); + let (next_p, ch) = Chan::new(); let imm_i = i; let imm_p = p; do spawn { diff --git a/src/test/bench/task-perf-jargon-metal-smoke.rs b/src/test/bench/task-perf-jargon-metal-smoke.rs index 8e7b48040cdf1..dc31ef06fa6f2 100644 --- a/src/test/bench/task-perf-jargon-metal-smoke.rs +++ b/src/test/bench/task-perf-jargon-metal-smoke.rs @@ -48,9 +48,9 @@ fn main() { args.clone() }; - let (p,c) = comm::stream(); + let (p,c) = Chan::new(); child_generation(from_str::(args[1]).unwrap(), c); - if p.try_recv().is_none() { + if p.recv_opt().is_none() { fail!("it happened when we slumbered"); } } diff --git a/src/test/compile-fail/bind-by-move-no-guards.rs b/src/test/compile-fail/bind-by-move-no-guards.rs index 348781d74977e..015d31bf42cce 100644 --- a/src/test/compile-fail/bind-by-move-no-guards.rs +++ b/src/test/compile-fail/bind-by-move-no-guards.rs @@ -8,10 +8,8 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -use std::comm; - fn main() { - let (p,c) = comm::stream(); + let (p,c) = Chan::new(); let x = Some(p); c.send(false); match x { diff --git a/src/test/compile-fail/builtin-superkinds-self-type.rs b/src/test/compile-fail/builtin-superkinds-self-type.rs index d7ee3aae4d52c..c82f752a4548e 100644 --- a/src/test/compile-fail/builtin-superkinds-self-type.rs +++ b/src/test/compile-fail/builtin-superkinds-self-type.rs @@ -11,10 +11,8 @@ // Tests (negatively) the ability for the Self type in default methods // to use capabilities granted by builtin kinds as supertraits. -use std::comm; - trait Foo : Freeze { - fn foo(self, chan: comm::Chan) { + fn foo(self, mut chan: Chan) { chan.send(self); //~ ERROR does not fulfill `Send` } } @@ -22,7 +20,7 @@ trait Foo : Freeze { impl Foo for T { } fn main() { - let (p,c) = comm::stream(); + let (p,c) = Chan::new(); 1193182.foo(c); assert!(p.recv() == 1193182); } diff --git a/src/test/compile-fail/unsendable-class.rs b/src/test/compile-fail/unsendable-class.rs index 08dbaaac295af..1eff31b2aa4b9 100644 --- a/src/test/compile-fail/unsendable-class.rs +++ b/src/test/compile-fail/unsendable-class.rs @@ -13,8 +13,6 @@ // Test that a class with an unsendable field can't be // sent -use std::comm; - struct foo { i: int, j: @~str, @@ -29,6 +27,6 @@ fn foo(i:int, j: @~str) -> foo { fn main() { let cat = ~"kitty"; - let (_, ch) = comm::stream(); //~ ERROR does not fulfill `Send` + let (_, ch) = Chan::new(); //~ ERROR does not fulfill `Send` ch.send(foo(42, @(cat))); //~ ERROR does not fulfill `Send` } diff --git a/src/test/run-pass/builtin-superkinds-capabilities-transitive.rs b/src/test/run-pass/builtin-superkinds-capabilities-transitive.rs index ec5af6929763f..7dc12c70b9be4 100644 --- a/src/test/run-pass/builtin-superkinds-capabilities-transitive.rs +++ b/src/test/run-pass/builtin-superkinds-capabilities-transitive.rs @@ -14,20 +14,18 @@ // a Send. Basically this just makes sure rustc is using // each_bound_trait_and_supertraits in type_contents correctly. -use std::comm; - trait Bar : Send { } trait Foo : Bar { } impl Foo for T { } impl Bar for T { } -fn foo(val: T, chan: comm::Chan) { +fn foo(val: T, chan: Chan) { chan.send(val); } pub fn main() { - let (p,c) = comm::stream(); + let (p,c) = Chan::new(); foo(31337, c); assert!(p.recv() == 31337); } diff --git a/src/test/run-pass/builtin-superkinds-capabilities-xc.rs b/src/test/run-pass/builtin-superkinds-capabilities-xc.rs index ea61b91e3b92e..f94d1af84f623 100644 --- a/src/test/run-pass/builtin-superkinds-capabilities-xc.rs +++ b/src/test/run-pass/builtin-superkinds-capabilities-xc.rs @@ -17,7 +17,6 @@ extern mod trait_superkinds_in_metadata; use trait_superkinds_in_metadata::{RequiresRequiresFreezeAndSend, RequiresFreeze}; -use std::comm; #[deriving(Eq)] struct X(T); @@ -25,12 +24,12 @@ struct X(T); impl RequiresFreeze for X { } impl RequiresRequiresFreezeAndSend for X { } -fn foo(val: T, chan: comm::Chan) { +fn foo(val: T, chan: Chan) { chan.send(val); } fn main() { - let (p,c) = comm::stream(); + let (p,c) = Chan::new(); foo(X(31337), c); assert!(p.recv() == X(31337)); } diff --git a/src/test/run-pass/builtin-superkinds-capabilities.rs b/src/test/run-pass/builtin-superkinds-capabilities.rs index 148fb5a340f15..fa3903b41d1ba 100644 --- a/src/test/run-pass/builtin-superkinds-capabilities.rs +++ b/src/test/run-pass/builtin-superkinds-capabilities.rs @@ -12,18 +12,16 @@ // builtin-kinds, e.g., if a trait requires Send to implement, then // at usage site of that trait, we know we have the Send capability. -use std::comm; - trait Foo : Send { } impl Foo for T { } -fn foo(val: T, chan: comm::Chan) { +fn foo(val: T, chan: Chan) { chan.send(val); } pub fn main() { - let (p,c) = comm::stream(); + let (p,c) = Chan::new(); foo(31337, c); assert!(p.recv() == 31337); } diff --git a/src/test/run-pass/builtin-superkinds-self-type.rs b/src/test/run-pass/builtin-superkinds-self-type.rs index a8f5f27b4d9a3..a71bedfefe021 100644 --- a/src/test/run-pass/builtin-superkinds-self-type.rs +++ b/src/test/run-pass/builtin-superkinds-self-type.rs @@ -11,10 +11,8 @@ // Tests the ability for the Self type in default methods to use // capabilities granted by builtin kinds as supertraits. -use std::comm; - trait Foo : Send { - fn foo(self, chan: comm::Chan) { + fn foo(self, chan: Chan) { chan.send(self); } } @@ -22,7 +20,7 @@ trait Foo : Send { impl Foo for T { } pub fn main() { - let (p,c) = comm::stream(); + let (p,c) = Chan::new(); 1193182.foo(c); assert!(p.recv() == 1193182); } diff --git a/src/test/run-pass/capture_nil.rs b/src/test/run-pass/capture_nil.rs index edd1a9cce65b4..0d9fdea4a9d51 100644 --- a/src/test/run-pass/capture_nil.rs +++ b/src/test/run-pass/capture_nil.rs @@ -27,7 +27,7 @@ use std::task; fn foo(x: ()) -> Port<()> { - let (p, c) = stream::<()>(); + let (p, c) = Chan::<()>::new(); do task::spawn() { c.send(x); } diff --git a/src/test/run-pass/closure-bounds-can-capture-chan.rs b/src/test/run-pass/closure-bounds-can-capture-chan.rs index 16c7eaf1037fd..3a92f4ba3f401 100644 --- a/src/test/run-pass/closure-bounds-can-capture-chan.rs +++ b/src/test/run-pass/closure-bounds-can-capture-chan.rs @@ -15,7 +15,7 @@ fn foo(blk: proc()) { } pub fn main() { - let (p,c) = comm::stream(); + let (p,c) = Chan::new(); do foo { c.send(()); } diff --git a/src/test/run-pass/comm.rs b/src/test/run-pass/comm.rs index 5eb3e247d672c..25e31f0b548b5 100644 --- a/src/test/run-pass/comm.rs +++ b/src/test/run-pass/comm.rs @@ -11,8 +11,8 @@ use std::task; pub fn main() { - let (p, ch) = stream(); - let _t = task::spawn(proc() child(&ch)); + let (p, ch) = Chan::new(); + let _t = task::spawn(proc() { child(&ch) }); let y = p.recv(); error!("received"); error!("{:?}", y); diff --git a/src/test/run-pass/hashmap-memory.rs b/src/test/run-pass/hashmap-memory.rs index 163a1253ef0a5..49aa8d18e90ec 100644 --- a/src/test/run-pass/hashmap-memory.rs +++ b/src/test/run-pass/hashmap-memory.rs @@ -21,7 +21,6 @@ pub fn map(filename: ~str, emit: map_reduce::putter) { emit(filename, ~"1"); } mod map_reduce { - use std::comm::{stream, SharedChan}; use std::hashmap::HashMap; use std::str; use std::task; @@ -43,12 +42,13 @@ mod map_reduce { fn map_task(ctrl: SharedChan, input: ~str) { let intermediates = @mut HashMap::new(); - fn emit(im: &mut HashMap<~str, int>, ctrl: SharedChan, key: ~str, + fn emit(im: &mut HashMap<~str, int>, + ctrl: SharedChan, key: ~str, _val: ~str) { if im.contains_key(&key) { return; } - let (pp, cc) = stream(); + let (pp, cc) = Chan::new(); error!("sending find_reducer"); ctrl.send(find_reducer(key.as_bytes().to_owned(), cc)); error!("receiving"); @@ -63,8 +63,7 @@ mod map_reduce { } pub fn map_reduce(inputs: ~[~str]) { - let (ctrl_port, ctrl_chan) = stream(); - let ctrl_chan = SharedChan::new(ctrl_chan); + let (ctrl_port, ctrl_chan) = SharedChan::new(); // This task becomes the master control task. It spawns others // to do the rest. @@ -81,10 +80,11 @@ mod map_reduce { match ctrl_port.recv() { mapper_done => { num_mappers -= 1; } find_reducer(k, cc) => { - let c = match reducers.find(&str::from_utf8_owned(k)) { - Some(&_c) => _c, - None => 0 - }; + let mut c; + match reducers.find(&str::from_utf8(k).to_owned()) { + Some(&_c) => { c = _c; } + None => { c = 0; } + } cc.send(c); } } diff --git a/src/test/run-pass/issue-3609.rs b/src/test/run-pass/issue-3609.rs index ab641e51960d3..4922ab18f8d45 100644 --- a/src/test/run-pass/issue-3609.rs +++ b/src/test/run-pass/issue-3609.rs @@ -14,6 +14,7 @@ enum Msg fn foo(name: ~str, samples_chan: Chan) { do task::spawn { + let mut samples_chan = samples_chan; let callback: SamplesFn = proc(buffer) { for i in range(0u, buffer.len()) { error!("{}: {}", i, buffer[i]) diff --git a/src/test/run-pass/issue-4446.rs b/src/test/run-pass/issue-4446.rs index ddcb544c64fca..53b45ba99caf0 100644 --- a/src/test/run-pass/issue-4446.rs +++ b/src/test/run-pass/issue-4446.rs @@ -9,7 +9,7 @@ // except according to those terms. pub fn main() { - let (port, chan) = stream(); + let (port, chan) = Chan::new(); do spawn { println(port.recv()); diff --git a/src/test/run-pass/issue-4448.rs b/src/test/run-pass/issue-4448.rs index 212406f9a205f..c3f871c79124b 100644 --- a/src/test/run-pass/issue-4448.rs +++ b/src/test/run-pass/issue-4448.rs @@ -8,11 +8,10 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -use std::comm; use std::task; pub fn main() { - let (port, chan) = comm::stream::<&'static str>(); + let (port, chan) = Chan::<&'static str>::new(); do task::spawn { assert_eq!(port.recv(), "hello, world"); diff --git a/src/test/run-pass/ivec-tag.rs b/src/test/run-pass/ivec-tag.rs index c7d5a2f77dae1..aa009a91aecf3 100644 --- a/src/test/run-pass/ivec-tag.rs +++ b/src/test/run-pass/ivec-tag.rs @@ -7,8 +7,10 @@ fn producer(c: &Chan<~[u8]>) { } pub fn main() { - let (p, ch) = stream::<~[u8]>(); - let _prod = task::spawn(proc() producer(&ch) ); + let (p, ch) = Chan::<~[u8]>::new(); + let _prod = task::spawn(proc() { + producer(&ch) + }); let _data: ~[u8] = p.recv(); } diff --git a/src/test/run-pass/logging-only-prints-once.rs b/src/test/run-pass/logging-only-prints-once.rs index 702eb5fffc7aa..6a88b804c22ee 100644 --- a/src/test/run-pass/logging-only-prints-once.rs +++ b/src/test/run-pass/logging-only-prints-once.rs @@ -25,7 +25,7 @@ impl fmt::Default for Foo { } pub fn main() { - let (p,c) = stream(); + let (p,c) = Chan::new(); do spawn { let f = Foo(@mut 0); debug!("{}", f); diff --git a/src/test/run-pass/send-resource.rs b/src/test/run-pass/send-resource.rs index 81e2d0a8ac8ca..7ede574a4d5be 100644 --- a/src/test/run-pass/send-resource.rs +++ b/src/test/run-pass/send-resource.rs @@ -25,10 +25,10 @@ fn test(f: int) -> test { } pub fn main() { - let (p, c) = stream(); + let (p, c) = Chan::new(); do task::spawn() { - let (pp, cc) = stream(); + let (pp, cc) = Chan::new(); c.send(cc); let _r = pp.recv(); diff --git a/src/test/run-pass/send-type-inference.rs b/src/test/run-pass/send-type-inference.rs index 79b05915b016c..a8dc4a68e9444 100644 --- a/src/test/run-pass/send-type-inference.rs +++ b/src/test/run-pass/send-type-inference.rs @@ -14,8 +14,8 @@ struct Command { val: V } -fn cache_server(c: Chan>>) { - let (_ctrl_port, ctrl_chan) = stream(); +fn cache_server(mut c: Chan>>) { + let (_ctrl_port, ctrl_chan) = Chan::new(); c.send(ctrl_chan); } pub fn main() { } diff --git a/src/test/run-pass/sendable-class.rs b/src/test/run-pass/sendable-class.rs index 6b262966277c5..0c7091a44547e 100644 --- a/src/test/run-pass/sendable-class.rs +++ b/src/test/run-pass/sendable-class.rs @@ -10,8 +10,6 @@ // Test that a class with only sendable fields can be sent -use std::comm; - struct foo { i: int, j: char, @@ -25,6 +23,6 @@ fn foo(i:int, j: char) -> foo { } pub fn main() { - let (_po, ch) = comm::stream(); + let (_po, ch) = Chan::new(); ch.send(foo(42, 'c')); } diff --git a/src/test/run-pass/spawn-types.rs b/src/test/run-pass/spawn-types.rs index 18459b7a3a312..67d1836b545cf 100644 --- a/src/test/run-pass/spawn-types.rs +++ b/src/test/run-pass/spawn-types.rs @@ -23,6 +23,6 @@ fn iotask(_cx: &ctx, ip: ~str) { } pub fn main() { - let (_p, ch) = stream::(); + let (_p, ch) = Chan::::new(); task::spawn(proc() iotask(&ch, ~"localhost") ); } diff --git a/src/test/run-pass/task-comm-0.rs b/src/test/run-pass/task-comm-0.rs index bcdb56a45fdb3..671ef16c5eba4 100644 --- a/src/test/run-pass/task-comm-0.rs +++ b/src/test/run-pass/task-comm-0.rs @@ -12,8 +12,6 @@ extern mod extra; -use std::comm::Chan; -use std::comm; use std::task; pub fn main() { test05(); } @@ -28,8 +26,8 @@ fn test05_start(ch : &Chan) { } fn test05() { - let (po, ch) = comm::stream(); - task::spawn(proc() test05_start(&ch) ); + let (po, ch) = Chan::new(); + task::spawn(proc() { test05_start(&ch) }); let mut value: int = po.recv(); error!("{}", value); value = po.recv(); diff --git a/src/test/run-pass/task-comm-10.rs b/src/test/run-pass/task-comm-10.rs index 7694891328877..fc8c6069152a5 100644 --- a/src/test/run-pass/task-comm-10.rs +++ b/src/test/run-pass/task-comm-10.rs @@ -12,11 +12,10 @@ extern mod extra; -use std::comm; use std::task; -fn start(c: &comm::Chan>) { - let (p, ch) = comm::stream(); +fn start(c: &Chan>) { + let (p, ch) = Chan::new(); c.send(ch); let mut a; @@ -30,10 +29,10 @@ fn start(c: &comm::Chan>) { } pub fn main() { - let (p, ch) = comm::stream(); - let _child = task::spawn(proc() start(&ch) ); + let (p, ch) = Chan::new(); + let _child = task::spawn(proc() { start(&ch) }); - let c = p.recv(); + let mut c = p.recv(); c.send(~"A"); c.send(~"B"); task::deschedule(); diff --git a/src/test/run-pass/task-comm-11.rs b/src/test/run-pass/task-comm-11.rs index e87809b2e98f1..6f5990e7d167e 100644 --- a/src/test/run-pass/task-comm-11.rs +++ b/src/test/run-pass/task-comm-11.rs @@ -12,16 +12,17 @@ extern mod extra; -use std::comm; use std::task; -fn start(c: &comm::Chan>) { - let (_p, ch) = comm::stream(); +fn start(c: &Chan>) { + let (_p, ch) = Chan::new(); c.send(ch); } pub fn main() { - let (p, ch) = comm::stream(); - let _child = task::spawn(proc() start(&ch) ); + let (mut p, ch) = Chan::new(); + let _child = task::spawn(proc() { + start(&ch) + }); let _c = p.recv(); } diff --git a/src/test/run-pass/task-comm-12.rs b/src/test/run-pass/task-comm-12.rs index 0d221a6cb1d77..ce30071aaf445 100644 --- a/src/test/run-pass/task-comm-12.rs +++ b/src/test/run-pass/task-comm-12.rs @@ -19,7 +19,7 @@ fn start(_task_number: int) { info!("Started / Finished task."); } fn test00() { let i: int = 0; let mut builder = task::task(); - let result = builder.future_result(); + let mut result = builder.future_result(); do builder.spawn { start(i) } diff --git a/src/test/run-pass/task-comm-13.rs b/src/test/run-pass/task-comm-13.rs index c8234aefbe577..ce3cd59bfb13f 100644 --- a/src/test/run-pass/task-comm-13.rs +++ b/src/test/run-pass/task-comm-13.rs @@ -12,17 +12,16 @@ extern mod extra; -use std::comm; use std::task; -fn start(c: &comm::Chan, start: int, number_of_messages: int) { +fn start(c: &Chan, start: int, number_of_messages: int) { let mut i: int = 0; while i < number_of_messages { c.send(start + i); i += 1; } } pub fn main() { info!("Check that we don't deadlock."); - let (_p, ch) = comm::stream(); - task::try(proc() start(&ch, 0, 10) ); + let (_p, ch) = Chan::new(); + task::try(proc() { start(&ch, 0, 10) }); info!("Joined task"); } diff --git a/src/test/run-pass/task-comm-14.rs b/src/test/run-pass/task-comm-14.rs index 8ef7f85e76827..2a7a0c25a21c2 100644 --- a/src/test/run-pass/task-comm-14.rs +++ b/src/test/run-pass/task-comm-14.rs @@ -10,19 +10,17 @@ // xfail-fast -use std::comm; use std::task; pub fn main() { - let (po, ch) = comm::stream(); - let ch = comm::SharedChan::new(ch); + let (po, ch) = SharedChan::new(); // Spawn 10 tasks each sending us back one int. let mut i = 10; while (i > 0) { info!("{}", i); let ch = ch.clone(); - task::spawn({let i = i; proc() child(i, &ch)}); + task::spawn({let i = i; proc() { child(i, &ch) }}); i = i - 1; } @@ -39,7 +37,7 @@ pub fn main() { info!("main thread exiting"); } -fn child(x: int, ch: &comm::SharedChan) { +fn child(x: int, ch: &SharedChan) { info!("{}", x); ch.send(x); } diff --git a/src/test/run-pass/task-comm-15.rs b/src/test/run-pass/task-comm-15.rs index 66b7b4db5bea7..9f0d9908f7f55 100644 --- a/src/test/run-pass/task-comm-15.rs +++ b/src/test/run-pass/task-comm-15.rs @@ -12,10 +12,9 @@ extern mod extra; -use std::comm; use std::task; -fn start(c: &comm::Chan, i0: int) { +fn start(c: &Chan, i0: int) { let mut i = i0; while i > 0 { c.send(0); @@ -28,7 +27,10 @@ pub fn main() { // is likely to terminate before the child completes, so from // the child's point of view the receiver may die. We should // drop messages on the floor in this case, and not crash! - let (p, ch) = comm::stream(); - task::spawn(proc() start(&ch, 10)); + let (p, ch) = Chan::new(); + task::spawn(proc() { + let mut ch = ch; + start(&ch, 10) + }); p.recv(); } diff --git a/src/test/run-pass/task-comm-16.rs b/src/test/run-pass/task-comm-16.rs index af4a8f68ac230..42d445dc24c2e 100644 --- a/src/test/run-pass/task-comm-16.rs +++ b/src/test/run-pass/task-comm-16.rs @@ -8,14 +8,13 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -use std::comm; use std::cmp; // Tests of ports and channels on various types fn test_rec() { struct R {val0: int, val1: u8, val2: char} - let (po, ch) = comm::stream(); + let (po, ch) = Chan::new(); let r0: R = R {val0: 0, val1: 1u8, val2: '2'}; ch.send(r0); let mut r1: R; @@ -26,7 +25,7 @@ fn test_rec() { } fn test_vec() { - let (po, ch) = comm::stream(); + let (po, ch) = Chan::new(); let v0: ~[int] = ~[0, 1, 2]; ch.send(v0); let v1 = po.recv(); @@ -36,7 +35,7 @@ fn test_vec() { } fn test_str() { - let (po, ch) = comm::stream(); + let (po, ch) = Chan::new(); let s0 = ~"test"; ch.send(s0); let s1 = po.recv(); @@ -80,7 +79,7 @@ impl cmp::Eq for t { } fn test_tag() { - let (po, ch) = comm::stream(); + let (po, ch) = Chan::new(); ch.send(tag1); ch.send(tag2(10)); ch.send(tag3(10, 11u8, 'A')); @@ -94,10 +93,10 @@ fn test_tag() { } fn test_chan() { - let (po, ch) = comm::stream(); - let (po0, ch0) = comm::stream(); + let (po, ch) = Chan::new(); + let (po0, ch0) = Chan::new(); ch.send(ch0); - let ch1 = po.recv(); + let mut ch1 = po.recv(); // Does the transmitted channel still work? ch1.send(10); diff --git a/src/test/run-pass/task-comm-3.rs b/src/test/run-pass/task-comm-3.rs index 030b70924f69c..9679aebcc9c56 100644 --- a/src/test/run-pass/task-comm-3.rs +++ b/src/test/run-pass/task-comm-3.rs @@ -12,8 +12,6 @@ extern mod extra; -use std::comm::SharedChan; -use std::comm; use std::task; pub fn main() { info!("===== WITHOUT THREADS ====="); test00(); } @@ -35,8 +33,7 @@ fn test00() { info!("Creating tasks"); - let (po, ch) = comm::stream(); - let ch = comm::SharedChan::new(ch); + let (po, ch) = SharedChan::new(); let mut i: int = 0; @@ -47,8 +44,11 @@ fn test00() { let mut builder = task::task(); results.push(builder.future_result()); builder.spawn({ + let ch = ch; let i = i; - proc() test00_start(&ch, i, number_of_messages) + proc() { + test00_start(&ch, i, number_of_messages) + } }); i = i + 1; } diff --git a/src/test/run-pass/task-comm-4.rs b/src/test/run-pass/task-comm-4.rs index 8e1704fe12f5e..3ac4c0e008768 100644 --- a/src/test/run-pass/task-comm-4.rs +++ b/src/test/run-pass/task-comm-4.rs @@ -10,14 +10,12 @@ #[allow(dead_assignment)]; -use std::comm; - pub fn main() { test00(); } fn test00() { let mut r: int = 0; let mut sum: int = 0; - let (p, c) = comm::stream(); + let (p, c) = Chan::new(); c.send(1); c.send(2); c.send(3); diff --git a/src/test/run-pass/task-comm-5.rs b/src/test/run-pass/task-comm-5.rs index 40cc7ef49e9d2..c63bf12db2b33 100644 --- a/src/test/run-pass/task-comm-5.rs +++ b/src/test/run-pass/task-comm-5.rs @@ -10,14 +10,12 @@ extern mod extra; -use std::comm; - pub fn main() { test00(); } fn test00() { let _r: int = 0; let mut sum: int = 0; - let (p, c) = comm::stream(); + let (p, c) = Chan::new(); let number_of_messages: int = 1000; let mut i: int = 0; while i < number_of_messages { c.send(i + 0); i += 1; } diff --git a/src/test/run-pass/task-comm-6.rs b/src/test/run-pass/task-comm-6.rs index b398ab41ed631..45994e78d9405 100644 --- a/src/test/run-pass/task-comm-6.rs +++ b/src/test/run-pass/task-comm-6.rs @@ -10,20 +10,16 @@ #[allow(dead_assignment)]; -use std::comm::SharedChan; -use std::comm; - pub fn main() { test00(); } fn test00() { let mut r: int = 0; let mut sum: int = 0; - let (p, ch) = comm::stream(); - let ch = SharedChan::new(ch); - let c0 = ch.clone(); - let c1 = ch.clone(); - let c2 = ch.clone(); - let c3 = ch.clone(); + let (p, ch) = SharedChan::new(); + let mut c0 = ch.clone(); + let mut c1 = ch.clone(); + let mut c2 = ch.clone(); + let mut c3 = ch.clone(); let number_of_messages: int = 1000; let mut i: int = 0; while i < number_of_messages { diff --git a/src/test/run-pass/task-comm-7.rs b/src/test/run-pass/task-comm-7.rs index c7c16af597cff..43ac3957ae2ed 100644 --- a/src/test/run-pass/task-comm-7.rs +++ b/src/test/run-pass/task-comm-7.rs @@ -14,12 +14,12 @@ extern mod extra; -use std::comm; use std::task; pub fn main() { test00(); } -fn test00_start(c: &comm::SharedChan, start: int, number_of_messages: int) { +fn test00_start(c: &SharedChan, start: int, + number_of_messages: int) { let mut i: int = 0; while i < number_of_messages { c.send(start + i); i += 1; } } @@ -27,8 +27,7 @@ fn test00_start(c: &comm::SharedChan, start: int, number_of_messages: int) fn test00() { let mut r: int = 0; let mut sum: int = 0; - let (p, ch) = comm::stream(); - let ch = comm::SharedChan::new(ch); + let (p, ch) = SharedChan::new(); let number_of_messages: int = 10; let c = ch.clone(); diff --git a/src/test/run-pass/task-comm-9.rs b/src/test/run-pass/task-comm-9.rs index 049810ff5692c..a2463ff76815f 100644 --- a/src/test/run-pass/task-comm-9.rs +++ b/src/test/run-pass/task-comm-9.rs @@ -12,12 +12,11 @@ extern mod extra; -use std::comm; use std::task; pub fn main() { test00(); } -fn test00_start(c: &comm::Chan, number_of_messages: int) { +fn test00_start(c: &Chan, number_of_messages: int) { let mut i: int = 0; while i < number_of_messages { c.send(i + 0); i += 1; } } @@ -25,13 +24,14 @@ fn test00_start(c: &comm::Chan, number_of_messages: int) { fn test00() { let r: int = 0; let mut sum: int = 0; - let (p, ch) = comm::stream(); + let (p, ch) = Chan::new(); let number_of_messages: int = 10; let mut builder = task::task(); let result = builder.future_result(); do builder.spawn { - test00_start(&ch, number_of_messages); + let mut ch = ch; + test00_start(&mut ch, number_of_messages); } let mut i: int = 0; diff --git a/src/test/run-pass/task-comm-chan-nil.rs b/src/test/run-pass/task-comm-chan-nil.rs index cd53c633c4e63..3277400704910 100644 --- a/src/test/run-pass/task-comm-chan-nil.rs +++ b/src/test/run-pass/task-comm-chan-nil.rs @@ -11,13 +11,11 @@ extern mod extra; -use std::comm; - // rustboot can't transmit nils across channels because they don't have // any size, but rustc currently can because they do have size. Whether // or not this is desirable I don't know, but here's a regression test. pub fn main() { - let (po, ch) = comm::stream(); + let (po, ch) = Chan::new(); ch.send(()); let n: () = po.recv(); assert_eq!(n, ()); diff --git a/src/test/run-pass/task-spawn-move-and-copy.rs b/src/test/run-pass/task-spawn-move-and-copy.rs index aeca54c1fb566..1ed0c23fd7a4c 100644 --- a/src/test/run-pass/task-spawn-move-and-copy.rs +++ b/src/test/run-pass/task-spawn-move-and-copy.rs @@ -12,7 +12,7 @@ use std::ptr; use std::task; pub fn main() { - let (p, ch) = stream::(); + let (p, ch) = Chan::::new(); let x = ~1; let x_in_parent = ptr::to_unsafe_ptr(&(*x)) as uint; diff --git a/src/test/run-pass/tempfile.rs b/src/test/run-pass/tempfile.rs index 663eafe170015..d3bb8128a3b9e 100644 --- a/src/test/run-pass/tempfile.rs +++ b/src/test/run-pass/tempfile.rs @@ -38,7 +38,7 @@ fn test_tempdir() { } fn test_rm_tempdir() { - let (rd, wr) = stream(); + let (rd, wr) = Chan::new(); let f: proc() = proc() { let tmp = TempDir::new("test_rm_tempdir").unwrap(); wr.send(tmp.path().clone()); diff --git a/src/test/run-pass/trait-bounds-in-arc.rs b/src/test/run-pass/trait-bounds-in-arc.rs index abd9ea1733f49..7e13d96094756 100644 --- a/src/test/run-pass/trait-bounds-in-arc.rs +++ b/src/test/run-pass/trait-bounds-in-arc.rs @@ -18,7 +18,6 @@ extern mod extra; use extra::arc; -use std::comm; use std::task; trait Pet { @@ -70,13 +69,13 @@ fn main() { ~dogge1 as ~Pet:Freeze+Send, ~fishe as ~Pet:Freeze+Send, ~dogge2 as ~Pet:Freeze+Send]); - let (p1,c1) = comm::stream(); + let (p1,c1) = Chan::new(); let arc1 = arc.clone(); do task::spawn { check_legs(arc1); c1.send(()); } - let (p2,c2) = comm::stream(); + let (p2,c2) = Chan::new(); let arc2 = arc.clone(); do task::spawn { check_names(arc2); c2.send(()); } - let (p3,c3) = comm::stream(); + let (p3,c3) = Chan::new(); let arc3 = arc.clone(); do task::spawn { check_pedigree(arc3); c3.send(()); } p1.recv(); diff --git a/src/test/run-pass/trivial-message.rs b/src/test/run-pass/trivial-message.rs index faea070c94de2..e95f9184a6302 100644 --- a/src/test/run-pass/trivial-message.rs +++ b/src/test/run-pass/trivial-message.rs @@ -13,10 +13,8 @@ message. */ -use std::comm; - pub fn main() { - let (po, ch) = comm::stream(); + let (po, ch) = Chan::new(); ch.send(42); let r = po.recv(); error!("{:?}", r); diff --git a/src/test/run-pass/unique-send-2.rs b/src/test/run-pass/unique-send-2.rs index f44802a9b34cf..d1c45a336fa7a 100644 --- a/src/test/run-pass/unique-send-2.rs +++ b/src/test/run-pass/unique-send-2.rs @@ -8,7 +8,6 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -use std::comm::{SharedChan, stream}; use std::task; fn child(c: &SharedChan<~uint>, i: uint) { @@ -16,13 +15,14 @@ fn child(c: &SharedChan<~uint>, i: uint) { } pub fn main() { - let (p, ch) = stream(); - let ch = SharedChan::new(ch); + let (p, ch) = SharedChan::new(); let n = 100u; let mut expected = 0u; for i in range(0u, n) { let ch = ch.clone(); - task::spawn(proc() child(&ch, i) ); + task::spawn(proc() { + child(&ch, i) + }); expected += i; } diff --git a/src/test/run-pass/unique-send.rs b/src/test/run-pass/unique-send.rs index a7d2f6a16a176..a1c0050e725b1 100644 --- a/src/test/run-pass/unique-send.rs +++ b/src/test/run-pass/unique-send.rs @@ -9,7 +9,7 @@ // except according to those terms. pub fn main() { - let (p, c) = stream(); + let (p, c) = Chan::new(); c.send(~100); let v = p.recv(); assert_eq!(v, ~100); diff --git a/src/test/run-pass/unwind-resource.rs b/src/test/run-pass/unwind-resource.rs index 2e01eef1c6961..f46769fa28e98 100644 --- a/src/test/run-pass/unwind-resource.rs +++ b/src/test/run-pass/unwind-resource.rs @@ -12,7 +12,6 @@ extern mod extra; -use std::comm::{stream, SharedChan}; use std::task; struct complainer { @@ -40,8 +39,7 @@ fn f(c: SharedChan) { } pub fn main() { - let (p, c) = stream(); - let c = SharedChan::new(c); + let (p, c) = SharedChan::new(); task::spawn(proc() f(c.clone())); error!("hiiiiiiiii"); assert!(p.recv()); diff --git a/src/test/run-pass/yield.rs b/src/test/run-pass/yield.rs index 47bf6323c3209..4ec30d6e02a0b 100644 --- a/src/test/run-pass/yield.rs +++ b/src/test/run-pass/yield.rs @@ -12,7 +12,7 @@ use std::task; pub fn main() { let mut builder = task::task(); - let result = builder.future_result(); + let mut result = builder.future_result(); builder.spawn(child); error!("1"); task::deschedule(); diff --git a/src/test/run-pass/yield1.rs b/src/test/run-pass/yield1.rs index 39d6bf7390fc7..c08c62b47a225 100644 --- a/src/test/run-pass/yield1.rs +++ b/src/test/run-pass/yield1.rs @@ -12,7 +12,7 @@ use std::task; pub fn main() { let mut builder = task::task(); - let result = builder.future_result(); + let mut result = builder.future_result(); builder.spawn(child); error!("1"); task::deschedule();