about summary refs log tree commit diff
path: root/src/libstd/comm
diff options
context:
space:
mode:
authorAlex Crichton <alex@alexcrichton.com>2014-02-19 08:26:11 -0800
committerAlex Crichton <alex@alexcrichton.com>2014-02-19 16:53:00 -0800
commit765a4e9fe35014eed3c7f60f8c7030ecb537871b (patch)
tree94c665f6deba7ca162fbc9590180caddca18a9ca /src/libstd/comm
parentace204a74548947914d08a7feca694941106175d (diff)
downloadrust-765a4e9fe35014eed3c7f60f8c7030ecb537871b.tar.gz
rust-765a4e9fe35014eed3c7f60f8c7030ecb537871b.zip
Fix sending/try_recv on channels off the runtime
The fairness yield mistakenly called `Local::take()` which meant that it would
only work if a local task was available. In theory sending on a channel (or
calling try_recv) requires no runtime because it never blocks, so there's no
reason it shouldn't support such a use case.

Closes #12391
Diffstat (limited to 'src/libstd/comm')
-rw-r--r--src/libstd/comm/mod.rs59
1 files changed, 49 insertions, 10 deletions
diff --git a/src/libstd/comm/mod.rs b/src/libstd/comm/mod.rs
index 6fc0cb71c5c..145bee50a20 100644
--- a/src/libstd/comm/mod.rs
+++ b/src/libstd/comm/mod.rs
@@ -385,17 +385,17 @@ impl<T: Send> Chan<T> {
     pub fn try_send(&self, t: T) -> bool {
         // 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.
+        // yield instead of doing a send immediately.
         //
-        // Note that we don't unconditionally attempt to yield because the
-        // TLS overhead can be a bit much.
+        // Don't unconditionally attempt to yield because the TLS overhead can
+        // be a bit much, and also use `try_take` instead of `take` because
+        // there's no reason that this send shouldn't be usable off the
+        // runtime.
         let cnt = self.sends.get() + 1;
         self.sends.set(cnt);
         if cnt % (RESCHED_FREQ as uint) == 0 {
-            let task: ~Task = Local::take();
-            task.maybe_yield();
+            let task: Option<~Task> = Local::try_take();
+            task.map(|t| t.maybe_yield());
         }
 
         let (new_inner, ret) = match self.inner {
@@ -521,12 +521,13 @@ impl<T: Send> Port<T> {
     pub fn try_recv(&self) -> TryRecvResult<T> {
         // If a thread is spinning in try_recv, we should take the opportunity
         // to reschedule things occasionally. See notes above in scheduling on
-        // sends for why this doesn't always hit TLS.
+        // sends for why this doesn't always hit TLS, and also for why this uses
+        // `try_take` instead of `take`.
         let cnt = self.receives.get() + 1;
         self.receives.set(cnt);
         if cnt % (RESCHED_FREQ as uint) == 0 {
-            let task: ~Task = Local::take();
-            task.maybe_yield();
+            let task: Option<~Task> = Local::try_take();
+            task.map(|t| t.maybe_yield());
         }
 
         loop {
@@ -1203,4 +1204,42 @@ mod test {
         // wait for the child task to exit before we exit
         p1.recv();
     })
+
+    test!(fn sends_off_the_runtime() {
+        use rt::thread::Thread;
+
+        let (p, c) = Chan::new();
+        let t = Thread::start(proc() {
+            for _ in range(0, 1000) {
+                c.send(());
+            }
+        });
+        for _ in range(0, 1000) {
+            p.recv();
+        }
+        t.join();
+    })
+
+    test!(fn try_recvs_off_the_runtime() {
+        use rt::thread::Thread;
+
+        let (p, c) = Chan::new();
+        let (pdone, cdone) = Chan::new();
+        let t = Thread::start(proc() {
+            let mut hits = 0;
+            while hits < 10 {
+                match p.try_recv() {
+                    Data(()) => { hits += 1; }
+                    Empty => { Thread::yield_now(); }
+                    Disconnected => return,
+                }
+            }
+            cdone.send(());
+        });
+        for _ in range(0, 10) {
+            c.send(());
+        }
+        t.join();
+        pdone.recv();
+    })
 }