parking_lot_core/thread_parker/
linux.rs1use core::{
2 ptr::{self, NonNull},
3 sync::atomic::{AtomicBool, AtomicI32, Ordering},
4};
5use std::thread;
6use std::time::{Duration, Instant};
7
8fn errno() -> libc::c_int {
9 #[cfg(target_os = "linux")]
10 unsafe {
11 *libc::__errno_location()
12 }
13 #[cfg(target_os = "android")]
14 unsafe {
15 *libc::__errno()
16 }
17}
18
19#[inline]
20fn raw_futex_syscall(
21 futex: *const AtomicI32,
22 syscall: libc::c_long,
23 operation: libc::c_int,
24 timeout: *const libc::c_void,
25) -> Result<libc::c_long, libc::c_int> {
26 let result = unsafe {
27 libc::syscall(
28 syscall,
29 futex.cast_mut().cast::<u32>(),
30 operation,
31 1,
32 timeout.cast_mut().cast::<libc::timespec>(),
33 )
34 };
35 if result == -1 {
36 Err(errno())
37 } else {
38 Ok(result)
39 }
40}
41
42cfg_select! {
46 target_arch = "m68k" => {
47 const SYS_FUTEX_TIME32: Option<libc::c_long> = Some(libc::SYS_futex_time32);
48 const SYS_FUTEX_TIME64: Option<libc::c_long> = Some(libc::SYS_futex as libc::c_long);
49 }
50 any(target_arch = "mips", target_arch = "mips32r6") => {
51 const SYS_FUTEX_TIME32: Option<libc::c_long> = Some(libc::SYS_futex as libc::c_long);
52 const SYS_FUTEX_TIME64: Option<libc::c_long> = Some(4000 + 422);
54 }
55 any(target_arch = "hexagon", target_arch = "riscv32") => {
56 const SYS_FUTEX_TIME32: Option<libc::c_long> = None;
57 const SYS_FUTEX_TIME64: Option<libc::c_long> = Some(422);
58 }
59 all(target_pointer_width = "32", not(target_arch = "x86_64")) => {
60 const SYS_FUTEX_TIME32: Option<libc::c_long> = Some(libc::SYS_futex as libc::c_long);
61 const SYS_FUTEX_TIME64: Option<libc::c_long> = Some(422);
62 }
63 _ => {
64 const SYS_FUTEX_TIME32: Option<libc::c_long> = None;
65 const SYS_FUTEX_TIME64: Option<libc::c_long> = Some(libc::SYS_futex as libc::c_long);
66 }
67}
68
69const _: () = if !(SYS_FUTEX_TIME32.is_some() || SYS_FUTEX_TIME64.is_some()) {
::core::panicking::panic("assertion failed: SYS_FUTEX_TIME32.is_some() || SYS_FUTEX_TIME64.is_some()")
}assert!(SYS_FUTEX_TIME32.is_some() || SYS_FUTEX_TIME64.is_some());
70
71#[repr(C)]
72struct Timespec32 {
73 tv_sec: i32,
74 tv_nsec: i32,
75}
76
77#[repr(C)]
78struct Timespec64 {
79 tv_sec: i64,
80 tv_nsec: i64,
81}
82
83static FUTEX_TIME64_SUPPORTED: AtomicBool = AtomicBool::new(true);
87
88#[inline]
89fn futex_syscall(
90 futex: *const AtomicI32,
91 operation: libc::c_int,
92 timeout32: *const libc::c_void,
93 timeout64: *const libc::c_void,
94) -> Result<libc::c_long, libc::c_int> {
95 match (SYS_FUTEX_TIME32, SYS_FUTEX_TIME64) {
96 (Some(time32), Some(time64)) => {
97 if FUTEX_TIME64_SUPPORTED.load(Ordering::Relaxed) {
98 match raw_futex_syscall(futex, time64, operation, timeout64) {
99 Err(libc::ENOSYS) => {
100 FUTEX_TIME64_SUPPORTED.store(false, Ordering::Relaxed);
101 }
102 result => return result,
103 }
104 }
105 raw_futex_syscall(futex, time32, operation, timeout32)
106 }
107 (Some(time32), None) => raw_futex_syscall(futex, time32, operation, timeout32),
108 (None, Some(time64)) => raw_futex_syscall(futex, time64, operation, timeout64),
109 (None, None) => ::core::panicking::unreachable_default_msg()unreachable!(),
110 }
111}
112
113pub struct ThreadParker {
115 futex: AtomicI32,
116}
117
118impl super::ThreadParkerT for ThreadParker {
119 type UnparkHandle = UnparkHandle;
120
121 const IS_CHEAP_TO_CONSTRUCT: bool = true;
122
123 #[inline]
124 fn new() -> ThreadParker {
125 ThreadParker {
126 futex: AtomicI32::new(0),
127 }
128 }
129
130 #[inline]
131 unsafe fn prepare_park(&self) {
132 self.futex.store(1, Ordering::Relaxed);
133 }
134
135 #[inline]
136 unsafe fn timed_out(&self) -> bool {
137 self.futex.load(Ordering::Relaxed) != 0
138 }
139
140 #[inline]
141 unsafe fn park(&self) {
142 while self.futex.load(Ordering::Acquire) != 0 {
143 self.futex_wait(None);
144 }
145 }
146
147 #[inline]
148 unsafe fn park_until(&self, timeout: Instant) -> bool {
149 while self.futex.load(Ordering::Acquire) != 0 {
150 let now = Instant::now();
151 if timeout <= now {
152 return false;
153 }
154 let diff = timeout - now;
155 self.futex_wait(Some(diff));
156 }
157 true
158 }
159
160 #[inline]
163 unsafe fn unpark_lock(&self) -> UnparkHandle {
164 let handle = UnparkHandle {
167 futex: NonNull::from(&self.futex),
168 };
169
170 self.futex.store(0, Ordering::Release);
172 handle
173 }
174}
175
176impl ThreadParker {
177 #[inline]
178 fn futex_wait(&self, timeout: Option<Duration>) {
179 let timed = timeout.is_some();
180 let ts32 = timeout.map(|timeout| Timespec32 {
181 tv_sec: i32::try_from(timeout.as_secs()).unwrap_or(i32::MAX),
182 tv_nsec: timeout.subsec_nanos() as i32,
183 });
184 let ts64 = timeout.map(|timeout| Timespec64 {
185 tv_sec: i64::try_from(timeout.as_secs()).unwrap_or(i64::MAX),
186 tv_nsec: i64::from(timeout.subsec_nanos()),
187 });
188 let ts32_ptr = match &ts32 {
189 Some(ts) => ptr::from_ref(ts).cast(),
190 None => ptr::null(),
191 };
192 let ts64_ptr = match &ts64 {
193 Some(ts) => ptr::from_ref(ts).cast(),
194 None => ptr::null(),
195 };
196 let result = futex_syscall(
197 &self.futex,
198 libc::FUTEX_WAIT | libc::FUTEX_PRIVATE_FLAG,
199 ts32_ptr,
200 ts64_ptr,
201 );
202
203 match result {
204 Ok(0) | Err(libc::EINTR) | Err(libc::EAGAIN) => {}
205 Err(libc::ETIMEDOUT) if timed => {}
206 Ok(result) => {
::core::panicking::panic_fmt(format_args!("unexpected futex wait result: {0}",
result));
}panic!("unexpected futex wait result: {result}"),
207 Err(error) => {
::core::panicking::panic_fmt(format_args!("futex wait failed with error {0}",
error));
}panic!("futex wait failed with error {error}"),
208 }
209 }
210}
211
212pub struct UnparkHandle {
213 futex: NonNull<AtomicI32>,
214}
215
216impl super::UnparkHandleT for UnparkHandle {
217 #[inline]
218 unsafe fn unpark(self) {
219 let result = futex_syscall(
222 self.futex.as_ptr(),
223 libc::FUTEX_WAKE | libc::FUTEX_PRIVATE_FLAG,
224 ptr::null(),
225 ptr::null(),
226 );
227
228 match result {
229 Ok(0) | Ok(1) | Err(libc::EFAULT) => {}
230 Ok(result) => {
::core::panicking::panic_fmt(format_args!("unexpected futex wake result: {0}",
result));
}panic!("unexpected futex wake result: {result}"),
231 Err(error) => {
::core::panicking::panic_fmt(format_args!("futex wake failed with error {0}",
error));
}panic!("futex wake failed with error {error}"),
232 }
233 }
234}
235
236#[inline]
237pub fn thread_yield() {
238 thread::yield_now();
239}