]> git.proxmox.com Git - rustc.git/blob - vendor/parking_lot_core/src/thread_parker/linux.rs
New upstream version 1.44.1+dfsg1
[rustc.git] / vendor / parking_lot_core / src / thread_parker / linux.rs
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 libc;
13 use std::{thread, time::Instant};
14
15 // x32 Linux uses a non-standard type for tv_nsec in timespec.
16 // See https://sourceware.org/bugzilla/show_bug.cgi?id=16437
17 #[cfg(all(target_arch = "x86_64", target_pointer_width = "32"))]
18 #[allow(non_camel_case_types)]
19 type tv_nsec_t = i64;
20 #[cfg(not(all(target_arch = "x86_64", target_pointer_width = "32")))]
21 #[allow(non_camel_case_types)]
22 type tv_nsec_t = libc::c_long;
23
24 fn errno() -> libc::c_int {
25 #[cfg(target_os = "linux")]
26 unsafe {
27 *libc::__errno_location()
28 }
29 #[cfg(target_os = "android")]
30 unsafe {
31 *libc::__errno()
32 }
33 }
34
35 // Helper type for putting a thread to sleep until some other thread wakes it up
36 pub struct ThreadParker {
37 futex: AtomicI32,
38 }
39
40 impl super::ThreadParkerT for ThreadParker {
41 type UnparkHandle = UnparkHandle;
42
43 const IS_CHEAP_TO_CONSTRUCT: bool = true;
44
45 #[inline]
46 fn new() -> ThreadParker {
47 ThreadParker {
48 futex: AtomicI32::new(0),
49 }
50 }
51
52 #[inline]
53 unsafe fn prepare_park(&self) {
54 self.futex.store(1, Ordering::Relaxed);
55 }
56
57 #[inline]
58 unsafe fn timed_out(&self) -> bool {
59 self.futex.load(Ordering::Relaxed) != 0
60 }
61
62 #[inline]
63 unsafe fn park(&self) {
64 while self.futex.load(Ordering::Acquire) != 0 {
65 self.futex_wait(None);
66 }
67 }
68
69 #[inline]
70 unsafe fn park_until(&self, timeout: Instant) -> bool {
71 while self.futex.load(Ordering::Acquire) != 0 {
72 let now = Instant::now();
73 if timeout <= now {
74 return false;
75 }
76 let diff = timeout - now;
77 if diff.as_secs() as libc::time_t as u64 != diff.as_secs() {
78 // Timeout overflowed, just sleep indefinitely
79 self.park();
80 return true;
81 }
82 let ts = libc::timespec {
83 tv_sec: diff.as_secs() as libc::time_t,
84 tv_nsec: diff.subsec_nanos() as tv_nsec_t,
85 };
86 self.futex_wait(Some(ts));
87 }
88 true
89 }
90
91 // Locks the parker to prevent the target thread from exiting. This is
92 // necessary to ensure that thread-local ThreadData objects remain valid.
93 // This should be called while holding the queue lock.
94 #[inline]
95 unsafe fn unpark_lock(&self) -> UnparkHandle {
96 // We don't need to lock anything, just clear the state
97 self.futex.store(0, Ordering::Release);
98
99 UnparkHandle { futex: &self.futex }
100 }
101 }
102
103 impl ThreadParker {
104 #[inline]
105 fn futex_wait(&self, ts: Option<libc::timespec>) {
106 let ts_ptr = ts
107 .as_ref()
108 .map(|ts_ref| ts_ref as *const _)
109 .unwrap_or(ptr::null());
110 let r = unsafe {
111 libc::syscall(
112 libc::SYS_futex,
113 &self.futex,
114 libc::FUTEX_WAIT | libc::FUTEX_PRIVATE_FLAG,
115 1,
116 ts_ptr,
117 )
118 };
119 debug_assert!(r == 0 || r == -1);
120 if r == -1 {
121 debug_assert!(
122 errno() == libc::EINTR
123 || errno() == libc::EAGAIN
124 || (ts.is_some() && errno() == libc::ETIMEDOUT)
125 );
126 }
127 }
128 }
129
130 pub struct UnparkHandle {
131 futex: *const AtomicI32,
132 }
133
134 impl super::UnparkHandleT for UnparkHandle {
135 #[inline]
136 unsafe fn unpark(self) {
137 // The thread data may have been freed at this point, but it doesn't
138 // matter since the syscall will just return EFAULT in that case.
139 let r = libc::syscall(
140 libc::SYS_futex,
141 self.futex,
142 libc::FUTEX_WAKE | libc::FUTEX_PRIVATE_FLAG,
143 1,
144 );
145 debug_assert!(r == 0 || r == 1 || r == -1);
146 if r == -1 {
147 debug_assert_eq!(errno(), libc::EFAULT);
148 }
149 }
150 }
151
152 #[inline]
153 pub fn thread_yield() {
154 thread::yield_now();
155 }