summaryrefslogtreecommitdiffstats
path: root/vendor/parking_lot_core-0.8.6/src/thread_parker/unix.rs
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/parking_lot_core-0.8.6/src/thread_parker/unix.rs')
-rw-r--r--vendor/parking_lot_core-0.8.6/src/thread_parker/unix.rs242
1 files changed, 242 insertions, 0 deletions
diff --git a/vendor/parking_lot_core-0.8.6/src/thread_parker/unix.rs b/vendor/parking_lot_core-0.8.6/src/thread_parker/unix.rs
new file mode 100644
index 000000000..c2381e6df
--- /dev/null
+++ b/vendor/parking_lot_core-0.8.6/src/thread_parker/unix.rs
@@ -0,0 +1,242 @@
+// Copyright 2016 Amanieu d'Antras
+//
+// Licensed under the Apache License, Version 2.0, <LICENSE-APACHE or
+// http://apache.org/licenses/LICENSE-2.0> or the MIT license <LICENSE-MIT or
+// http://opensource.org/licenses/MIT>, at your option. This file may not be
+// copied, modified, or distributed except according to those terms.
+
+#[cfg(any(target_os = "macos", target_os = "ios"))]
+use core::ptr;
+use core::{
+ cell::{Cell, UnsafeCell},
+ mem::MaybeUninit,
+};
+use instant::Instant;
+use libc;
+use std::{thread, time::Duration};
+
+// x32 Linux uses a non-standard type for tv_nsec in timespec.
+// See https://sourceware.org/bugzilla/show_bug.cgi?id=16437
+#[cfg(all(target_arch = "x86_64", target_pointer_width = "32"))]
+#[allow(non_camel_case_types)]
+type tv_nsec_t = i64;
+#[cfg(not(all(target_arch = "x86_64", target_pointer_width = "32")))]
+#[allow(non_camel_case_types)]
+type tv_nsec_t = libc::c_long;
+
+// Helper type for putting a thread to sleep until some other thread wakes it up
+pub struct ThreadParker {
+ should_park: Cell<bool>,
+ mutex: UnsafeCell<libc::pthread_mutex_t>,
+ condvar: UnsafeCell<libc::pthread_cond_t>,
+ initialized: Cell<bool>,
+}
+
+impl super::ThreadParkerT for ThreadParker {
+ type UnparkHandle = UnparkHandle;
+
+ const IS_CHEAP_TO_CONSTRUCT: bool = false;
+
+ #[inline]
+ fn new() -> ThreadParker {
+ ThreadParker {
+ should_park: Cell::new(false),
+ mutex: UnsafeCell::new(libc::PTHREAD_MUTEX_INITIALIZER),
+ condvar: UnsafeCell::new(libc::PTHREAD_COND_INITIALIZER),
+ initialized: Cell::new(false),
+ }
+ }
+
+ #[inline]
+ unsafe fn prepare_park(&self) {
+ self.should_park.set(true);
+ if !self.initialized.get() {
+ self.init();
+ self.initialized.set(true);
+ }
+ }
+
+ #[inline]
+ unsafe fn timed_out(&self) -> bool {
+ // We need to grab the mutex here because another thread may be
+ // concurrently executing UnparkHandle::unpark, which is done without
+ // holding the queue lock.
+ let r = libc::pthread_mutex_lock(self.mutex.get());
+ debug_assert_eq!(r, 0);
+ let should_park = self.should_park.get();
+ let r = libc::pthread_mutex_unlock(self.mutex.get());
+ debug_assert_eq!(r, 0);
+ should_park
+ }
+
+ #[inline]
+ unsafe fn park(&self) {
+ let r = libc::pthread_mutex_lock(self.mutex.get());
+ debug_assert_eq!(r, 0);
+ while self.should_park.get() {
+ let r = libc::pthread_cond_wait(self.condvar.get(), self.mutex.get());
+ debug_assert_eq!(r, 0);
+ }
+ let r = libc::pthread_mutex_unlock(self.mutex.get());
+ debug_assert_eq!(r, 0);
+ }
+
+ #[inline]
+ unsafe fn park_until(&self, timeout: Instant) -> bool {
+ let r = libc::pthread_mutex_lock(self.mutex.get());
+ debug_assert_eq!(r, 0);
+ while self.should_park.get() {
+ let now = Instant::now();
+ if timeout <= now {
+ let r = libc::pthread_mutex_unlock(self.mutex.get());
+ debug_assert_eq!(r, 0);
+ return false;
+ }
+
+ if let Some(ts) = timeout_to_timespec(timeout - now) {
+ let r = libc::pthread_cond_timedwait(self.condvar.get(), self.mutex.get(), &ts);
+ if ts.tv_sec < 0 {
+ // On some systems, negative timeouts will return EINVAL. In
+ // that case we won't sleep and will just busy loop instead,
+ // which is the best we can do.
+ debug_assert!(r == 0 || r == libc::ETIMEDOUT || r == libc::EINVAL);
+ } else {
+ debug_assert!(r == 0 || r == libc::ETIMEDOUT);
+ }
+ } else {
+ // Timeout calculation overflowed, just sleep indefinitely
+ let r = libc::pthread_cond_wait(self.condvar.get(), self.mutex.get());
+ debug_assert_eq!(r, 0);
+ }
+ }
+ let r = libc::pthread_mutex_unlock(self.mutex.get());
+ debug_assert_eq!(r, 0);
+ true
+ }
+
+ #[inline]
+ unsafe fn unpark_lock(&self) -> UnparkHandle {
+ let r = libc::pthread_mutex_lock(self.mutex.get());
+ debug_assert_eq!(r, 0);
+
+ UnparkHandle {
+ thread_parker: self,
+ }
+ }
+}
+
+impl ThreadParker {
+ /// Initializes the condvar to use CLOCK_MONOTONIC instead of CLOCK_REALTIME.
+ #[cfg(any(target_os = "macos", target_os = "ios", target_os = "android"))]
+ #[inline]
+ unsafe fn init(&self) {}
+
+ /// Initializes the condvar to use CLOCK_MONOTONIC instead of CLOCK_REALTIME.
+ #[cfg(not(any(target_os = "macos", target_os = "ios", target_os = "android")))]
+ #[inline]
+ unsafe fn init(&self) {
+ let mut attr = MaybeUninit::<libc::pthread_condattr_t>::uninit();
+ let r = libc::pthread_condattr_init(attr.as_mut_ptr());
+ debug_assert_eq!(r, 0);
+ let r = libc::pthread_condattr_setclock(attr.as_mut_ptr(), libc::CLOCK_MONOTONIC);
+ debug_assert_eq!(r, 0);
+ let r = libc::pthread_cond_init(self.condvar.get(), attr.as_ptr());
+ debug_assert_eq!(r, 0);
+ let r = libc::pthread_condattr_destroy(attr.as_mut_ptr());
+ debug_assert_eq!(r, 0);
+ }
+}
+
+impl Drop for ThreadParker {
+ #[inline]
+ fn drop(&mut self) {
+ // On DragonFly pthread_mutex_destroy() returns EINVAL if called on a
+ // mutex that was just initialized with libc::PTHREAD_MUTEX_INITIALIZER.
+ // Once it is used (locked/unlocked) or pthread_mutex_init() is called,
+ // this behaviour no longer occurs. The same applies to condvars.
+ unsafe {
+ let r = libc::pthread_mutex_destroy(self.mutex.get());
+ debug_assert!(r == 0 || r == libc::EINVAL);
+ let r = libc::pthread_cond_destroy(self.condvar.get());
+ debug_assert!(r == 0 || r == libc::EINVAL);
+ }
+ }
+}
+
+pub struct UnparkHandle {
+ thread_parker: *const ThreadParker,
+}
+
+impl super::UnparkHandleT for UnparkHandle {
+ #[inline]
+ unsafe fn unpark(self) {
+ (*self.thread_parker).should_park.set(false);
+
+ // We notify while holding the lock here to avoid races with the target
+ // thread. In particular, the thread could exit after we unlock the
+ // mutex, which would make the condvar access invalid memory.
+ let r = libc::pthread_cond_signal((*self.thread_parker).condvar.get());
+ debug_assert_eq!(r, 0);
+ let r = libc::pthread_mutex_unlock((*self.thread_parker).mutex.get());
+ debug_assert_eq!(r, 0);
+ }
+}
+
+// Returns the current time on the clock used by pthread_cond_t as a timespec.
+#[cfg(any(target_os = "macos", target_os = "ios"))]
+#[inline]
+fn timespec_now() -> libc::timespec {
+ let mut now = MaybeUninit::<libc::timeval>::uninit();
+ let r = unsafe { libc::gettimeofday(now.as_mut_ptr(), ptr::null_mut()) };
+ debug_assert_eq!(r, 0);
+ // SAFETY: We know `libc::gettimeofday` has initialized the value.
+ let now = unsafe { now.assume_init() };
+ libc::timespec {
+ tv_sec: now.tv_sec,
+ tv_nsec: now.tv_usec as tv_nsec_t * 1000,
+ }
+}
+#[cfg(not(any(target_os = "macos", target_os = "ios")))]
+#[inline]
+fn timespec_now() -> libc::timespec {
+ let mut now = MaybeUninit::<libc::timespec>::uninit();
+ let clock = if cfg!(target_os = "android") {
+ // Android doesn't support pthread_condattr_setclock, so we need to
+ // specify the timeout in CLOCK_REALTIME.
+ libc::CLOCK_REALTIME
+ } else {
+ libc::CLOCK_MONOTONIC
+ };
+ let r = unsafe { libc::clock_gettime(clock, now.as_mut_ptr()) };
+ debug_assert_eq!(r, 0);
+ // SAFETY: We know `libc::clock_gettime` has initialized the value.
+ unsafe { now.assume_init() }
+}
+
+// Converts a relative timeout into an absolute timeout in the clock used by
+// pthread_cond_t.
+#[inline]
+fn timeout_to_timespec(timeout: Duration) -> Option<libc::timespec> {
+ // Handle overflows early on
+ if timeout.as_secs() > libc::time_t::max_value() as u64 {
+ return None;
+ }
+
+ let now = timespec_now();
+ let mut nsec = now.tv_nsec + timeout.subsec_nanos() as tv_nsec_t;
+ let mut sec = now.tv_sec.checked_add(timeout.as_secs() as libc::time_t);
+ if nsec >= 1_000_000_000 {
+ nsec -= 1_000_000_000;
+ sec = sec.and_then(|sec| sec.checked_add(1));
+ }
+
+ sec.map(|sec| libc::timespec {
+ tv_nsec: nsec,
+ tv_sec: sec,
+ })
+}
+
+#[inline]
+pub fn thread_yield() {
+ thread::yield_now();
+}