about summary refs log tree commit diff
path: root/src/libstd
diff options
context:
space:
mode:
authorBen Blum <bblum@andrew.cmu.edu>2013-08-08 22:26:23 -0400
committerBen Blum <bblum@andrew.cmu.edu>2013-08-12 13:54:21 -0400
commitce48e71d28a69151a9f1fb7a620dcbad2834cef3 (patch)
tree9cba77e27a673dbadf959a8e159fd924cb148374 /src/libstd
parent31f9b5159297ffbc6da8f6e640cd6f4f8ed7c44d (diff)
downloadrust-ce48e71d28a69151a9f1fb7a620dcbad2834cef3.tar.gz
rust-ce48e71d28a69151a9f1fb7a620dcbad2834cef3.zip
Fix select() in light of the deschedule...and then race. Close #8347.
Diffstat (limited to 'src/libstd')
-rw-r--r--src/libstd/rt/comm.rs4
-rw-r--r--src/libstd/select.rs20
2 files changed, 23 insertions, 1 deletions
diff --git a/src/libstd/rt/comm.rs b/src/libstd/rt/comm.rs
index f43d1c74eae..42d59ccdf95 100644
--- a/src/libstd/rt/comm.rs
+++ b/src/libstd/rt/comm.rs
@@ -114,7 +114,9 @@ impl<T> ChanOne<T> {
     // 'do_resched' configures whether the scheduler immediately switches to
     // the receiving task, or leaves the sending task still running.
     fn try_send_inner(self, val: T, do_resched: bool) -> bool {
-        rtassert!(!rt::in_sched_context());
+        if do_resched {
+            rtassert!(!rt::in_sched_context());
+        }
 
         let mut this = self;
         let mut recvr_active = true;
diff --git a/src/libstd/select.rs b/src/libstd/select.rs
index 2053c971bce..a92339e2562 100644
--- a/src/libstd/select.rs
+++ b/src/libstd/select.rs
@@ -8,6 +8,8 @@
 // option. This file may not be copied, modified, or distributed
 // except according to those terms.
 
+use cell::Cell;
+use comm;
 use container::Container;
 use iterator::Iterator;
 use option::*;
@@ -16,6 +18,8 @@ use option::*;
 use rt::sched::Scheduler;
 use rt::select::{SelectInner, SelectPortInner};
 use rt::local::Local;
+use rt::rtio::EventLoop;
+use task;
 use vec::{OwnedVector, MutableVector};
 
 /// Trait for message-passing primitives that can be select()ed on.
@@ -45,6 +49,14 @@ pub fn select<A: Select>(ports: &mut [A]) -> uint {
     // (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 p = Cell::new(p);
+    let c = Cell::new(c);
+
     let sched = Local::take::<Scheduler>();
     do sched.deschedule_running_task_and_then |sched, task| {
         let task_handles = task.make_selectable(ports.len());
@@ -57,8 +69,16 @@ pub fn select<A: Select>(ports: &mut [A]) -> uint {
                 break;
             }
         }
+
+        let c = Cell::new(c.take());
+        do sched.event_loop.callback { c.take().send_deferred(()) }
     }
 
+    // Unkillable is necessary not because getting killed is dangerous here,
+    // but to force the recv not to use the same kill-flag that we used for
+    // selecting. Otherwise a user-sender could spuriously wakeup us here.
+    do task::unkillable { p.take().recv(); }
+
     // 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.