47
47
use std:: marker:: PhantomData ;
48
48
use std:: rc:: Rc ;
49
49
use std:: sync:: { Arc , Mutex , Condvar } ;
50
- use std:: sync:: atomic:: { AtomicUsize , Ordering } ;
50
+ use std:: sync:: atomic:: AtomicUsize ;
51
+ use std:: sync:: atomic:: Ordering :: SeqCst ;
51
52
use std:: time:: Duration ;
52
53
53
54
/// Block the current thread.
@@ -237,37 +238,56 @@ impl Inner {
237
238
fn park ( & self , timeout : Option < Duration > ) -> Result < ( ) , ParkError > {
238
239
// If currently notified, then we skip sleeping. This is checked outside
239
240
// of the lock to avoid acquiring a mutex if not necessary.
240
- match self . state . compare_and_swap ( NOTIFY , IDLE , Ordering :: SeqCst ) {
241
- NOTIFY => return Ok ( ( ) ) ,
242
- IDLE => { } ,
243
- _ => unreachable ! ( ) ,
241
+ if self . state . compare_exchange ( NOTIFY , IDLE , SeqCst , SeqCst ) . is_ok ( ) {
242
+ return Ok ( ( ) ) ;
243
+ }
244
+
245
+ // If the duration is zero, then there is no need to actually block
246
+ if let Some ( ref dur) = timeout {
247
+ if * dur == Duration :: from_millis ( 0 ) {
248
+ return Ok ( ( ) ) ;
249
+ }
244
250
}
245
251
246
252
// The state is currently idle, so obtain the lock and then try to
247
253
// transition to a sleeping state.
248
254
let mut m = self . mutex . lock ( ) . unwrap ( ) ;
249
255
250
256
// Transition to sleeping
251
- match self . state . compare_and_swap ( IDLE , SLEEP , Ordering :: SeqCst ) {
252
- NOTIFY => {
257
+ match self . state . compare_exchange ( IDLE , SLEEP , SeqCst , SeqCst ) {
258
+ Ok ( _) => { }
259
+ Err ( NOTIFY ) => {
253
260
// Notified before we could sleep, consume the notification and
254
261
// exit
255
- self . state . store ( IDLE , Ordering :: SeqCst ) ;
262
+ self . state . store ( IDLE , SeqCst ) ;
256
263
return Ok ( ( ) ) ;
257
264
}
258
- IDLE => { } ,
259
265
_ => unreachable ! ( ) ,
260
266
}
261
267
262
- m = match timeout {
263
- Some ( timeout) => self . condvar . wait_timeout ( m, timeout) . unwrap ( ) . 0 ,
264
- None => self . condvar . wait ( m) . unwrap ( ) ,
268
+ match timeout {
269
+ Some ( timeout) => {
270
+ m = self . condvar . wait_timeout ( m, timeout) . unwrap ( ) . 0 ;
271
+
272
+ // Transition back to idle. If the state has transitioned to `NOTIFY`,
273
+ // this will consume that notification.
274
+ match self . state . swap ( IDLE , SeqCst ) {
275
+ NOTIFY => { } , // Got a notification
276
+ SLEEP => { } , // No notification, timed out
277
+ _ => unreachable ! ( ) ,
278
+ }
279
+ }
280
+ None => {
281
+ loop {
282
+ m = self . condvar . wait ( m) . unwrap ( ) ;
283
+ match self . state . compare_exchange ( NOTIFY , IDLE , SeqCst , SeqCst ) {
284
+ Ok ( _) => break , // Got a notification
285
+ Err ( _) => { } // Spurious wakeup, go back to sleep
286
+ }
287
+ }
288
+ }
265
289
} ;
266
290
267
- // Transition back to idle. If the state has transitioned to `NOTIFY`,
268
- // this will consume that notification
269
- self . state . store ( IDLE , Ordering :: SeqCst ) ;
270
-
271
291
// Explicitly drop the mutex guard. There is no real point in doing it
272
292
// except that I find it helpful to make it explicit where we want the
273
293
// mutex to unlock.
@@ -277,26 +297,30 @@ impl Inner {
277
297
}
278
298
279
299
fn unpark ( & self ) {
280
- // First, try transitioning from IDLE -> NOTIFY, this does not require a
281
- // lock.
282
- match self . state . compare_and_swap ( IDLE , NOTIFY , Ordering :: SeqCst ) {
283
- IDLE | NOTIFY => return ,
284
- SLEEP => { }
285
- _ => unreachable ! ( ) ,
286
- }
287
-
288
- // The other half is sleeping, this requires a lock
289
- let _m = self . mutex . lock ( ) . unwrap ( ) ;
300
+ loop {
301
+ // First, try transitioning from IDLE -> NOTIFY, this does not require a
302
+ // lock.
303
+ match self . state . compare_exchange ( IDLE , NOTIFY , SeqCst , SeqCst ) {
304
+ Ok ( _) => return , // No one was waiting
305
+ Err ( NOTIFY ) => return , // Already unparked
306
+ Err ( SLEEP ) => { } // Gotta wake up
307
+ _ => unreachable ! ( ) ,
308
+ }
290
309
291
- // Transition to NOTIFY
292
- match self . state . swap ( NOTIFY , Ordering :: SeqCst ) {
293
- SLEEP => { }
294
- NOTIFY => return ,
295
- IDLE => return ,
296
- _ => unreachable ! ( ) ,
310
+ // The other half is sleeping, this requires a lock
311
+ let _m = self . mutex . lock ( ) . unwrap ( ) ;
312
+
313
+ // Transition to NOTIFY
314
+ match self . state . compare_exchange ( SLEEP , NOTIFY , SeqCst , SeqCst ) {
315
+ Ok ( _) => {
316
+ // Wakeup the sleeper
317
+ self . condvar . notify_one ( ) ;
318
+ return ;
319
+ }
320
+ Err ( NOTIFY ) => return , // A different thread unparked
321
+ Err ( IDLE ) => { } // Parked thread went away, try again
322
+ _ => unreachable ! ( ) ,
323
+ }
297
324
}
298
-
299
- // Wakeup the sleeper
300
- self . condvar . notify_one ( ) ;
301
325
}
302
326
}
0 commit comments