@@ -14,7 +14,7 @@ use sync::{mutex, MutexGuard, PoisonError};
14
14
use sys_common:: condvar as sys;
15
15
use sys_common:: mutex as sys_mutex;
16
16
use sys_common:: poison:: { self , LockResult } ;
17
- use time:: Duration ;
17
+ use time:: { Duration , Instant } ;
18
18
19
19
/// A type indicating whether a timed wait on a condition variable returned
20
20
/// due to a time out or not.
@@ -219,6 +219,61 @@ impl Condvar {
219
219
}
220
220
}
221
221
222
+ /// Blocks the current thread until this condition variable receives a
223
+ /// notification and the required condition is met. There are no spurious
224
+ /// wakeups when calling this.
225
+ ///
226
+ /// This function will atomically unlock the mutex specified (represented by
227
+ /// `guard`) and block the current thread. This means that any calls
228
+ /// to [`notify_one`] or [`notify_all`] which happen logically after the
229
+ /// mutex is unlocked are candidates to wake this thread up. When this
230
+ /// function call returns, the lock specified will have been re-acquired.
231
+ ///
232
+ /// # Errors
233
+ ///
234
+ /// This function will return an error if the mutex being waited on is
235
+ /// poisoned when this thread re-acquires the lock. For more information,
236
+ /// see information about [poisoning] on the [`Mutex`] type.
237
+ ///
238
+ /// [`notify_one`]: #method.notify_one
239
+ /// [`notify_all`]: #method.notify_all
240
+ /// [poisoning]: ../sync/struct.Mutex.html#poisoning
241
+ /// [`Mutex`]: ../sync/struct.Mutex.html
242
+ ///
243
+ /// # Examples
244
+ ///
245
+ /// ```
246
+ /// use std::sync::{Arc, Mutex, Condvar};
247
+ /// use std::thread;
248
+ ///
249
+ /// let pair = Arc::new((Mutex::new(false), Condvar::new()));
250
+ /// let pair2 = pair.clone();
251
+ ///
252
+ /// thread::spawn(move|| {
253
+ /// let &(ref lock, ref cvar) = &*pair2;
254
+ /// let mut started = lock.lock().unwrap();
255
+ /// *started = true;
256
+ /// // We notify the condvar that the value has changed.
257
+ /// cvar.notify_one();
258
+ /// });
259
+ ///
260
+ /// // Wait for the thread to start up.
261
+ /// let &(ref lock, ref cvar) = &*pair;
262
+ /// // As long as the value inside the `Mutex` is false, we wait.
263
+ /// cvar.wait_until(lock.lock().unwrap(), |ref started| { started });
264
+ /// ```
265
+ #[ stable( feature = "wait_until" , since = "1.24" ) ]
266
+ pub fn wait_until < ' a , T , F > ( & self , mut guard : MutexGuard < ' a , T > ,
267
+ mut condition : F )
268
+ -> LockResult < MutexGuard < ' a , T > >
269
+ where F : FnMut ( & T ) -> bool {
270
+ while !condition ( & * guard) {
271
+ guard = self . wait ( guard) ?;
272
+ }
273
+ Ok ( guard)
274
+ }
275
+
276
+
222
277
/// Waits on this condition variable for a notification, timing out after a
223
278
/// specified duration.
224
279
///
@@ -293,7 +348,15 @@ impl Condvar {
293
348
///
294
349
/// Note that the best effort is made to ensure that the time waited is
295
350
/// measured with a monotonic clock, and not affected by the changes made to
296
- /// the system time.
351
+ /// the system time. This function is susceptible to spurious wakeups.
352
+ /// Condition variables normally have a boolean predicate associated with
353
+ /// them, and the predicate must always be checked each time this function
354
+ /// returns to protect against spurious wakeups. Additionally, it is
355
+ /// typically desirable for the time-out to not exceed some duration in
356
+ /// spite of spurious wakes, thus the sleep-duration is decremented by the
357
+ /// amount slept. Alternatively, use the `wait_timeout_until` method
358
+ /// to wait until a condition is met with a total time-out regardless
359
+ /// of spurious wakes.
297
360
///
298
361
/// The returned [`WaitTimeoutResult`] value indicates if the timeout is
299
362
/// known to have elapsed.
@@ -302,6 +365,7 @@ impl Condvar {
302
365
/// returns, regardless of whether the timeout elapsed or not.
303
366
///
304
367
/// [`wait`]: #method.wait
368
+ /// [`wait_timeout_until`]: #method.wait_timeout_until
305
369
/// [`WaitTimeoutResult`]: struct.WaitTimeoutResult.html
306
370
///
307
371
/// # Examples
@@ -353,6 +417,76 @@ impl Condvar {
353
417
}
354
418
}
355
419
420
+ /// Waits on this condition variable for a notification, timing out after a
421
+ /// specified duration.
422
+ ///
423
+ /// The semantics of this function are equivalent to [`wait_until`] except
424
+ /// that the thread will be blocked for roughly no longer than `dur`. This
425
+ /// method should not be used for precise timing due to anomalies such as
426
+ /// preemption or platform differences that may not cause the maximum
427
+ /// amount of time waited to be precisely `dur`.
428
+ ///
429
+ /// Note that the best effort is made to ensure that the time waited is
430
+ /// measured with a monotonic clock, and not affected by the changes made to
431
+ /// the system time.
432
+ ///
433
+ /// The returned [`WaitTimeoutResult`] value indicates if the timeout is
434
+ /// known to have elapsed without the condition being met.
435
+ ///
436
+ /// Like [`wait_until`], the lock specified will be re-acquired when this
437
+ /// function returns, regardless of whether the timeout elapsed or not.
438
+ ///
439
+ /// [`wait_until`]: #method.wait_until
440
+ /// [`wait_timeout`]: #method.wait_timeout
441
+ /// [`WaitTimeoutResult`]: struct.WaitTimeoutResult.html
442
+ ///
443
+ /// # Examples
444
+ ///
445
+ /// ```
446
+ /// use std::sync::{Arc, Mutex, Condvar};
447
+ /// use std::thread;
448
+ /// use std::time::Duration;
449
+ ///
450
+ /// let pair = Arc::new((Mutex::new(false), Condvar::new()));
451
+ /// let pair2 = pair.clone();
452
+ ///
453
+ /// thread::spawn(move|| {
454
+ /// let &(ref lock, ref cvar) = &*pair2;
455
+ /// let mut started = lock.lock().unwrap();
456
+ /// *started = true;
457
+ /// // We notify the condvar that the value has changed.
458
+ /// cvar.notify_one();
459
+ /// });
460
+ ///
461
+ /// // wait for the thread to start up
462
+ /// let &(ref lock, ref cvar) = &*pair;
463
+ /// let result = cvar.wait_timeout_until(lock, Duration::from_millis(100), |started| {
464
+ /// started
465
+ /// }).unwrap();
466
+ /// if result.1.timed_out() {
467
+ /// // timed-out without the condition ever evaluating to true.
468
+ /// }
469
+ /// // access the locked mutex via result.0
470
+ /// ```
471
+ #[ stable( feature = "wait_timeout_until" , since = "1.24" ) ]
472
+ pub fn wait_timeout_until < ' a , T , F > ( & self , mut guard : MutexGuard < ' a , T > ,
473
+ mut dur : Duration , mut condition : F )
474
+ -> LockResult < ( MutexGuard < ' a , T > , WaitTimeoutResult ) >
475
+ where F : FnMut ( & T ) -> bool {
476
+ let timed_out = Duration :: new ( 0 , 0 ) ;
477
+ loop {
478
+ if !condition ( & * guard) {
479
+ return Ok ( ( guard, WaitTimeoutResult ( false ) ) ) ;
480
+ } else if dur == timed_out {
481
+ return Ok ( ( guard, WaitTimeoutResult ( false ) ) ) ;
482
+ }
483
+ let wait_timer = Instant :: now ( ) ;
484
+ let wait_result = self . wait_timeout ( guard, dur) ?;
485
+ dur = dur. checked_sub ( wait_timer. elapsed ( ) ) . unwrap_or ( timed_out) ;
486
+ guard = wait_result. 0 ;
487
+ }
488
+ }
489
+
356
490
/// Wakes up one blocked thread on this condvar.
357
491
///
358
492
/// If there is a blocked thread on this condition variable, then it will
@@ -546,6 +680,29 @@ mod tests {
546
680
}
547
681
}
548
682
683
+ #[ test]
684
+ #[ cfg_attr( target_os = "emscripten" , ignore) ]
685
+ fn wait_until ( ) {
686
+ let pair = Arc :: new ( ( Mutex :: new ( false ) , Condvar :: new ( ) ) ) ;
687
+ let pair2 = pair. clone ( ) ;
688
+
689
+ // Inside of our lock, spawn a new thread, and then wait for it to start.
690
+ thread:: spawn ( move || {
691
+ let & ( ref lock, ref cvar) = & * pair2;
692
+ let mut started = lock. lock ( ) . unwrap ( ) ;
693
+ * started = true ;
694
+ // We notify the condvar that the value has changed.
695
+ cvar. notify_one ( ) ;
696
+ } ) ;
697
+
698
+ // Wait for the thread to start up.
699
+ let & ( ref lock, ref cvar) = & * pair;
700
+ let guard = cvar. wait_until ( lock. lock ( ) . unwrap ( ) , |started| {
701
+ started
702
+ } ) ;
703
+ assert ! ( * guard) ;
704
+ }
705
+
549
706
#[ test]
550
707
#[ cfg_attr( target_os = "emscripten" , ignore) ]
551
708
fn wait_timeout_wait ( ) {
@@ -565,6 +722,52 @@ mod tests {
565
722
}
566
723
}
567
724
725
+ #[ test]
726
+ #[ cfg_attr( target_os = "emscripten" , ignore) ]
727
+ fn wait_timeout_until_wait ( ) {
728
+ let m = Arc :: new ( Mutex :: new ( ( ) ) ) ;
729
+ let c = Arc :: new ( Condvar :: new ( ) ) ;
730
+
731
+ let g = m. lock ( ) . unwrap ( ) ;
732
+ let ( _g, wait) = c. wait_timeout_until ( g, Duration :: from_millis ( 1 ) , || { false } ) . unwrap ( ) ;
733
+ // no spurious wakeups. ensure it timed-out
734
+ assert ! ( wait. timed_out( ) ) ;
735
+ }
736
+
737
+ #[ test]
738
+ #[ cfg_attr( target_os = "emscripten" , ignore) ]
739
+ fn wait_timeout_until_instant_satisfy ( ) {
740
+ let m = Arc :: new ( Mutex :: new ( ( ) ) ) ;
741
+ let c = Arc :: new ( Condvar :: new ( ) ) ;
742
+
743
+ let g = m. lock ( ) . unwrap ( ) ;
744
+ let ( _g, wait) = c. wait_timeout_until ( g, Duration :: from_millis ( 0 ) , || { true } ) . unwrap ( ) ;
745
+ // ensure it didn't time-out even if we were not given any time.
746
+ assert ! ( !wait. timed_out( ) ) ;
747
+ }
748
+
749
+ #[ test]
750
+ #[ cfg_attr( target_os = "emscripten" , ignore) ]
751
+ fn wait_timeout_until_wake ( ) {
752
+ let pair = Arc :: new ( ( Mutex :: new ( false ) , Condvar :: new ( ) ) ) ;
753
+ let pair_copy = pair. clone ( ) ;
754
+
755
+ let g = m. lock ( ) . unwrap ( ) ;
756
+ let t = thread:: spawn ( move || {
757
+ let & ( ref lock, ref cvar) = & * pair2;
758
+ let mut started = lock. lock ( ) . unwrap ( ) ;
759
+ thread:: sleep ( Duration :: from_millis ( 1 ) ) ;
760
+ started = true ;
761
+ cvar. notify_one ( ) ;
762
+ } ) ;
763
+ let ( g2, wait) = c. wait_timeout_until ( g, Duration :: from_millis ( u64:: MAX ) , |& notified| {
764
+ notified
765
+ } ) . unwrap ( ) ;
766
+ // ensure it didn't time-out even if we were not given any time.
767
+ assert ! ( !wait. timed_out( ) ) ;
768
+ assert ! ( * g2) ;
769
+ }
770
+
568
771
#[ test]
569
772
#[ cfg_attr( target_os = "emscripten" , ignore) ]
570
773
fn wait_timeout_wake ( ) {
0 commit comments