Skip to content

Commit be7738b

Browse files
committed
Add SendDeferred trait and use it to fix #8214.
1 parent f1c1f92 commit be7738b

File tree

3 files changed

+162
-28
lines changed

3 files changed

+162
-28
lines changed

src/libextra/sync.rs

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818

1919
use std::borrow;
2020
use std::comm;
21+
use std::comm::SendDeferred;
2122
use std::task;
2223
use std::unstable::sync::{Exclusive, UnsafeAtomicRcBox};
2324
use std::unstable::atomics;
@@ -49,7 +50,7 @@ impl WaitQueue {
4950
if self.head.peek() {
5051
// Pop and send a wakeup signal. If the waiter was killed, its port
5152
// will have closed. Keep trying until we get a live task.
52-
if comm::try_send_one(self.head.recv(), ()) {
53+
if self.head.recv().try_send_deferred(()) {
5354
true
5455
} else {
5556
self.signal()
@@ -62,7 +63,7 @@ impl WaitQueue {
6263
fn broadcast(&self) -> uint {
6364
let mut count = 0;
6465
while self.head.peek() {
65-
if comm::try_send_one(self.head.recv(), ()) {
66+
if self.head.recv().try_send_deferred(()) {
6667
count += 1;
6768
}
6869
}
@@ -102,7 +103,7 @@ impl<Q:Send> Sem<Q> {
102103
// Tell outer scope we need to block.
103104
waiter_nobe = Some(WaitEnd);
104105
// Enqueue ourself.
105-
state.waiters.tail.send(SignalEnd);
106+
state.waiters.tail.send_deferred(SignalEnd);
106107
}
107108
}
108109
// Uncomment if you wish to test for sem races. Not valgrind-friendly.
@@ -256,7 +257,7 @@ impl<'self> Condvar<'self> {
256257
}
257258
// Enqueue ourself to be woken up by a signaller.
258259
let SignalEnd = SignalEnd.take_unwrap();
259-
state.blocked[condvar_id].tail.send(SignalEnd);
260+
state.blocked[condvar_id].tail.send_deferred(SignalEnd);
260261
} else {
261262
out_of_bounds = Some(state.blocked.len());
262263
}

src/libstd/comm.rs

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ use either::{Either, Left, Right};
1919
use kinds::Send;
2020
use option::{Option, Some};
2121
use unstable::sync::Exclusive;
22+
pub use rt::comm::SendDeferred;
2223
use rtcomm = rt::comm;
2324
use rt;
2425

@@ -105,6 +106,21 @@ impl<T: Send> GenericSmartChan<T> for Chan<T> {
105106
}
106107
}
107108

109+
impl<T: Send> SendDeferred<T> for Chan<T> {
110+
fn send_deferred(&self, x: T) {
111+
match self.inner {
112+
Left(ref chan) => chan.send(x),
113+
Right(ref chan) => chan.send_deferred(x)
114+
}
115+
}
116+
fn try_send_deferred(&self, x: T) -> bool {
117+
match self.inner {
118+
Left(ref chan) => chan.try_send(x),
119+
Right(ref chan) => chan.try_send_deferred(x)
120+
}
121+
}
122+
}
123+
108124
impl<T: Send> GenericPort<T> for Port<T> {
109125
fn recv(&self) -> T {
110126
match self.inner {
@@ -250,6 +266,20 @@ impl<T: Send> ChanOne<T> {
250266
Right(p) => p.try_send(data)
251267
}
252268
}
269+
pub fn send_deferred(self, data: T) {
270+
let ChanOne { inner } = self;
271+
match inner {
272+
Left(p) => p.send(data),
273+
Right(p) => p.send_deferred(data)
274+
}
275+
}
276+
pub fn try_send_deferred(self, data: T) -> bool {
277+
let ChanOne { inner } = self;
278+
match inner {
279+
Left(p) => p.try_send(data),
280+
Right(p) => p.try_send_deferred(data)
281+
}
282+
}
253283
}
254284

255285
pub fn recv_one<T: Send>(port: PortOne<T>) -> T {

src/libstd/rt/comm.rs

Lines changed: 127 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@ use comm::{GenericChan, GenericSmartChan, GenericPort, Peekable};
2525
use cell::Cell;
2626
use clone::Clone;
2727
use rt::{context, SchedulerContext};
28+
use tuple::ImmutableTuple;
2829

2930
/// A combined refcount / BlockedTask-as-uint pointer.
3031
///
@@ -86,12 +87,32 @@ impl<T> ChanOne<T> {
8687
}
8788
}
8889

90+
/// Send a message on the one-shot channel. If a receiver task is blocked
91+
/// waiting for the message, will wake it up and reschedule to it.
8992
pub fn send(self, val: T) {
9093
self.try_send(val);
9194
}
9295

96+
/// As `send`, but also returns whether or not the receiver endpoint is still open.
9397
pub fn try_send(self, val: T) -> bool {
98+
self.try_send_inner(val, true)
99+
}
100+
101+
/// Send a message without immediately rescheduling to a blocked receiver.
102+
/// This can be useful in contexts where rescheduling is forbidden, or to
103+
/// optimize for when the sender expects to still have useful work to do.
104+
pub fn send_deferred(self, val: T) {
105+
self.try_send_deferred(val);
106+
}
107+
108+
/// As `send_deferred` and `try_send` together.
109+
pub fn try_send_deferred(self, val: T) -> bool {
110+
self.try_send_inner(val, false)
111+
}
94112

113+
// 'do_resched' configures whether the scheduler immediately switches to
114+
// the receiving task, or leaves the sending task still running.
115+
fn try_send_inner(self, val: T, do_resched: bool) -> bool {
95116
rtassert!(context() != SchedulerContext);
96117

97118
let mut this = self;
@@ -130,9 +151,16 @@ impl<T> ChanOne<T> {
130151
task_as_state => {
131152
// Port is blocked. Wake it up.
132153
let recvr = BlockedTask::cast_from_uint(task_as_state);
133-
do recvr.wake().map_consume |woken_task| {
134-
Scheduler::run_task(woken_task);
135-
};
154+
if do_resched {
155+
do recvr.wake().map_consume |woken_task| {
156+
Scheduler::run_task(woken_task);
157+
};
158+
} else {
159+
let recvr = Cell::new(recvr);
160+
do Local::borrow::<Scheduler, ()> |sched| {
161+
sched.enqueue_blocked_task(recvr.take());
162+
}
163+
}
136164
}
137165
}
138166
}
@@ -152,6 +180,7 @@ impl<T> PortOne<T> {
152180
}
153181
}
154182

183+
/// Wait for a message on the one-shot port. Fails if the send end is closed.
155184
pub fn recv(self) -> T {
156185
match self.try_recv() {
157186
Some(val) => val,
@@ -161,6 +190,7 @@ impl<T> PortOne<T> {
161190
}
162191
}
163192

193+
/// As `recv`, but returns `None` if the send end is closed rather than failing.
164194
pub fn try_recv(self) -> Option<T> {
165195
let mut this = self;
166196

@@ -382,6 +412,12 @@ impl<T> Drop for PortOne<T> {
382412
}
383413
}
384414

415+
/// Trait for non-rescheduling send operations, similar to `send_deferred` on ChanOne.
416+
pub trait SendDeferred<T> {
417+
fn send_deferred(&self, val: T);
418+
fn try_send_deferred(&self, val: T) -> bool;
419+
}
420+
385421
struct StreamPayload<T> {
386422
val: T,
387423
next: PortOne<StreamPayload<T>>
@@ -409,6 +445,15 @@ pub fn stream<T: Send>() -> (Port<T>, Chan<T>) {
409445
return (port, chan);
410446
}
411447

448+
impl<T: Send> Chan<T> {
449+
fn try_send_inner(&self, val: T, do_resched: bool) -> bool {
450+
let (next_pone, next_cone) = oneshot();
451+
let cone = self.next.take();
452+
self.next.put_back(next_cone);
453+
cone.try_send_inner(StreamPayload { val: val, next: next_pone }, do_resched)
454+
}
455+
}
456+
412457
impl<T: Send> GenericChan<T> for Chan<T> {
413458
fn send(&self, val: T) {
414459
self.try_send(val);
@@ -417,10 +462,16 @@ impl<T: Send> GenericChan<T> for Chan<T> {
417462

418463
impl<T: Send> GenericSmartChan<T> for Chan<T> {
419464
fn try_send(&self, val: T) -> bool {
420-
let (next_pone, next_cone) = oneshot();
421-
let cone = self.next.take();
422-
self.next.put_back(next_cone);
423-
cone.try_send(StreamPayload { val: val, next: next_pone })
465+
self.try_send_inner(val, true)
466+
}
467+
}
468+
469+
impl<T: Send> SendDeferred<T> for Chan<T> {
470+
fn send_deferred(&self, val: T) {
471+
self.try_send_deferred(val);
472+
}
473+
fn try_send_deferred(&self, val: T) -> bool {
474+
self.try_send_inner(val, false)
424475
}
425476
}
426477

@@ -495,6 +546,17 @@ impl<T> SharedChan<T> {
495546
}
496547
}
497548

549+
impl<T: Send> SharedChan<T> {
550+
fn try_send_inner(&self, val: T, do_resched: bool) -> bool {
551+
unsafe {
552+
let (next_pone, next_cone) = oneshot();
553+
let cone = (*self.next.get()).swap(~next_cone, SeqCst);
554+
cone.unwrap().try_send_inner(StreamPayload { val: val, next: next_pone },
555+
do_resched)
556+
}
557+
}
558+
}
559+
498560
impl<T: Send> GenericChan<T> for SharedChan<T> {
499561
fn send(&self, val: T) {
500562
self.try_send(val);
@@ -503,11 +565,16 @@ impl<T: Send> GenericChan<T> for SharedChan<T> {
503565

504566
impl<T: Send> GenericSmartChan<T> for SharedChan<T> {
505567
fn try_send(&self, val: T) -> bool {
506-
unsafe {
507-
let (next_pone, next_cone) = oneshot();
508-
let cone = (*self.next.get()).swap(~next_cone, SeqCst);
509-
cone.unwrap().try_send(StreamPayload { val: val, next: next_pone })
510-
}
568+
self.try_send_inner(val, true)
569+
}
570+
}
571+
572+
impl<T: Send> SendDeferred<T> for SharedChan<T> {
573+
fn send_deferred(&self, val: T) {
574+
self.try_send_deferred(val);
575+
}
576+
fn try_send_deferred(&self, val: T) -> bool {
577+
self.try_send_inner(val, false)
511578
}
512579
}
513580

@@ -584,31 +651,32 @@ pub fn megapipe<T: Send>() -> MegaPipe<T> {
584651

585652
impl<T: Send> GenericChan<T> for MegaPipe<T> {
586653
fn send(&self, val: T) {
587-
match *self {
588-
(_, ref c) => c.send(val)
589-
}
654+
self.second_ref().send(val)
590655
}
591656
}
592657

593658
impl<T: Send> GenericSmartChan<T> for MegaPipe<T> {
594659
fn try_send(&self, val: T) -> bool {
595-
match *self {
596-
(_, ref c) => c.try_send(val)
597-
}
660+
self.second_ref().try_send(val)
598661
}
599662
}
600663

601664
impl<T: Send> GenericPort<T> for MegaPipe<T> {
602665
fn recv(&self) -> T {
603-
match *self {
604-
(ref p, _) => p.recv()
605-
}
666+
self.first_ref().recv()
606667
}
607668

608669
fn try_recv(&self) -> Option<T> {
609-
match *self {
610-
(ref p, _) => p.try_recv()
611-
}
670+
self.first_ref().try_recv()
671+
}
672+
}
673+
674+
impl<T: Send> SendDeferred<T> for MegaPipe<T> {
675+
fn send_deferred(&self, val: T) {
676+
self.second_ref().send_deferred(val)
677+
}
678+
fn try_send_deferred(&self, val: T) -> bool {
679+
self.second_ref().try_send_deferred(val)
612680
}
613681
}
614682

@@ -1017,4 +1085,39 @@ mod test {
10171085
}
10181086
}
10191087
}
1088+
1089+
#[test]
1090+
fn send_deferred() {
1091+
use unstable::sync::atomically;
1092+
1093+
// Tests no-rescheduling of send_deferred on all types of channels.
1094+
do run_in_newsched_task {
1095+
let (pone, cone) = oneshot();
1096+
let (pstream, cstream) = stream();
1097+
let (pshared, cshared) = stream();
1098+
let cshared = SharedChan::new(cshared);
1099+
let mp = megapipe();
1100+
1101+
let pone = Cell::new(pone);
1102+
do spawntask { pone.take().recv(); }
1103+
let pstream = Cell::new(pstream);
1104+
do spawntask { pstream.take().recv(); }
1105+
let pshared = Cell::new(pshared);
1106+
do spawntask { pshared.take().recv(); }
1107+
let p_mp = Cell::new(mp.clone());
1108+
do spawntask { p_mp.take().recv(); }
1109+
1110+
let cs = Cell::new((cone, cstream, cshared, mp));
1111+
unsafe {
1112+
do atomically {
1113+
let (cone, cstream, cshared, mp) = cs.take();
1114+
cone.send_deferred(());
1115+
cstream.send_deferred(());
1116+
cshared.send_deferred(());
1117+
mp.send_deferred(());
1118+
}
1119+
}
1120+
}
1121+
}
1122+
10201123
}

0 commit comments

Comments
 (0)