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.
10 sync
::atomic
::{AtomicI32, Ordering}
,
14 use std
::time
::Instant
;
16 // x32 Linux uses a non-standard type for tv_nsec in timespec.
17 // See https://sourceware.org/bugzilla/show_bug.cgi?id=16437
18 #[cfg(all(target_arch = "x86_64", target_pointer_width = "32"))]
19 #[allow(non_camel_case_types)]
21 #[cfg(not(all(target_arch = "x86_64", target_pointer_width = "32")))]
22 #[allow(non_camel_case_types)]
23 type tv_nsec_t
= libc
::c_long
;
25 fn errno() -> libc
::c_int
{
26 #[cfg(target_os = "linux")]
28 *libc
::__errno_location()
30 #[cfg(target_os = "android")]
36 // Helper type for putting a thread to sleep until some other thread wakes it up
37 pub struct ThreadParker
{
41 impl super::ThreadParkerT
for ThreadParker
{
42 type UnparkHandle
= UnparkHandle
;
44 const IS_CHEAP_TO_CONSTRUCT
: bool
= true;
47 fn new() -> ThreadParker
{
49 futex
: AtomicI32
::new(0),
54 unsafe fn prepare_park(&self) {
55 self.futex
.store(1, Ordering
::Relaxed
);
59 unsafe fn timed_out(&self) -> bool
{
60 self.futex
.load(Ordering
::Relaxed
) != 0
64 unsafe fn park(&self) {
65 while self.futex
.load(Ordering
::Acquire
) != 0 {
66 self.futex_wait(None
);
71 unsafe fn park_until(&self, timeout
: Instant
) -> bool
{
72 while self.futex
.load(Ordering
::Acquire
) != 0 {
73 let now
= Instant
::now();
77 let diff
= timeout
- now
;
78 if diff
.as_secs() as libc
::time_t
as u64 != diff
.as_secs() {
79 // Timeout overflowed, just sleep indefinitely
83 // SAFETY: libc::timespec is zero initializable.
84 let mut ts
: libc
::timespec
= std
::mem
::zeroed();
85 ts
.tv_sec
= diff
.as_secs() as libc
::time_t
;
86 ts
.tv_nsec
= diff
.subsec_nanos() as tv_nsec_t
;
87 self.futex_wait(Some(ts
));
92 // Locks the parker to prevent the target thread from exiting. This is
93 // necessary to ensure that thread-local ThreadData objects remain valid.
94 // This should be called while holding the queue lock.
96 unsafe fn unpark_lock(&self) -> UnparkHandle
{
97 // We don't need to lock anything, just clear the state
98 self.futex
.store(0, Ordering
::Release
);
100 UnparkHandle { futex: &self.futex }
106 fn futex_wait(&self, ts
: Option
<libc
::timespec
>) {
109 .map(|ts_ref
| ts_ref
as *const _
)
110 .unwrap_or(ptr
::null());
115 libc
::FUTEX_WAIT
| libc
::FUTEX_PRIVATE_FLAG
,
120 debug_assert
!(r
== 0 || r
== -1);
123 errno() == libc
::EINTR
124 || errno() == libc
::EAGAIN
125 || (ts
.is_some() && errno() == libc
::ETIMEDOUT
)
131 pub struct UnparkHandle
{
132 futex
: *const AtomicI32
,
135 impl super::UnparkHandleT
for UnparkHandle
{
137 unsafe fn unpark(self) {
138 // The thread data may have been freed at this point, but it doesn't
139 // matter since the syscall will just return EFAULT in that case.
140 let r
= libc
::syscall(
143 libc
::FUTEX_WAKE
| libc
::FUTEX_PRIVATE_FLAG
,
146 debug_assert
!(r
== 0 || r
== 1 || r
== -1);
148 debug_assert_eq
!(errno(), libc
::EFAULT
);
154 pub fn thread_yield() {