@@ -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.
@@ -221,6 +221,64 @@ impl Condvar {
221
221
}
222
222
}
223
223
224
+ /// Blocks the current thread until this condition variable receives a
225
+ /// notification and the required condition is met. Spurious wakeups are
226
+ /// ignored and this function will only return once the condition has been
227
+ /// met.
228
+ ///
229
+ /// This function will atomically unlock the mutex specified (represented by
230
+ /// `guard`) and block the current thread. This means that any calls
231
+ /// to [`notify_one`] or [`notify_all`] which happen logically after the
232
+ /// mutex is unlocked are candidates to wake this thread up. When this
233
+ /// function call returns, the lock specified will have been re-acquired.
234
+ ///
235
+ /// # Errors
236
+ ///
237
+ /// This function will return an error if the mutex being waited on is
238
+ /// poisoned when this thread re-acquires the lock. For more information,
239
+ /// see information about [poisoning] on the [`Mutex`] type.
240
+ ///
241
+ /// [`notify_one`]: #method.notify_one
242
+ /// [`notify_all`]: #method.notify_all
243
+ /// [poisoning]: ../sync/struct.Mutex.html#poisoning
244
+ /// [`Mutex`]: ../sync/struct.Mutex.html
245
+ ///
246
+ /// # Examples
247
+ ///
248
+ /// ```
249
+ /// #![feature(wait_until)]
250
+ ///
251
+ /// use std::sync::{Arc, Mutex, Condvar};
252
+ /// use std::thread;
253
+ ///
254
+ /// let pair = Arc::new((Mutex::new(false), Condvar::new()));
255
+ /// let pair2 = pair.clone();
256
+ ///
257
+ /// thread::spawn(move|| {
258
+ /// let &(ref lock, ref cvar) = &*pair2;
259
+ /// let mut started = lock.lock().unwrap();
260
+ /// *started = true;
261
+ /// // We notify the condvar that the value has changed.
262
+ /// cvar.notify_one();
263
+ /// });
264
+ ///
265
+ /// // Wait for the thread to start up.
266
+ /// let &(ref lock, ref cvar) = &*pair;
267
+ /// // As long as the value inside the `Mutex` is false, we wait.
268
+ /// let _guard = cvar.wait_until(lock.lock().unwrap(), |started| { *started }).unwrap();
269
+ /// ```
270
+ #[ unstable( feature = "wait_until" , issue = "47960" ) ]
271
+ pub fn wait_until < ' a , T , F > ( & self , mut guard : MutexGuard < ' a , T > ,
272
+ mut condition : F )
273
+ -> LockResult < MutexGuard < ' a , T > >
274
+ where F : FnMut ( & mut T ) -> bool {
275
+ while !condition ( & mut * guard) {
276
+ guard = self . wait ( guard) ?;
277
+ }
278
+ Ok ( guard)
279
+ }
280
+
281
+
224
282
/// Waits on this condition variable for a notification, timing out after a
225
283
/// specified duration.
226
284
///
@@ -295,7 +353,15 @@ impl Condvar {
295
353
///
296
354
/// Note that the best effort is made to ensure that the time waited is
297
355
/// measured with a monotonic clock, and not affected by the changes made to
298
- /// the system time.
356
+ /// the system time. This function is susceptible to spurious wakeups.
357
+ /// Condition variables normally have a boolean predicate associated with
358
+ /// them, and the predicate must always be checked each time this function
359
+ /// returns to protect against spurious wakeups. Additionally, it is
360
+ /// typically desirable for the time-out to not exceed some duration in
361
+ /// spite of spurious wakes, thus the sleep-duration is decremented by the
362
+ /// amount slept. Alternatively, use the `wait_timeout_until` method
363
+ /// to wait until a condition is met with a total time-out regardless
364
+ /// of spurious wakes.
299
365
///
300
366
/// The returned [`WaitTimeoutResult`] value indicates if the timeout is
301
367
/// known to have elapsed.
@@ -304,6 +370,7 @@ impl Condvar {
304
370
/// returns, regardless of whether the timeout elapsed or not.
305
371
///
306
372
/// [`wait`]: #method.wait
373
+ /// [`wait_timeout_until`]: #method.wait_timeout_until
307
374
/// [`WaitTimeoutResult`]: struct.WaitTimeoutResult.html
308
375
///
309
376
/// # Examples
@@ -355,6 +422,80 @@ impl Condvar {
355
422
}
356
423
}
357
424
425
+ /// Waits on this condition variable for a notification, timing out after a
426
+ /// specified duration. Spurious wakes will not cause this function to
427
+ /// return.
428
+ ///
429
+ /// The semantics of this function are equivalent to [`wait_until`] except
430
+ /// that the thread will be blocked for roughly no longer than `dur`. This
431
+ /// method should not be used for precise timing due to anomalies such as
432
+ /// preemption or platform differences that may not cause the maximum
433
+ /// amount of time waited to be precisely `dur`.
434
+ ///
435
+ /// Note that the best effort is made to ensure that the time waited is
436
+ /// measured with a monotonic clock, and not affected by the changes made to
437
+ /// the system time.
438
+ ///
439
+ /// The returned [`WaitTimeoutResult`] value indicates if the timeout is
440
+ /// known to have elapsed without the condition being met.
441
+ ///
442
+ /// Like [`wait_until`], the lock specified will be re-acquired when this
443
+ /// function returns, regardless of whether the timeout elapsed or not.
444
+ ///
445
+ /// [`wait_until`]: #method.wait_until
446
+ /// [`wait_timeout`]: #method.wait_timeout
447
+ /// [`WaitTimeoutResult`]: struct.WaitTimeoutResult.html
448
+ ///
449
+ /// # Examples
450
+ ///
451
+ /// ```
452
+ /// #![feature(wait_timeout_until)]
453
+ ///
454
+ /// use std::sync::{Arc, Mutex, Condvar};
455
+ /// use std::thread;
456
+ /// use std::time::Duration;
457
+ ///
458
+ /// let pair = Arc::new((Mutex::new(false), Condvar::new()));
459
+ /// let pair2 = pair.clone();
460
+ ///
461
+ /// thread::spawn(move|| {
462
+ /// let &(ref lock, ref cvar) = &*pair2;
463
+ /// let mut started = lock.lock().unwrap();
464
+ /// *started = true;
465
+ /// // We notify the condvar that the value has changed.
466
+ /// cvar.notify_one();
467
+ /// });
468
+ ///
469
+ /// // wait for the thread to start up
470
+ /// let &(ref lock, ref cvar) = &*pair;
471
+ /// let result = cvar.wait_timeout_until(
472
+ /// lock.lock().unwrap(),
473
+ /// Duration::from_millis(100),
474
+ /// |&mut started| started,
475
+ /// ).unwrap();
476
+ /// if result.1.timed_out() {
477
+ /// // timed-out without the condition ever evaluating to true.
478
+ /// }
479
+ /// // access the locked mutex via result.0
480
+ /// ```
481
+ #[ unstable( feature = "wait_timeout_until" , issue = "47960" ) ]
482
+ pub fn wait_timeout_until < ' a , T , F > ( & self , mut guard : MutexGuard < ' a , T > ,
483
+ dur : Duration , mut condition : F )
484
+ -> LockResult < ( MutexGuard < ' a , T > , WaitTimeoutResult ) >
485
+ where F : FnMut ( & mut T ) -> bool {
486
+ let start = Instant :: now ( ) ;
487
+ loop {
488
+ if condition ( & mut * guard) {
489
+ return Ok ( ( guard, WaitTimeoutResult ( false ) ) ) ;
490
+ }
491
+ let timeout = match dur. checked_sub ( start. elapsed ( ) ) {
492
+ Some ( timeout) => timeout,
493
+ None => return Ok ( ( guard, WaitTimeoutResult ( true ) ) ) ,
494
+ } ;
495
+ guard = self . wait_timeout ( guard, timeout) ?. 0 ;
496
+ }
497
+ }
498
+
358
499
/// Wakes up one blocked thread on this condvar.
359
500
///
360
501
/// If there is a blocked thread on this condition variable, then it will
@@ -480,6 +621,7 @@ impl Drop for Condvar {
480
621
481
622
#[ cfg( test) ]
482
623
mod tests {
624
+ /// #![feature(wait_until)]
483
625
use sync:: mpsc:: channel;
484
626
use sync:: { Condvar , Mutex , Arc } ;
485
627
use sync:: atomic:: { AtomicBool , Ordering } ;
@@ -548,6 +690,29 @@ mod tests {
548
690
}
549
691
}
550
692
693
+ #[ test]
694
+ #[ cfg_attr( target_os = "emscripten" , ignore) ]
695
+ fn wait_until ( ) {
696
+ let pair = Arc :: new ( ( Mutex :: new ( false ) , Condvar :: new ( ) ) ) ;
697
+ let pair2 = pair. clone ( ) ;
698
+
699
+ // Inside of our lock, spawn a new thread, and then wait for it to start.
700
+ thread:: spawn ( move || {
701
+ let & ( ref lock, ref cvar) = & * pair2;
702
+ let mut started = lock. lock ( ) . unwrap ( ) ;
703
+ * started = true ;
704
+ // We notify the condvar that the value has changed.
705
+ cvar. notify_one ( ) ;
706
+ } ) ;
707
+
708
+ // Wait for the thread to start up.
709
+ let & ( ref lock, ref cvar) = & * pair;
710
+ let guard = cvar. wait_until ( lock. lock ( ) . unwrap ( ) , |started| {
711
+ * started
712
+ } ) ;
713
+ assert ! ( * guard. unwrap( ) ) ;
714
+ }
715
+
551
716
#[ test]
552
717
#[ cfg_attr( target_os = "emscripten" , ignore) ]
553
718
fn wait_timeout_wait ( ) {
@@ -567,6 +732,53 @@ mod tests {
567
732
}
568
733
}
569
734
735
+ #[ test]
736
+ #[ cfg_attr( target_os = "emscripten" , ignore) ]
737
+ fn wait_timeout_until_wait ( ) {
738
+ let m = Arc :: new ( Mutex :: new ( ( ) ) ) ;
739
+ let c = Arc :: new ( Condvar :: new ( ) ) ;
740
+
741
+ let g = m. lock ( ) . unwrap ( ) ;
742
+ let ( _g, wait) = c. wait_timeout_until ( g, Duration :: from_millis ( 1 ) , |_| { false } ) . unwrap ( ) ;
743
+ // no spurious wakeups. ensure it timed-out
744
+ assert ! ( wait. timed_out( ) ) ;
745
+ }
746
+
747
+ #[ test]
748
+ #[ cfg_attr( target_os = "emscripten" , ignore) ]
749
+ fn wait_timeout_until_instant_satisfy ( ) {
750
+ let m = Arc :: new ( Mutex :: new ( ( ) ) ) ;
751
+ let c = Arc :: new ( Condvar :: new ( ) ) ;
752
+
753
+ let g = m. lock ( ) . unwrap ( ) ;
754
+ let ( _g, wait) = c. wait_timeout_until ( g, Duration :: from_millis ( 0 ) , |_| { true } ) . unwrap ( ) ;
755
+ // ensure it didn't time-out even if we were not given any time.
756
+ assert ! ( !wait. timed_out( ) ) ;
757
+ }
758
+
759
+ #[ test]
760
+ #[ cfg_attr( target_os = "emscripten" , ignore) ]
761
+ fn wait_timeout_until_wake ( ) {
762
+ let pair = Arc :: new ( ( Mutex :: new ( false ) , Condvar :: new ( ) ) ) ;
763
+ let pair_copy = pair. clone ( ) ;
764
+
765
+ let & ( ref m, ref c) = & * pair;
766
+ let g = m. lock ( ) . unwrap ( ) ;
767
+ let _t = thread:: spawn ( move || {
768
+ let & ( ref lock, ref cvar) = & * pair_copy;
769
+ let mut started = lock. lock ( ) . unwrap ( ) ;
770
+ thread:: sleep ( Duration :: from_millis ( 1 ) ) ;
771
+ * started = true ;
772
+ cvar. notify_one ( ) ;
773
+ } ) ;
774
+ let ( g2, wait) = c. wait_timeout_until ( g, Duration :: from_millis ( u64:: MAX ) , |& mut notified| {
775
+ notified
776
+ } ) . unwrap ( ) ;
777
+ // ensure it didn't time-out even if we were not given any time.
778
+ assert ! ( !wait. timed_out( ) ) ;
779
+ assert ! ( * g2) ;
780
+ }
781
+
570
782
#[ test]
571
783
#[ cfg_attr( target_os = "emscripten" , ignore) ]
572
784
fn wait_timeout_wake ( ) {
0 commit comments