@@ -23,6 +23,14 @@ const TIMESPEC_MAX: libc::timespec = libc::timespec {
2323 tv_nsec : 1_000_000_000 - 1 ,
2424} ;
2525
26+ fn saturating_cast_to_time_t ( value : u64 ) -> libc:: time_t {
27+ if value > <libc:: time_t >:: max_value ( ) as u64 {
28+ <libc:: time_t >:: max_value ( )
29+ } else {
30+ value as libc:: time_t
31+ }
32+ }
33+
2634impl Condvar {
2735 pub const fn new ( ) -> Condvar {
2836 // Might be moved and address is changing it is better to avoid
@@ -79,8 +87,7 @@ impl Condvar {
7987
8088 // Nanosecond calculations can't overflow because both values are below 1e9.
8189 let nsec = dur. subsec_nanos ( ) as libc:: c_long + now. tv_nsec as libc:: c_long ;
82- // FIXME: Casting u64 into time_t could truncate the value.
83- let sec = ( dur. as_secs ( ) as libc:: time_t )
90+ let sec = saturating_cast_to_time_t ( dur. as_secs ( ) )
8491 . checked_add ( ( nsec / 1_000_000_000 ) as libc:: time_t )
8592 . and_then ( |s| s. checked_add ( now. tv_sec ) ) ;
8693 let nsec = nsec % 1_000_000_000 ;
@@ -100,10 +107,29 @@ impl Condvar {
100107 // https://github.com/llvm-mirror/libcxx/blob/release_35/src/condition_variable.cpp#L46
101108 // https://github.com/llvm-mirror/libcxx/blob/release_35/include/__mutex_base#L367
102109 #[ cfg( any( target_os = "macos" , target_os = "ios" , target_os = "android" ) ) ]
103- pub unsafe fn wait_timeout ( & self , mutex : & Mutex , dur : Duration ) -> bool {
110+ pub unsafe fn wait_timeout ( & self , mutex : & Mutex , mut dur : Duration ) -> bool {
104111 use ptr;
105112 use time:: Instant ;
106113
114+ // 1000 years
115+ let max_dur = Duration :: from_secs ( 1000 * 365 * 86400 ) ;
116+
117+ if dur > max_dur {
118+ // OSX implementation of `pthread_cond_timedwait` is buggy
119+ // with super long durations. When duration is greater than
120+ // 0x100_0000_0000_0000 seconds, `pthread_cond_timedwait`
121+ // in macOS Sierra return error 316.
122+ //
123+ // This program demonstrates the issue:
124+ // https://gist.github.com/stepancheg/198db4623a20aad2ad7cddb8fda4a63c
125+ //
126+ // To work around this issue, and possible bugs of other OSes, timeout
127+ // is clamped to 1000 years, which is allowable per the API of `wait_timeout`
128+ // because of spurious wakeups.
129+
130+ dur = max_dur;
131+ }
132+
107133 // First, figure out what time it currently is, in both system and
108134 // stable time. pthread_cond_timedwait uses system time, but we want to
109135 // report timeout based on stable time.
@@ -116,7 +142,7 @@ impl Condvar {
116142 ( sys_now. tv_usec * 1000 ) as libc:: c_long ;
117143 let extra = ( nsec / 1_000_000_000 ) as libc:: time_t ;
118144 let nsec = nsec % 1_000_000_000 ;
119- let seconds = dur. as_secs ( ) as libc :: time_t ;
145+ let seconds = saturating_cast_to_time_t ( dur. as_secs ( ) ) ;
120146
121147 let timeout = sys_now. tv_sec . checked_add ( extra) . and_then ( |s| {
122148 s. checked_add ( seconds)
0 commit comments