Change:
I have encountered what I think is unexpected behavior from on OSX: when called with "large" values the call will return immediately, instead of waiting for an call. What constitutes "large" is variable, and appears to correspond to the current value in the system monotonic clock source. The following code reproduces the issue: If you run the code above with the "small" value uncommented the internal loop only runs once, waiting for the 1-second sleep to expire before is called. But if you run with the "large" value uncommented, the internal loop runs a very large number of times as each call to returns almost immediately. As noted above, the actual value of "large" decreases over time, I believe based on . I unfortunately couldn't come up with a way to prove this is the case. I've seen this behavior on OSX Yosemite and Sierra. Edit: this happens on 1.11, 1.12, beta, and nightly.
This is likely related to the implementation using unchecked arithmetic and something overflowing. I'll tweak it to much Linux and test out to make sure it works.
Hm no that's correctly handling overflow... Hmm...
Fixed by

Discussion:
mod tests { use sync::mpsc::channel; use sync::{Condvar, Mutex, Arc}; <ins> use sync::atomic::{AtomicBool, Ordering}; </ins> use thread; use time::Duration; <del> use u32; </del> <ins> use u64; </ins> #[test] fn smoke() {

#[test] #[cfg_attr(target_os = "emscripten", ignore)] <del> fn wait_timeout_ms() { </del> <ins> fn wait_timeout_wait() { </ins> let m = Arc::new(Mutex::new(())); <del> let m2 = m.clone(); </del> let c = Arc::new(Condvar::new()); <del> let c2 = c.clone(); let g = m.lock().unwrap(); let (g, _no_timeout) = c.wait_timeout(g, Duration::from_millis(1)).unwrap(); // spurious wakeups mean this isn't necessarily true // assert!(!no_timeout); let _t = thread::spawn(move || { let _g = m2.lock().unwrap(); c2.notify_one(); }); let (g, timeout_res) = c.wait_timeout(g, Duration::from_millis(u32::MAX as u64)).unwrap(); assert!(!timeout_res.timed_out()); drop(g); </del> <ins> loop { let g = m.lock().unwrap(); let (_g, no_timeout) = c.wait_timeout(g, Duration::from_millis(1)).unwrap(); // spurious wakeups mean this isn't necessarily true // so execute test again, if not timeout if !no_timeout.timed_out() { continue; } break; } } #[test] #[cfg_attr(target_os = "emscripten", ignore)] fn wait_timeout_wake() { let m = Arc::new(Mutex::new(())); let c = Arc::new(Condvar::new()); loop { let g = m.lock().unwrap(); let c2 = c.clone(); let m2 = m.clone(); let notified = Arc::new(AtomicBool::new(false)); let notified_copy = notified.clone(); let t = thread::spawn(move || { let _g = m2.lock().unwrap(); thread::sleep(Duration::from_millis(1)); notified_copy.store(true, Ordering::SeqCst); c2.notify_one(); }); let (g, timeout_res) = c.wait_timeout(g, Duration::from_millis(u64::MAX)).unwrap(); assert!(!timeout_res.timed_out()); // spurious wakeups mean this isn't necessarily true // so execute test again, if not notified if !notified.load(Ordering::SeqCst) { t.join().unwrap(); continue; } drop(g); t.join().unwrap(); break; } </ins> } #[test]

tv_nsec: 1_000_000_000 - 1, }; <ins> fn saturating_cast_to_time_t(value: u64) -> libc::time_t { if value > <libc::time_t>::max_value() as u64 { <libc::time_t>::max_value() } else { value as libc::time_t } } </ins> impl Condvar { pub const fn new() -> Condvar { // Might be moved and address is changing it is better to avoid

// Nanosecond calculations can't overflow because both values are below 1e9. let nsec = dur.subsec_nanos() as libc::c_long + now.tv_nsec as libc::c_long; <del> // FIXME: Casting u64 into time_t could truncate the value. let sec = (dur.as_secs() as libc::time_t) </del> <ins> let sec = saturating_cast_to_time_t(dur.as_secs()) </ins> .checked_add((nsec / 1_000_000_000) as libc::time_t) .and_then(|s| s.checked_add(now.tv_sec)); let nsec = nsec % 1_000_000_000;

// https://github.com/llvm-mirror/libcxx/blob/release_35/src/condition_variable.cpp#L46 // https://github.com/llvm-mirror/libcxx/blob/release_35/include/__mutex_base#L367 #[cfg(any(target_os = "macos", target_os = "ios", target_os = "android"))] <del> pub unsafe fn wait_timeout(&self, mutex: &Mutex, dur: Duration) -> bool { </del> <ins> pub unsafe fn wait_timeout(&self, mutex: &Mutex, mut dur: Duration) -> bool { </ins> use ptr; use time::Instant; <ins> // 1000 years let max_dur = Duration::from_secs(1000 * 365 * 86400); if dur > max_dur { // OSX implementation of `pthread_cond_timedwait` is buggy // with super long durations. When duration is greater than // 0x100_0000_0000_0000 seconds, `pthread_cond_timedwait` // in macOS Sierra return error 316. // // This program demonstrates the issue: // https://gist.github.com/stepancheg/198db4623a20aad2ad7cddb8fda4a63c // // To work around this issue, and possible bugs of other OSes, timeout // is clamped to 1000 years, which is allowable per the API of `wait_timeout` // because of spurious wakeups. dur = max_dur; } </ins> // First, figure out what time it currently is, in both system and // stable time.  pthread_cond_timedwait uses system time, but we want to // report timeout based on stable time.

(sys_now.tv_usec * 1000) as libc::c_long; let extra = (nsec / 1_000_000_000) as libc::time_t; let nsec = nsec % 1_000_000_000; <del> let seconds = dur.as_secs() as libc::time_t; </del> <ins> let seconds = saturating_cast_to_time_t(dur.as_secs()); </ins> let timeout = sys_now.tv_sec.checked_add(extra).and_then(|s| { s.checked_add(seconds)