parking_lot_core/thread_parker/
unix.rs
1#[cfg(any(target_os = "macos", target_os = "tvos", target_os = "ios", target_os = "watchos"))]
9use core::ptr;
10use core::{
11 cell::{Cell, UnsafeCell},
12 mem::MaybeUninit,
13};
14use libc;
15use std::time::Instant;
16use std::{thread, time::Duration};
17
18#[cfg(all(target_arch = "x86_64", target_pointer_width = "32"))]
21#[allow(non_camel_case_types)]
22type tv_nsec_t = i64;
23#[cfg(not(all(target_arch = "x86_64", target_pointer_width = "32")))]
24#[allow(non_camel_case_types)]
25type tv_nsec_t = libc::c_long;
26
27pub struct ThreadParker {
29 should_park: Cell<bool>,
30 mutex: UnsafeCell<libc::pthread_mutex_t>,
31 condvar: UnsafeCell<libc::pthread_cond_t>,
32 initialized: Cell<bool>,
33}
34
35impl super::ThreadParkerT for ThreadParker {
36 type UnparkHandle = UnparkHandle;
37
38 const IS_CHEAP_TO_CONSTRUCT: bool = false;
39
40 #[inline]
41 fn new() -> ThreadParker {
42 ThreadParker {
43 should_park: Cell::new(false),
44 mutex: UnsafeCell::new(libc::PTHREAD_MUTEX_INITIALIZER),
45 condvar: UnsafeCell::new(libc::PTHREAD_COND_INITIALIZER),
46 initialized: Cell::new(false),
47 }
48 }
49
50 #[inline]
51 unsafe fn prepare_park(&self) {
52 self.should_park.set(true);
53 if !self.initialized.get() {
54 self.init();
55 self.initialized.set(true);
56 }
57 }
58
59 #[inline]
60 unsafe fn timed_out(&self) -> bool {
61 let r = libc::pthread_mutex_lock(self.mutex.get());
65 debug_assert_eq!(r, 0);
66 let should_park = self.should_park.get();
67 let r = libc::pthread_mutex_unlock(self.mutex.get());
68 debug_assert_eq!(r, 0);
69 should_park
70 }
71
72 #[inline]
73 unsafe fn park(&self) {
74 let r = libc::pthread_mutex_lock(self.mutex.get());
75 debug_assert_eq!(r, 0);
76 while self.should_park.get() {
77 let r = libc::pthread_cond_wait(self.condvar.get(), self.mutex.get());
78 debug_assert_eq!(r, 0);
79 }
80 let r = libc::pthread_mutex_unlock(self.mutex.get());
81 debug_assert_eq!(r, 0);
82 }
83
84 #[inline]
85 unsafe fn park_until(&self, timeout: Instant) -> bool {
86 let r = libc::pthread_mutex_lock(self.mutex.get());
87 debug_assert_eq!(r, 0);
88 while self.should_park.get() {
89 let now = Instant::now();
90 if timeout <= now {
91 let r = libc::pthread_mutex_unlock(self.mutex.get());
92 debug_assert_eq!(r, 0);
93 return false;
94 }
95
96 if let Some(ts) = timeout_to_timespec(timeout - now) {
97 let r = libc::pthread_cond_timedwait(self.condvar.get(), self.mutex.get(), &ts);
98 if ts.tv_sec < 0 {
99 debug_assert!(r == 0 || r == libc::ETIMEDOUT || r == libc::EINVAL);
103 } else {
104 debug_assert!(r == 0 || r == libc::ETIMEDOUT);
105 }
106 } else {
107 let r = libc::pthread_cond_wait(self.condvar.get(), self.mutex.get());
109 debug_assert_eq!(r, 0);
110 }
111 }
112 let r = libc::pthread_mutex_unlock(self.mutex.get());
113 debug_assert_eq!(r, 0);
114 true
115 }
116
117 #[inline]
118 unsafe fn unpark_lock(&self) -> UnparkHandle {
119 let r = libc::pthread_mutex_lock(self.mutex.get());
120 debug_assert_eq!(r, 0);
121
122 UnparkHandle {
123 thread_parker: self,
124 }
125 }
126}
127
128impl ThreadParker {
129 #[cfg(any(
131 target_os = "macos",
132 target_os = "ios",
133 target_os = "tvos",
134 target_os = "watchos",
135 target_os = "android",
136 target_os = "espidf"
137 ))]
138 #[inline]
139 unsafe fn init(&self) {}
140
141 #[cfg(not(any(
143 target_os = "macos",
144 target_os = "ios",
145 target_os = "tvos",
146 target_os = "watchos",
147 target_os = "android",
148 target_os = "espidf"
149 )))]
150 #[inline]
151 unsafe fn init(&self) {
152 let mut attr = MaybeUninit::<libc::pthread_condattr_t>::uninit();
153 let r = libc::pthread_condattr_init(attr.as_mut_ptr());
154 debug_assert_eq!(r, 0);
155 let r = libc::pthread_condattr_setclock(attr.as_mut_ptr(), libc::CLOCK_MONOTONIC);
156 debug_assert_eq!(r, 0);
157 let r = libc::pthread_cond_init(self.condvar.get(), attr.as_ptr());
158 debug_assert_eq!(r, 0);
159 let r = libc::pthread_condattr_destroy(attr.as_mut_ptr());
160 debug_assert_eq!(r, 0);
161 }
162}
163
164impl Drop for ThreadParker {
165 #[inline]
166 fn drop(&mut self) {
167 unsafe {
172 let r = libc::pthread_mutex_destroy(self.mutex.get());
173 debug_assert!(r == 0 || r == libc::EINVAL);
174 let r = libc::pthread_cond_destroy(self.condvar.get());
175 debug_assert!(r == 0 || r == libc::EINVAL);
176 }
177 }
178}
179
180pub struct UnparkHandle {
181 thread_parker: *const ThreadParker,
182}
183
184impl super::UnparkHandleT for UnparkHandle {
185 #[inline]
186 unsafe fn unpark(self) {
187 (*self.thread_parker).should_park.set(false);
188
189 let r = libc::pthread_cond_signal((*self.thread_parker).condvar.get());
193 debug_assert_eq!(r, 0);
194 let r = libc::pthread_mutex_unlock((*self.thread_parker).mutex.get());
195 debug_assert_eq!(r, 0);
196 }
197}
198
199#[cfg(any(target_os = "macos", target_os = "ios", target_os = "tvos", target_os = "watchos"))]
201#[inline]
202fn timespec_now() -> libc::timespec {
203 let mut now = MaybeUninit::<libc::timeval>::uninit();
204 let r = unsafe { libc::gettimeofday(now.as_mut_ptr(), ptr::null_mut()) };
205 debug_assert_eq!(r, 0);
206 let now = unsafe { now.assume_init() };
208 libc::timespec {
209 tv_sec: now.tv_sec,
210 tv_nsec: now.tv_usec as tv_nsec_t * 1000,
211 }
212}
213#[cfg(not(any(target_os = "macos", target_os = "ios", target_os = "tvos", target_os = "watchos")))]
214#[inline]
215fn timespec_now() -> libc::timespec {
216 let mut now = MaybeUninit::<libc::timespec>::uninit();
217 let clock = if cfg!(target_os = "android") {
218 libc::CLOCK_REALTIME
221 } else {
222 libc::CLOCK_MONOTONIC
223 };
224 let r = unsafe { libc::clock_gettime(clock, now.as_mut_ptr()) };
225 debug_assert_eq!(r, 0);
226 unsafe { now.assume_init() }
228}
229
230#[inline]
233fn timeout_to_timespec(timeout: Duration) -> Option<libc::timespec> {
234 if timeout.as_secs() > libc::time_t::max_value() as u64 {
236 return None;
237 }
238
239 let now = timespec_now();
240 let mut nsec = now.tv_nsec + timeout.subsec_nanos() as tv_nsec_t;
241 let mut sec = now.tv_sec.checked_add(timeout.as_secs() as libc::time_t);
242 if nsec >= 1_000_000_000 {
243 nsec -= 1_000_000_000;
244 sec = sec.and_then(|sec| sec.checked_add(1));
245 }
246
247 sec.map(|sec| libc::timespec {
248 tv_nsec: nsec,
249 tv_sec: sec,
250 })
251}
252
253#[inline]
254pub fn thread_yield() {
255 thread::yield_now();
256}