@@ -14,7 +14,7 @@ use sync::{mutex, MutexGuard, PoisonError};
1414use sys_common:: condvar as sys;
1515use sys_common:: mutex as sys_mutex;
1616use sys_common:: poison:: { self , LockResult } ;
17- use time:: Duration ;
17+ use time:: { Duration , Instant } ;
1818
1919/// A type indicating whether a timed wait on a condition variable returned
2020/// due to a time out or not.
@@ -221,6 +221,64 @@ impl Condvar {
221221 }
222222 }
223223
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+
224282 /// Waits on this condition variable for a notification, timing out after a
225283 /// specified duration.
226284 ///
@@ -295,7 +353,15 @@ impl Condvar {
295353 ///
296354 /// Note that the best effort is made to ensure that the time waited is
297355 /// 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.
299365 ///
300366 /// The returned [`WaitTimeoutResult`] value indicates if the timeout is
301367 /// known to have elapsed.
@@ -304,6 +370,7 @@ impl Condvar {
304370 /// returns, regardless of whether the timeout elapsed or not.
305371 ///
306372 /// [`wait`]: #method.wait
373+ /// [`wait_timeout_until`]: #method.wait_timeout_until
307374 /// [`WaitTimeoutResult`]: struct.WaitTimeoutResult.html
308375 ///
309376 /// # Examples
@@ -355,6 +422,80 @@ impl Condvar {
355422 }
356423 }
357424
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+
358499 /// Wakes up one blocked thread on this condvar.
359500 ///
360501 /// If there is a blocked thread on this condition variable, then it will
@@ -480,6 +621,7 @@ impl Drop for Condvar {
480621
481622#[ cfg( test) ]
482623mod tests {
624+ /// #![feature(wait_until)]
483625 use sync:: mpsc:: channel;
484626 use sync:: { Condvar , Mutex , Arc } ;
485627 use sync:: atomic:: { AtomicBool , Ordering } ;
@@ -548,6 +690,29 @@ mod tests {
548690 }
549691 }
550692
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+
551716 #[ test]
552717 #[ cfg_attr( target_os = "emscripten" , ignore) ]
553718 fn wait_timeout_wait ( ) {
@@ -567,6 +732,53 @@ mod tests {
567732 }
568733 }
569734
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+
570782 #[ test]
571783 #[ cfg_attr( target_os = "emscripten" , ignore) ]
572784 fn wait_timeout_wake ( ) {
0 commit comments