@@ -18,7 +18,7 @@ use rt::kill::BlockedTask;
18
18
use kinds:: Send ;
19
19
use rt:: sched:: Scheduler ;
20
20
use rt:: local:: Local ;
21
- use unstable:: atomics:: { AtomicUint , AtomicOption , SeqCst } ;
21
+ use unstable:: atomics:: { AtomicUint , AtomicOption , Acquire , SeqCst } ;
22
22
use unstable:: sync:: UnsafeAtomicRcBox ;
23
23
use util:: Void ;
24
24
use comm:: { GenericChan , GenericSmartChan , GenericPort , Peekable } ;
@@ -164,36 +164,39 @@ impl<T> PortOne<T> {
164
164
let mut this = self ;
165
165
let packet = this. packet ( ) ;
166
166
167
- // XXX: Optimize this to not require the two context switches when data is available
168
-
169
- // Switch to the scheduler to put the ~Task into the Packet state.
170
- let sched = Local :: take :: < Scheduler > ( ) ;
171
- do sched. deschedule_running_task_and_then |sched, task| {
172
- unsafe {
173
- // Atomically swap the task pointer into the Packet state, issuing
174
- // an acquire barrier to prevent reordering of the subsequent read
175
- // of the payload. Also issues a release barrier to prevent reordering
176
- // of any previous writes to the task structure.
177
- let task_as_state = task. cast_to_uint ( ) ;
178
- let oldstate = ( * packet) . state . swap ( task_as_state, SeqCst ) ;
179
- match oldstate {
180
- STATE_BOTH => {
181
- // Data has not been sent. Now we're blocked.
182
- rtdebug ! ( "non-rendezvous recv" ) ;
183
- sched. metrics . non_rendezvous_recvs += 1 ;
167
+ // Optimistic check. If data was sent already, we don't even need to block.
168
+ // No release barrier needed here; we're not handing off our task pointer yet.
169
+ if unsafe { ( * packet) . state . load ( Acquire ) } != STATE_ONE {
170
+ // No data available yet.
171
+ // Switch to the scheduler to put the ~Task into the Packet state.
172
+ let sched = Local :: take :: < Scheduler > ( ) ;
173
+ do sched. deschedule_running_task_and_then |sched, task| {
174
+ unsafe {
175
+ // Atomically swap the task pointer into the Packet state, issuing
176
+ // an acquire barrier to prevent reordering of the subsequent read
177
+ // of the payload. Also issues a release barrier to prevent
178
+ // reordering of any previous writes to the task structure.
179
+ let task_as_state = task. cast_to_uint ( ) ;
180
+ let oldstate = ( * packet) . state . swap ( task_as_state, SeqCst ) ;
181
+ match oldstate {
182
+ STATE_BOTH => {
183
+ // Data has not been sent. Now we're blocked.
184
+ rtdebug ! ( "non-rendezvous recv" ) ;
185
+ sched. metrics . non_rendezvous_recvs += 1 ;
186
+ }
187
+ STATE_ONE => {
188
+ rtdebug ! ( "rendezvous recv" ) ;
189
+ sched. metrics . rendezvous_recvs += 1 ;
190
+
191
+ // Channel is closed. Switch back and check the data.
192
+ // NB: We have to drop back into the scheduler event loop here
193
+ // instead of switching immediately back or we could end up
194
+ // triggering infinite recursion on the scheduler's stack.
195
+ let recvr = BlockedTask :: cast_from_uint ( task_as_state) ;
196
+ sched. enqueue_blocked_task ( recvr) ;
197
+ }
198
+ _ => util:: unreachable ( )
184
199
}
185
- STATE_ONE => {
186
- rtdebug ! ( "rendezvous recv" ) ;
187
- sched. metrics . rendezvous_recvs += 1 ;
188
-
189
- // Channel is closed. Switch back and check the data.
190
- // NB: We have to drop back into the scheduler event loop here
191
- // instead of switching immediately back or we could end up
192
- // triggering infinite recursion on the scheduler's stack.
193
- let recvr = BlockedTask :: cast_from_uint ( task_as_state) ;
194
- sched. enqueue_blocked_task ( recvr) ;
195
- }
196
- _ => util:: unreachable ( )
197
200
}
198
201
}
199
202
}
@@ -212,7 +215,7 @@ impl<T> PortOne<T> {
212
215
// a different scheduler for resuming. That send synchronized memory.
213
216
214
217
unsafe {
215
- let payload = util :: replace ( & mut ( * packet) . payload , None ) ;
218
+ let payload = ( * packet) . payload . take ( ) ;
216
219
217
220
// The sender has closed up shop. Drop the packet.
218
221
let _packet: ~Packet < T > = cast:: transmute ( this. void_packet ) ;
0 commit comments