1 // Copyright 2016 Amanieu d'Antras
3 // Licensed under the Apache License, Version 2.0, <LICENSE-APACHE or
4 // http://apache.org/licenses/LICENSE-2.0> or the MIT license <LICENSE-MIT or
5 // http://opensource.org/licenses/MIT>, at your option. This file may not be
6 // copied, modified, or distributed except according to those terms.
8 #[cfg(any(target_os = "macos", target_os = "ios"))]
11 cell
::{Cell, UnsafeCell}
,
17 time
::{Duration, Instant}
,
20 // x32 Linux uses a non-standard type for tv_nsec in timespec.
21 // See https://sourceware.org/bugzilla/show_bug.cgi?id=16437
22 #[cfg(all(target_arch = "x86_64", target_pointer_width = "32"))]
23 #[allow(non_camel_case_types)]
25 #[cfg(not(all(target_arch = "x86_64", target_pointer_width = "32")))]
26 #[allow(non_camel_case_types)]
27 type tv_nsec_t
= libc
::c_long
;
29 // Helper type for putting a thread to sleep until some other thread wakes it up
30 pub struct ThreadParker
{
31 should_park
: Cell
<bool
>,
32 mutex
: UnsafeCell
<libc
::pthread_mutex_t
>,
33 condvar
: UnsafeCell
<libc
::pthread_cond_t
>,
34 initialized
: Cell
<bool
>,
37 impl super::ThreadParkerT
for ThreadParker
{
38 type UnparkHandle
= UnparkHandle
;
40 const IS_CHEAP_TO_CONSTRUCT
: bool
= false;
43 fn new() -> ThreadParker
{
45 should_park
: Cell
::new(false),
46 mutex
: UnsafeCell
::new(libc
::PTHREAD_MUTEX_INITIALIZER
),
47 condvar
: UnsafeCell
::new(libc
::PTHREAD_COND_INITIALIZER
),
48 initialized
: Cell
::new(false),
53 unsafe fn prepare_park(&self) {
54 self.should_park
.set(true);
55 if !self.initialized
.get() {
57 self.initialized
.set(true);
62 unsafe fn timed_out(&self) -> bool
{
63 // We need to grab the mutex here because another thread may be
64 // concurrently executing UnparkHandle::unpark, which is done without
65 // holding the queue lock.
66 let r
= libc
::pthread_mutex_lock(self.mutex
.get());
67 debug_assert_eq
!(r
, 0);
68 let should_park
= self.should_park
.get();
69 let r
= libc
::pthread_mutex_unlock(self.mutex
.get());
70 debug_assert_eq
!(r
, 0);
75 unsafe fn park(&self) {
76 let r
= libc
::pthread_mutex_lock(self.mutex
.get());
77 debug_assert_eq
!(r
, 0);
78 while self.should_park
.get() {
79 let r
= libc
::pthread_cond_wait(self.condvar
.get(), self.mutex
.get());
80 debug_assert_eq
!(r
, 0);
82 let r
= libc
::pthread_mutex_unlock(self.mutex
.get());
83 debug_assert_eq
!(r
, 0);
87 unsafe fn park_until(&self, timeout
: Instant
) -> bool
{
88 let r
= libc
::pthread_mutex_lock(self.mutex
.get());
89 debug_assert_eq
!(r
, 0);
90 while self.should_park
.get() {
91 let now
= Instant
::now();
93 let r
= libc
::pthread_mutex_unlock(self.mutex
.get());
94 debug_assert_eq
!(r
, 0);
98 if let Some(ts
) = timeout_to_timespec(timeout
- now
) {
99 let r
= libc
::pthread_cond_timedwait(self.condvar
.get(), self.mutex
.get(), &ts
);
101 // On some systems, negative timeouts will return EINVAL. In
102 // that case we won't sleep and will just busy loop instead,
103 // which is the best we can do.
104 debug_assert
!(r
== 0 || r
== libc
::ETIMEDOUT
|| r
== libc
::EINVAL
);
106 debug_assert
!(r
== 0 || r
== libc
::ETIMEDOUT
);
109 // Timeout calculation overflowed, just sleep indefinitely
110 let r
= libc
::pthread_cond_wait(self.condvar
.get(), self.mutex
.get());
111 debug_assert_eq
!(r
, 0);
114 let r
= libc
::pthread_mutex_unlock(self.mutex
.get());
115 debug_assert_eq
!(r
, 0);
120 unsafe fn unpark_lock(&self) -> UnparkHandle
{
121 let r
= libc
::pthread_mutex_lock(self.mutex
.get());
122 debug_assert_eq
!(r
, 0);
131 /// Initializes the condvar to use CLOCK_MONOTONIC instead of CLOCK_REALTIME.
132 #[cfg(any(target_os = "macos", target_os = "ios", target_os = "android"))]
134 unsafe fn init(&self) {}
136 /// Initializes the condvar to use CLOCK_MONOTONIC instead of CLOCK_REALTIME.
137 #[cfg(not(any(target_os = "macos", target_os = "ios", target_os = "android")))]
139 unsafe fn init(&self) {
140 let mut attr
= MaybeUninit
::<libc
::pthread_condattr_t
>::uninit();
141 let r
= libc
::pthread_condattr_init(attr
.as_mut_ptr());
142 debug_assert_eq
!(r
, 0);
143 let r
= libc
::pthread_condattr_setclock(attr
.as_mut_ptr(), libc
::CLOCK_MONOTONIC
);
144 debug_assert_eq
!(r
, 0);
145 let r
= libc
::pthread_cond_init(self.condvar
.get(), attr
.as_ptr());
146 debug_assert_eq
!(r
, 0);
147 let r
= libc
::pthread_condattr_destroy(attr
.as_mut_ptr());
148 debug_assert_eq
!(r
, 0);
152 impl Drop
for ThreadParker
{
155 // On DragonFly pthread_mutex_destroy() returns EINVAL if called on a
156 // mutex that was just initialized with libc::PTHREAD_MUTEX_INITIALIZER.
157 // Once it is used (locked/unlocked) or pthread_mutex_init() is called,
158 // this behaviour no longer occurs. The same applies to condvars.
160 let r
= libc
::pthread_mutex_destroy(self.mutex
.get());
161 if cfg
!(target_os
= "dragonfly") {
162 debug_assert
!(r
== 0 || r
== libc
::EINVAL
);
164 debug_assert_eq
!(r
, 0);
166 let r
= libc
::pthread_cond_destroy(self.condvar
.get());
167 if cfg
!(target_os
= "dragonfly") {
168 debug_assert
!(r
== 0 || r
== libc
::EINVAL
);
170 debug_assert_eq
!(r
, 0);
176 pub struct UnparkHandle
{
177 thread_parker
: *const ThreadParker
,
180 impl super::UnparkHandleT
for UnparkHandle
{
182 unsafe fn unpark(self) {
183 (*self.thread_parker
).should_park
.set(false);
185 // We notify while holding the lock here to avoid races with the target
186 // thread. In particular, the thread could exit after we unlock the
187 // mutex, which would make the condvar access invalid memory.
188 let r
= libc
::pthread_cond_signal((*self.thread_parker
).condvar
.get());
189 debug_assert_eq
!(r
, 0);
190 let r
= libc
::pthread_mutex_unlock((*self.thread_parker
).mutex
.get());
191 debug_assert_eq
!(r
, 0);
195 // Returns the current time on the clock used by pthread_cond_t as a timespec.
196 #[cfg(any(target_os = "macos", target_os = "ios"))]
198 fn timespec_now() -> libc
::timespec
{
199 let mut now
= MaybeUninit
::<libc
::timeval
>::uninit();
200 let r
= unsafe { libc::gettimeofday(now.as_mut_ptr(), ptr::null_mut()) }
;
201 debug_assert_eq
!(r
, 0);
202 // SAFETY: We know `libc::gettimeofday` has initialized the value.
203 let now
= unsafe { now.assume_init() }
;
206 tv_nsec
: now
.tv_usec
as tv_nsec_t
* 1000,
209 #[cfg(not(any(target_os = "macos", target_os = "ios")))]
211 fn timespec_now() -> libc
::timespec
{
212 let mut now
= MaybeUninit
::<libc
::timespec
>::uninit();
213 let clock
= if cfg
!(target_os
= "android") {
214 // Android doesn't support pthread_condattr_setclock, so we need to
215 // specify the timeout in CLOCK_REALTIME.
218 libc
::CLOCK_MONOTONIC
220 let r
= unsafe { libc::clock_gettime(clock, now.as_mut_ptr()) }
;
221 debug_assert_eq
!(r
, 0);
222 // SAFETY: We know `libc::clock_gettime` has initialized the value.
223 unsafe { now.assume_init() }
226 // Converts a relative timeout into an absolute timeout in the clock used by
229 fn timeout_to_timespec(timeout
: Duration
) -> Option
<libc
::timespec
> {
230 // Handle overflows early on
231 if timeout
.as_secs() > libc
::time_t
::max_value() as u64 {
235 let now
= timespec_now();
236 let mut nsec
= now
.tv_nsec
+ timeout
.subsec_nanos() as tv_nsec_t
;
237 let mut sec
= now
.tv_sec
.checked_add(timeout
.as_secs() as libc
::time_t
);
238 if nsec
>= 1_000_000_000 {
239 nsec
-= 1_000_000_000;
240 sec
= sec
.and_then(|sec
| sec
.checked_add(1));
243 sec
.map(|sec
| libc
::timespec
{
250 pub fn thread_yield() {