Skip to content

Commit ce48e71

Browse files
committed
Fix select() in light of the deschedule...and then race. Close #8347.
1 parent 31f9b51 commit ce48e71

File tree

2 files changed

+23
-1
lines changed

2 files changed

+23
-1
lines changed

src/libstd/rt/comm.rs

+3-1
Original file line numberDiff line numberDiff line change
@@ -114,7 +114,9 @@ impl<T> ChanOne<T> {
114114
// 'do_resched' configures whether the scheduler immediately switches to
115115
// the receiving task, or leaves the sending task still running.
116116
fn try_send_inner(self, val: T, do_resched: bool) -> bool {
117-
rtassert!(!rt::in_sched_context());
117+
if do_resched {
118+
rtassert!(!rt::in_sched_context());
119+
}
118120

119121
let mut this = self;
120122
let mut recvr_active = true;

src/libstd/select.rs

+20
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@
88
// option. This file may not be copied, modified, or distributed
99
// except according to those terms.
1010

11+
use cell::Cell;
12+
use comm;
1113
use container::Container;
1214
use iterator::Iterator;
1315
use option::*;
@@ -16,6 +18,8 @@ use option::*;
1618
use rt::sched::Scheduler;
1719
use rt::select::{SelectInner, SelectPortInner};
1820
use rt::local::Local;
21+
use rt::rtio::EventLoop;
22+
use task;
1923
use vec::{OwnedVector, MutableVector};
2024

2125
/// Trait for message-passing primitives that can be select()ed on.
@@ -45,6 +49,14 @@ pub fn select<A: Select>(ports: &mut [A]) -> uint {
4549
// (If not, we need to unblock from all of them. Length is a placeholder.)
4650
let mut ready_index = ports.len();
4751

52+
// XXX: We're using deschedule...and_then in an unsafe way here (see #8132),
53+
// in that we need to continue mutating the ready_index in the environment
54+
// after letting the task get woken up. The and_then closure needs to delay
55+
// the task from resuming until all ports have become blocked_on.
56+
let (p,c) = comm::oneshot();
57+
let p = Cell::new(p);
58+
let c = Cell::new(c);
59+
4860
let sched = Local::take::<Scheduler>();
4961
do sched.deschedule_running_task_and_then |sched, task| {
5062
let task_handles = task.make_selectable(ports.len());
@@ -57,8 +69,16 @@ pub fn select<A: Select>(ports: &mut [A]) -> uint {
5769
break;
5870
}
5971
}
72+
73+
let c = Cell::new(c.take());
74+
do sched.event_loop.callback { c.take().send_deferred(()) }
6075
}
6176

77+
// Unkillable is necessary not because getting killed is dangerous here,
78+
// but to force the recv not to use the same kill-flag that we used for
79+
// selecting. Otherwise a user-sender could spuriously wakeup us here.
80+
do task::unkillable { p.take().recv(); }
81+
6282
// Task resumes. Now unblock ourselves from all the ports we blocked on.
6383
// If the success index wasn't reset, 'take' will just take all of them.
6484
// Iterate in reverse so the 'earliest' index that's ready gets returned.

0 commit comments

Comments
 (0)