1
+ use std:: fmt;
1
2
use std:: pin:: Pin ;
2
3
use std:: time:: Duration ;
3
4
4
- use slab:: Slab ;
5
-
6
5
use super :: mutex:: { guard_lock, MutexGuard } ;
7
6
use crate :: future:: { timeout, Future } ;
8
- use crate :: task:: { Context , Poll , Waker } ;
7
+ use crate :: sync:: WakerSet ;
8
+ use crate :: task:: { Context , Poll } ;
9
9
10
10
#[ derive( Debug , PartialEq , Eq , Copy , Clone ) ]
11
11
pub struct WaitTimeoutResult ( bool ) ;
@@ -56,11 +56,13 @@ impl WaitTimeoutResult {
56
56
///
57
57
/// # })
58
58
/// ```
59
- #[ derive( Debug ) ]
60
59
pub struct Condvar {
61
- blocked : std :: sync :: Mutex < Slab < Option < Waker > > > ,
60
+ wakers : WakerSet ,
62
61
}
63
62
63
+ unsafe impl Send for Condvar { }
64
+ unsafe impl Sync for Condvar { }
65
+
64
66
impl Default for Condvar {
65
67
fn default ( ) -> Self {
66
68
Condvar :: new ( )
@@ -79,7 +81,7 @@ impl Condvar {
79
81
/// ```
80
82
pub fn new ( ) -> Self {
81
83
Condvar {
82
- blocked : std :: sync :: Mutex :: new ( Slab :: new ( ) ) ,
84
+ wakers : WakerSet :: new ( ) ,
83
85
}
84
86
}
85
87
@@ -88,12 +90,6 @@ impl Condvar {
88
90
/// Unlike the std equivalent, this does not check that a single mutex is used at runtime.
89
91
/// However, as a best practice avoid using with multiple mutexes.
90
92
///
91
- /// # Warning
92
- /// Any attempt to poll this future before the notification is received will result in a
93
- /// spurious wakeup. This allows the implementation to be efficient, and is technically valid
94
- /// semantics for a condition variable. However, this may result in unexpected behaviour when this future is
95
- /// used with future combinators. In most cases `Condvar::wait_until` is easier to use correctly.
96
- ///
97
93
/// # Examples
98
94
///
99
95
/// ```
@@ -136,7 +132,6 @@ impl Condvar {
136
132
cond : self ,
137
133
guard : Some ( guard) ,
138
134
key : None ,
139
- notified : false ,
140
135
}
141
136
}
142
137
@@ -189,13 +184,6 @@ impl Condvar {
189
184
190
185
/// Waits on this condition variable for a notification, timing out after a specified duration.
191
186
///
192
- /// # Warning
193
- /// This has similar limitations to `Condvar::wait`, where polling before a notify is sent can
194
- /// result in a spurious wakeup. In addition, the timeout may itself trigger a spurious wakeup,
195
- /// if no other task is holding the mutex when the future is polled. Thus the
196
- /// `WaitTimeoutResult` should not be trusted to determine if the condition variable was
197
- /// actually notified.
198
- ///
199
187
/// For these reasons `Condvar::wait_timeout_until` is recommended in most cases.
200
188
///
201
189
/// # Examples
@@ -234,7 +222,6 @@ impl Condvar {
234
222
/// #
235
223
/// # })
236
224
/// ```
237
- #[ cfg( feature = "unstable" ) ]
238
225
#[ allow( clippy:: needless_lifetimes) ]
239
226
pub async fn wait_timeout < ' a , T > (
240
227
& self ,
@@ -332,8 +319,7 @@ impl Condvar {
332
319
/// # }) }
333
320
/// ```
334
321
pub fn notify_one ( & self ) {
335
- let blocked = self . blocked . lock ( ) . unwrap ( ) ;
336
- notify ( blocked, false ) ;
322
+ self . wakers . notify_one ( ) ;
337
323
}
338
324
339
325
/// Wakes up all blocked tasks on this condvar.
@@ -369,28 +355,21 @@ impl Condvar {
369
355
/// # }) }
370
356
/// ```
371
357
pub fn notify_all ( & self ) {
372
- let blocked = self . blocked . lock ( ) . unwrap ( ) ;
373
- notify ( blocked, true ) ;
358
+ self . wakers . notify_all ( ) ;
374
359
}
375
360
}
376
361
377
- #[ inline]
378
- fn notify ( mut blocked : std:: sync:: MutexGuard < ' _ , Slab < Option < Waker > > > , all : bool ) {
379
- for ( _, entry) in blocked. iter_mut ( ) {
380
- if let Some ( w) = entry. take ( ) {
381
- w. wake ( ) ;
382
- if !all {
383
- return ;
384
- }
385
- }
362
+ impl fmt:: Debug for Condvar {
363
+ fn fmt ( & self , f : & mut fmt:: Formatter < ' _ > ) -> fmt:: Result {
364
+ //f.debug_struct("Condvar").finish()
365
+ f. pad ( "Condvar { .. }" )
386
366
}
387
367
}
388
368
389
369
struct AwaitNotify < ' a , ' b , T > {
390
370
cond : & ' a Condvar ,
391
371
guard : Option < MutexGuard < ' b , T > > ,
392
372
key : Option < usize > ,
393
- notified : bool ,
394
373
}
395
374
396
375
impl < ' a , ' b , T > Future for AwaitNotify < ' a , ' b , T > {
@@ -399,16 +378,22 @@ impl<'a, 'b, T> Future for AwaitNotify<'a, 'b, T> {
399
378
fn poll ( mut self : Pin < & mut Self > , cx : & mut Context < ' _ > ) -> Poll < Self :: Output > {
400
379
match self . guard . take ( ) {
401
380
Some ( _) => {
402
- let mut blocked = self . cond . blocked . lock ( ) . unwrap ( ) ;
403
- let w = cx. waker ( ) . clone ( ) ;
404
- self . key = Some ( blocked. insert ( Some ( w) ) ) ;
405
-
381
+ self . key = Some ( self . cond . wakers . insert ( cx) ) ;
406
382
// the guard is dropped when we return, which frees the lock
407
383
Poll :: Pending
408
384
}
409
385
None => {
410
- self . notified = true ;
411
- Poll :: Ready ( ( ) )
386
+ if let Some ( key) = self . key {
387
+ if self . cond . wakers . complete_if_notified ( key, cx) {
388
+ self . key = None ;
389
+ Poll :: Ready ( ( ) )
390
+ } else {
391
+ Poll :: Pending
392
+ }
393
+ } else {
394
+ // This should only happen if it is polled twice after receiving a notification
395
+ Poll :: Ready ( ( ) )
396
+ }
412
397
}
413
398
}
414
399
}
@@ -417,15 +402,7 @@ impl<'a, 'b, T> Future for AwaitNotify<'a, 'b, T> {
417
402
impl < ' a , ' b , T > Drop for AwaitNotify < ' a , ' b , T > {
418
403
fn drop ( & mut self ) {
419
404
if let Some ( key) = self . key {
420
- let mut blocked = self . cond . blocked . lock ( ) . unwrap ( ) ;
421
- let opt_waker = blocked. remove ( key) ;
422
-
423
- if opt_waker. is_none ( ) && !self . notified {
424
- // wake up the next task, because this task was notified, but
425
- // we are dropping it before it can finished.
426
- // This may result in a spurious wake-up, but that's ok.
427
- notify ( blocked, false ) ;
428
- }
405
+ self . cond . wakers . cancel ( key) ;
429
406
}
430
407
}
431
408
}
0 commit comments