@@ -23,6 +23,14 @@ const TIMESPEC_MAX: libc::timespec = libc::timespec {
23
23
tv_nsec : 1_000_000_000 - 1 ,
24
24
} ;
25
25
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
+
26
34
impl Condvar {
27
35
pub const fn new ( ) -> Condvar {
28
36
// Might be moved and address is changing it is better to avoid
@@ -79,8 +87,7 @@ impl Condvar {
79
87
80
88
// Nanosecond calculations can't overflow because both values are below 1e9.
81
89
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 ( ) )
84
91
. checked_add ( ( nsec / 1_000_000_000 ) as libc:: time_t )
85
92
. and_then ( |s| s. checked_add ( now. tv_sec ) ) ;
86
93
let nsec = nsec % 1_000_000_000 ;
@@ -100,10 +107,29 @@ impl Condvar {
100
107
// https://github.com/llvm-mirror/libcxx/blob/release_35/src/condition_variable.cpp#L46
101
108
// https://github.com/llvm-mirror/libcxx/blob/release_35/include/__mutex_base#L367
102
109
#[ 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 {
104
111
use ptr;
105
112
use time:: Instant ;
106
113
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
+
107
133
// First, figure out what time it currently is, in both system and
108
134
// stable time. pthread_cond_timedwait uses system time, but we want to
109
135
// report timeout based on stable time.
@@ -116,7 +142,7 @@ impl Condvar {
116
142
( sys_now. tv_usec * 1000 ) as libc:: c_long ;
117
143
let extra = ( nsec / 1_000_000_000 ) as libc:: time_t ;
118
144
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 ( ) ) ;
120
146
121
147
let timeout = sys_now. tv_sec . checked_add ( extra) . and_then ( |s| {
122
148
s. checked_add ( seconds)
0 commit comments