1010
1111use cell:: UnsafeCell ;
1212use libc;
13- use ptr;
1413use sys:: mutex:: { self , Mutex } ;
15- use time:: { Instant , Duration } ;
14+ use time:: Duration ;
1615
1716pub struct Condvar { inner : UnsafeCell < libc:: pthread_cond_t > }
1817
1918unsafe impl Send for Condvar { }
2019unsafe impl Sync for Condvar { }
2120
21+ const TIMESPEC_MAX : libc:: timespec = libc:: timespec {
22+ tv_sec : <libc:: time_t >:: max_value ( ) ,
23+ tv_nsec : 1_000_000_000 - 1 ,
24+ } ;
25+
2226impl Condvar {
2327 pub const fn new ( ) -> Condvar {
2428 // Might be moved and address is changing it is better to avoid
2529 // initialization of potentially opaque OS data before it landed
2630 Condvar { inner : UnsafeCell :: new ( libc:: PTHREAD_COND_INITIALIZER ) }
2731 }
2832
33+ #[ cfg( any( target_os = "macos" , target_os = "ios" , target_os = "android" ) ) ]
34+ pub unsafe fn init ( & mut self ) { }
35+
36+ #[ cfg( not( any( target_os = "macos" , target_os = "ios" , target_os = "android" ) ) ) ]
37+ pub unsafe fn init ( & mut self ) {
38+ use mem;
39+ let mut attr: libc:: pthread_condattr_t = mem:: uninitialized ( ) ;
40+ let r = libc:: pthread_condattr_init ( & mut attr) ;
41+ assert_eq ! ( r, 0 ) ;
42+ let r = libc:: pthread_condattr_setclock ( & mut attr, libc:: CLOCK_MONOTONIC ) ;
43+ assert_eq ! ( r, 0 ) ;
44+ let r = libc:: pthread_cond_init ( self . inner . get ( ) , & attr) ;
45+ assert_eq ! ( r, 0 ) ;
46+ let r = libc:: pthread_condattr_destroy ( & mut attr) ;
47+ assert_eq ! ( r, 0 ) ;
48+ }
49+
2950 #[ inline]
3051 pub unsafe fn notify_one ( & self ) {
3152 let r = libc:: pthread_cond_signal ( self . inner . get ( ) ) ;
@@ -44,10 +65,45 @@ impl Condvar {
4465 debug_assert_eq ! ( r, 0 ) ;
4566 }
4667
68+ // This implementation is used on systems that support pthread_condattr_setclock
69+ // where we configure condition variable to use monotonic clock (instead of
70+ // default system clock). This approach avoids all problems that result
71+ // from changes made to the system time.
72+ #[ cfg( not( any( target_os = "macos" , target_os = "ios" , target_os = "android" ) ) ) ]
73+ pub unsafe fn wait_timeout ( & self , mutex : & Mutex , dur : Duration ) -> bool {
74+ use mem;
75+
76+ let mut now: libc:: timespec = mem:: zeroed ( ) ;
77+ let r = libc:: clock_gettime ( libc:: CLOCK_MONOTONIC , & mut now) ;
78+ assert_eq ! ( r, 0 ) ;
79+
80+ // Nanosecond calculations can't overflow because both values are below 1e9.
81+ 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 )
84+ . checked_add ( ( nsec / 1_000_000_000 ) as libc:: time_t )
85+ . and_then ( |s| s. checked_add ( now. tv_sec ) ) ;
86+ let nsec = nsec % 1_000_000_000 ;
87+
88+ let timeout = sec. map ( |s| {
89+ libc:: timespec { tv_sec : s, tv_nsec : nsec }
90+ } ) . unwrap_or ( TIMESPEC_MAX ) ;
91+
92+ let r = libc:: pthread_cond_timedwait ( self . inner . get ( ) , mutex:: raw ( mutex) ,
93+ & timeout) ;
94+ assert ! ( r == libc:: ETIMEDOUT || r == 0 ) ;
95+ r == 0
96+ }
97+
98+
4799 // This implementation is modeled after libcxx's condition_variable
48100 // https://github.com/llvm-mirror/libcxx/blob/release_35/src/condition_variable.cpp#L46
49101 // https://github.com/llvm-mirror/libcxx/blob/release_35/include/__mutex_base#L367
102+ #[ cfg( any( target_os = "macos" , target_os = "ios" , target_os = "android" ) ) ]
50103 pub unsafe fn wait_timeout ( & self , mutex : & Mutex , dur : Duration ) -> bool {
104+ use ptr;
105+ use time:: Instant ;
106+
51107 // First, figure out what time it currently is, in both system and
52108 // stable time. pthread_cond_timedwait uses system time, but we want to
53109 // report timeout based on stable time.
@@ -66,12 +122,7 @@ impl Condvar {
66122 s. checked_add ( seconds)
67123 } ) . map ( |s| {
68124 libc:: timespec { tv_sec : s, tv_nsec : nsec }
69- } ) . unwrap_or_else ( || {
70- libc:: timespec {
71- tv_sec : <libc:: time_t >:: max_value ( ) ,
72- tv_nsec : 1_000_000_000 - 1 ,
73- }
74- } ) ;
125+ } ) . unwrap_or ( TIMESPEC_MAX ) ;
75126
76127 // And wait!
77128 let r = libc:: pthread_cond_timedwait ( self . inner . get ( ) , mutex:: raw ( mutex) ,
0 commit comments