10
10
11
11
use cell:: UnsafeCell ;
12
12
use libc;
13
- use ptr;
14
13
use sys:: mutex:: { self , Mutex } ;
15
- use time:: { Instant , Duration } ;
14
+ use time:: Duration ;
16
15
17
16
pub struct Condvar { inner : UnsafeCell < libc:: pthread_cond_t > }
18
17
19
18
unsafe impl Send for Condvar { }
20
19
unsafe impl Sync for Condvar { }
21
20
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
+
22
26
impl Condvar {
23
27
pub const fn new ( ) -> Condvar {
24
28
// Might be moved and address is changing it is better to avoid
25
29
// initialization of potentially opaque OS data before it landed
26
30
Condvar { inner : UnsafeCell :: new ( libc:: PTHREAD_COND_INITIALIZER ) }
27
31
}
28
32
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
+
29
50
#[ inline]
30
51
pub unsafe fn notify_one ( & self ) {
31
52
let r = libc:: pthread_cond_signal ( self . inner . get ( ) ) ;
@@ -44,10 +65,45 @@ impl Condvar {
44
65
debug_assert_eq ! ( r, 0 ) ;
45
66
}
46
67
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
+
47
99
// This implementation is modeled after libcxx's condition_variable
48
100
// https://github.com/llvm-mirror/libcxx/blob/release_35/src/condition_variable.cpp#L46
49
101
// 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" ) ) ]
50
103
pub unsafe fn wait_timeout ( & self , mutex : & Mutex , dur : Duration ) -> bool {
104
+ use ptr;
105
+ use time:: Instant ;
106
+
51
107
// First, figure out what time it currently is, in both system and
52
108
// stable time. pthread_cond_timedwait uses system time, but we want to
53
109
// report timeout based on stable time.
@@ -66,12 +122,7 @@ impl Condvar {
66
122
s. checked_add ( seconds)
67
123
} ) . map ( |s| {
68
124
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 ) ;
75
126
76
127
// And wait!
77
128
let r = libc:: pthread_cond_timedwait ( self . inner . get ( ) , mutex:: raw ( mutex) ,
0 commit comments