Skip to content

Commit

Permalink
Fix sending/try_recv on channels off the runtime
Browse files Browse the repository at this point in the history
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 rust-lang#12391
  • Loading branch information
alexcrichton committed Feb 20, 2014
1 parent ace204a commit 765a4e9
Showing 1 changed file with 49 additions and 10 deletions.
59 changes: 49 additions & 10 deletions src/libstd/comm/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down Expand Up @@ -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 {
Expand Down Expand Up @@ -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();
})
}

0 comments on commit 765a4e9

Please sign in to comment.