1
1
use std:: pin:: Pin ;
2
2
use std:: time:: Duration ;
3
3
4
- use futures_timer:: Delay ;
5
4
use slab:: Slab ;
6
5
7
6
use super :: mutex:: { guard_lock, MutexGuard } ;
8
- use crate :: future:: Future ;
7
+ use crate :: future:: { timeout , Future } ;
9
8
use crate :: task:: { Context , Poll , Waker } ;
10
9
11
10
#[ derive( Debug , PartialEq , Eq , Copy , Clone ) ]
@@ -29,7 +28,7 @@ impl WaitTimeoutResult {
29
28
/// # Examples
30
29
///
31
30
/// ```
32
- /// # fn main() { async_std::task::block_on(async {
31
+ /// # async_std::task::block_on(async {
33
32
/// #
34
33
/// use std::sync::Arc;
35
34
///
@@ -55,7 +54,7 @@ impl WaitTimeoutResult {
55
54
/// started = cvar.wait(started).await;
56
55
/// }
57
56
///
58
- /// # }) }
57
+ /// # })
59
58
/// ```
60
59
#[ derive( Debug ) ]
61
60
pub struct Condvar {
@@ -89,10 +88,16 @@ impl Condvar {
89
88
/// Unlike the std equivalent, this does not check that a single mutex is used at runtime.
90
89
/// However, as a best practice avoid using with multiple mutexes.
91
90
///
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
+ ///
92
97
/// # Examples
93
98
///
94
99
/// ```
95
- /// # fn main() { async_std::task::block_on(async {
100
+ /// # async_std::task::block_on(async {
96
101
/// use std::sync::Arc;
97
102
///
98
103
/// use async_std::sync::{Mutex, Condvar};
@@ -115,7 +120,7 @@ impl Condvar {
115
120
/// while !*started {
116
121
/// started = cvar.wait(started).await;
117
122
/// }
118
- /// # }) }
123
+ /// # })
119
124
/// ```
120
125
#[ allow( clippy:: needless_lifetimes) ]
121
126
pub async fn wait < ' a , T > ( & self , guard : MutexGuard < ' a , T > ) -> MutexGuard < ' a , T > {
@@ -142,7 +147,7 @@ impl Condvar {
142
147
/// # Examples
143
148
///
144
149
/// ```
145
- /// # fn main() { async_std::task::block_on(async {
150
+ /// # async_std::task::block_on(async {
146
151
/// #
147
152
/// use std::sync::Arc;
148
153
///
@@ -165,9 +170,8 @@ impl Condvar {
165
170
/// // As long as the value inside the `Mutex<bool>` is `false`, we wait.
166
171
/// let _guard = cvar.wait_until(lock.lock().await, |started| { *started }).await;
167
172
/// #
168
- /// # }) }
173
+ /// # })
169
174
/// ```
170
- #[ cfg( feature = "unstable" ) ]
171
175
#[ allow( clippy:: needless_lifetimes) ]
172
176
pub async fn wait_until < ' a , T , F > (
173
177
& self ,
@@ -185,10 +189,19 @@ impl Condvar {
185
189
186
190
/// Waits on this condition variable for a notification, timing out after a specified duration.
187
191
///
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
+ /// For these reasons `Condvar::wait_timeout_until` is recommended in most cases.
200
+ ///
188
201
/// # Examples
189
202
///
190
203
/// ```
191
- /// # fn main() { async_std::task::block_on(async {
204
+ /// # async_std::task::block_on(async {
192
205
/// #
193
206
/// use std::sync::Arc;
194
207
/// use std::time::Duration;
@@ -219,22 +232,73 @@ impl Condvar {
219
232
/// }
220
233
/// }
221
234
/// #
222
- /// # }) }
235
+ /// # })
223
236
/// ```
237
+ #[ cfg( feature = "unstable" ) ]
224
238
#[ allow( clippy:: needless_lifetimes) ]
225
239
pub async fn wait_timeout < ' a , T > (
226
240
& self ,
227
241
guard : MutexGuard < ' a , T > ,
228
242
dur : Duration ,
229
243
) -> ( MutexGuard < ' a , T > , WaitTimeoutResult ) {
230
244
let mutex = guard_lock ( & guard) ;
231
- let timeout_result = TimeoutWaitFuture {
232
- await_notify : self . await_notify ( guard) ,
233
- delay : Delay :: new ( dur ) ,
245
+ match timeout ( dur , self . wait ( guard ) ) . await {
246
+ Ok ( guard ) => ( guard, WaitTimeoutResult ( false ) ) ,
247
+ Err ( _ ) => ( mutex . lock ( ) . await , WaitTimeoutResult ( true ) ) ,
234
248
}
235
- . await ;
249
+ }
236
250
237
- ( mutex. lock ( ) . await , timeout_result)
251
+ /// Waits on this condition variable for a notification, timing out after a specified duration.
252
+ /// Spurious wakes will not cause this function to return.
253
+ ///
254
+ /// # Examples
255
+ /// ```
256
+ /// # async_std::task::block_on(async {
257
+ /// use std::sync::Arc;
258
+ /// use std::time::Duration;
259
+ ///
260
+ /// use async_std::sync::{Mutex, Condvar};
261
+ /// use async_std::task;
262
+ ///
263
+ /// let pair = Arc::new((Mutex::new(false), Condvar::new()));
264
+ /// let pair2 = pair.clone();
265
+ ///
266
+ /// task::spawn(async move {
267
+ /// let (lock, cvar) = &*pair2;
268
+ /// let mut started = lock.lock().await;
269
+ /// *started = true;
270
+ /// // We notify the condvar that the value has changed.
271
+ /// cvar.notify_one();
272
+ /// });
273
+ ///
274
+ /// // wait for the thread to start up
275
+ /// let (lock, cvar) = &*pair;
276
+ /// let result = cvar.wait_timeout_until(
277
+ /// lock.lock().await,
278
+ /// Duration::from_millis(100),
279
+ /// |&mut started| started,
280
+ /// ).await;
281
+ /// if result.1.timed_out() {
282
+ /// // timed-out without the condition ever evaluating to true.
283
+ /// }
284
+ /// // access the locked mutex via result.0
285
+ /// # });
286
+ /// ```
287
+ #[ allow( clippy:: needless_lifetimes) ]
288
+ pub async fn wait_timeout_until < ' a , T , F > (
289
+ & self ,
290
+ guard : MutexGuard < ' a , T > ,
291
+ dur : Duration ,
292
+ condition : F ,
293
+ ) -> ( MutexGuard < ' a , T > , WaitTimeoutResult )
294
+ where
295
+ F : FnMut ( & mut T ) -> bool ,
296
+ {
297
+ let mutex = guard_lock ( & guard) ;
298
+ match timeout ( dur, self . wait_until ( guard, condition) ) . await {
299
+ Ok ( guard) => ( guard, WaitTimeoutResult ( false ) ) ,
300
+ Err ( _) => ( mutex. lock ( ) . await , WaitTimeoutResult ( true ) ) ,
301
+ }
238
302
}
239
303
240
304
/// Wakes up one blocked task on this condvar.
@@ -365,27 +429,3 @@ impl<'a, 'b, T> Drop for AwaitNotify<'a, 'b, T> {
365
429
}
366
430
}
367
431
}
368
-
369
- struct TimeoutWaitFuture < ' a , ' b , T > {
370
- await_notify : AwaitNotify < ' a , ' b , T > ,
371
- delay : Delay ,
372
- }
373
-
374
- impl < ' a , ' b , T > TimeoutWaitFuture < ' a , ' b , T > {
375
- pin_utils:: unsafe_pinned!( await_notify: AwaitNotify <' a, ' b, T >) ;
376
- pin_utils:: unsafe_pinned!( delay: Delay ) ;
377
- }
378
-
379
- impl < ' a , ' b , T > Future for TimeoutWaitFuture < ' a , ' b , T > {
380
- type Output = WaitTimeoutResult ;
381
-
382
- fn poll ( mut self : Pin < & mut Self > , cx : & mut Context < ' _ > ) -> Poll < Self :: Output > {
383
- match self . as_mut ( ) . delay ( ) . poll ( cx) {
384
- Poll :: Pending => match self . await_notify ( ) . poll ( cx) {
385
- Poll :: Ready ( _) => Poll :: Ready ( WaitTimeoutResult ( false ) ) ,
386
- Poll :: Pending => Poll :: Pending ,
387
- } ,
388
- Poll :: Ready ( _) => Poll :: Ready ( WaitTimeoutResult ( true ) ) ,
389
- }
390
- }
391
- }
0 commit comments