1 // Copyright 2016 Amanieu d'Antras
2 //
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.
7 
8 use core::{
9     ptr,
10     sync::atomic::{AtomicI32, Ordering},
11 };
12 use instant::Instant;
13 use libc;
14 use std::thread;
15 
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)]
20 type tv_nsec_t = i64;
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;
24 
errno() -> libc::c_int25 fn errno() -> libc::c_int {
26     #[cfg(target_os = "linux")]
27     unsafe {
28         *libc::__errno_location()
29     }
30     #[cfg(target_os = "android")]
31     unsafe {
32         *libc::__errno()
33     }
34 }
35 
36 // Helper type for putting a thread to sleep until some other thread wakes it up
37 pub struct ThreadParker {
38     futex: AtomicI32,
39 }
40 
41 impl super::ThreadParkerT for ThreadParker {
42     type UnparkHandle = UnparkHandle;
43 
44     const IS_CHEAP_TO_CONSTRUCT: bool = true;
45 
46     #[inline]
new() -> ThreadParker47     fn new() -> ThreadParker {
48         ThreadParker {
49             futex: AtomicI32::new(0),
50         }
51     }
52 
53     #[inline]
prepare_park(&self)54     unsafe fn prepare_park(&self) {
55         self.futex.store(1, Ordering::Relaxed);
56     }
57 
58     #[inline]
timed_out(&self) -> bool59     unsafe fn timed_out(&self) -> bool {
60         self.futex.load(Ordering::Relaxed) != 0
61     }
62 
63     #[inline]
park(&self)64     unsafe fn park(&self) {
65         while self.futex.load(Ordering::Acquire) != 0 {
66             self.futex_wait(None);
67         }
68     }
69 
70     #[inline]
park_until(&self, timeout: Instant) -> bool71     unsafe fn park_until(&self, timeout: Instant) -> bool {
72         while self.futex.load(Ordering::Acquire) != 0 {
73             let now = Instant::now();
74             if timeout <= now {
75                 return false;
76             }
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
80                 self.park();
81                 return true;
82             }
83             let ts = libc::timespec {
84                 tv_sec: diff.as_secs() as libc::time_t,
85                 tv_nsec: diff.subsec_nanos() as tv_nsec_t,
86             };
87             self.futex_wait(Some(ts));
88         }
89         true
90     }
91 
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.
95     #[inline]
unpark_lock(&self) -> UnparkHandle96     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);
99 
100         UnparkHandle { futex: &self.futex }
101     }
102 }
103 
104 impl ThreadParker {
105     #[inline]
futex_wait(&self, ts: Option<libc::timespec>)106     fn futex_wait(&self, ts: Option<libc::timespec>) {
107         let ts_ptr = ts
108             .as_ref()
109             .map(|ts_ref| ts_ref as *const _)
110             .unwrap_or(ptr::null());
111         let r = unsafe {
112             libc::syscall(
113                 libc::SYS_futex,
114                 &self.futex,
115                 libc::FUTEX_WAIT | libc::FUTEX_PRIVATE_FLAG,
116                 1,
117                 ts_ptr,
118             )
119         };
120         debug_assert!(r == 0 || r == -1);
121         if r == -1 {
122             debug_assert!(
123                 errno() == libc::EINTR
124                     || errno() == libc::EAGAIN
125                     || (ts.is_some() && errno() == libc::ETIMEDOUT)
126             );
127         }
128     }
129 }
130 
131 pub struct UnparkHandle {
132     futex: *const AtomicI32,
133 }
134 
135 impl super::UnparkHandleT for UnparkHandle {
136     #[inline]
unpark(self)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(
141             libc::SYS_futex,
142             self.futex,
143             libc::FUTEX_WAKE | libc::FUTEX_PRIVATE_FLAG,
144             1,
145         );
146         debug_assert!(r == 0 || r == 1 || r == -1);
147         if r == -1 {
148             debug_assert_eq!(errno(), libc::EFAULT);
149         }
150     }
151 }
152 
153 #[inline]
thread_yield()154 pub fn thread_yield() {
155     thread::yield_now();
156 }
157