summaryrefslogtreecommitdiffstats
path: root/vendor/parking_lot_core-0.8.6/src/thread_parker/windows/keyed_event.rs
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/parking_lot_core-0.8.6/src/thread_parker/windows/keyed_event.rs')
-rw-r--r--vendor/parking_lot_core-0.8.6/src/thread_parker/windows/keyed_event.rs218
1 files changed, 218 insertions, 0 deletions
diff --git a/vendor/parking_lot_core-0.8.6/src/thread_parker/windows/keyed_event.rs b/vendor/parking_lot_core-0.8.6/src/thread_parker/windows/keyed_event.rs
new file mode 100644
index 000000000..7c371537c
--- /dev/null
+++ b/vendor/parking_lot_core-0.8.6/src/thread_parker/windows/keyed_event.rs
@@ -0,0 +1,218 @@
+// 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.
+
+use core::{
+ mem::{self, MaybeUninit},
+ ptr,
+};
+use instant::Instant;
+use std::sync::atomic::{AtomicUsize, Ordering};
+use winapi::{
+ shared::{
+ minwindef::{TRUE, ULONG},
+ ntdef::NTSTATUS,
+ ntstatus::{STATUS_SUCCESS, STATUS_TIMEOUT},
+ },
+ um::{
+ handleapi::CloseHandle,
+ libloaderapi::{GetModuleHandleA, GetProcAddress},
+ winnt::{
+ ACCESS_MASK, BOOLEAN, GENERIC_READ, GENERIC_WRITE, HANDLE, LARGE_INTEGER, LPCSTR,
+ PHANDLE, PLARGE_INTEGER, PVOID,
+ },
+ },
+};
+
+const STATE_UNPARKED: usize = 0;
+const STATE_PARKED: usize = 1;
+const STATE_TIMED_OUT: usize = 2;
+
+#[allow(non_snake_case)]
+pub struct KeyedEvent {
+ handle: HANDLE,
+ NtReleaseKeyedEvent: extern "system" fn(
+ EventHandle: HANDLE,
+ Key: PVOID,
+ Alertable: BOOLEAN,
+ Timeout: PLARGE_INTEGER,
+ ) -> NTSTATUS,
+ NtWaitForKeyedEvent: extern "system" fn(
+ EventHandle: HANDLE,
+ Key: PVOID,
+ Alertable: BOOLEAN,
+ Timeout: PLARGE_INTEGER,
+ ) -> NTSTATUS,
+}
+
+impl KeyedEvent {
+ #[inline]
+ unsafe fn wait_for(&self, key: PVOID, timeout: PLARGE_INTEGER) -> NTSTATUS {
+ (self.NtWaitForKeyedEvent)(self.handle, key, 0, timeout)
+ }
+
+ #[inline]
+ unsafe fn release(&self, key: PVOID) -> NTSTATUS {
+ (self.NtReleaseKeyedEvent)(self.handle, key, 0, ptr::null_mut())
+ }
+
+ #[allow(non_snake_case)]
+ pub fn create() -> Option<KeyedEvent> {
+ unsafe {
+ let ntdll = GetModuleHandleA(b"ntdll.dll\0".as_ptr() as LPCSTR);
+ if ntdll.is_null() {
+ return None;
+ }
+
+ let NtCreateKeyedEvent =
+ GetProcAddress(ntdll, b"NtCreateKeyedEvent\0".as_ptr() as LPCSTR);
+ if NtCreateKeyedEvent.is_null() {
+ return None;
+ }
+ let NtReleaseKeyedEvent =
+ GetProcAddress(ntdll, b"NtReleaseKeyedEvent\0".as_ptr() as LPCSTR);
+ if NtReleaseKeyedEvent.is_null() {
+ return None;
+ }
+ let NtWaitForKeyedEvent =
+ GetProcAddress(ntdll, b"NtWaitForKeyedEvent\0".as_ptr() as LPCSTR);
+ if NtWaitForKeyedEvent.is_null() {
+ return None;
+ }
+
+ let NtCreateKeyedEvent: extern "system" fn(
+ KeyedEventHandle: PHANDLE,
+ DesiredAccess: ACCESS_MASK,
+ ObjectAttributes: PVOID,
+ Flags: ULONG,
+ ) -> NTSTATUS = mem::transmute(NtCreateKeyedEvent);
+ let mut handle = MaybeUninit::uninit();
+ let status = NtCreateKeyedEvent(
+ handle.as_mut_ptr(),
+ GENERIC_READ | GENERIC_WRITE,
+ ptr::null_mut(),
+ 0,
+ );
+ if status != STATUS_SUCCESS {
+ return None;
+ }
+
+ Some(KeyedEvent {
+ handle: handle.assume_init(),
+ NtReleaseKeyedEvent: mem::transmute(NtReleaseKeyedEvent),
+ NtWaitForKeyedEvent: mem::transmute(NtWaitForKeyedEvent),
+ })
+ }
+ }
+
+ #[inline]
+ pub fn prepare_park(&'static self, key: &AtomicUsize) {
+ key.store(STATE_PARKED, Ordering::Relaxed);
+ }
+
+ #[inline]
+ pub fn timed_out(&'static self, key: &AtomicUsize) -> bool {
+ key.load(Ordering::Relaxed) == STATE_TIMED_OUT
+ }
+
+ #[inline]
+ pub unsafe fn park(&'static self, key: &AtomicUsize) {
+ let status = self.wait_for(key as *const _ as PVOID, ptr::null_mut());
+ debug_assert_eq!(status, STATUS_SUCCESS);
+ }
+
+ #[inline]
+ pub unsafe fn park_until(&'static self, key: &AtomicUsize, timeout: Instant) -> bool {
+ let now = Instant::now();
+ if timeout <= now {
+ // If another thread unparked us, we need to call
+ // NtWaitForKeyedEvent otherwise that thread will stay stuck at
+ // NtReleaseKeyedEvent.
+ if key.swap(STATE_TIMED_OUT, Ordering::Relaxed) == STATE_UNPARKED {
+ self.park(key);
+ return true;
+ }
+ return false;
+ }
+
+ // NT uses a timeout in units of 100ns. We use a negative value to
+ // indicate a relative timeout based on a monotonic clock.
+ let mut nt_timeout: LARGE_INTEGER = mem::zeroed();
+ let diff = timeout - now;
+ let value = (diff.as_secs() as i64)
+ .checked_mul(-10000000)
+ .and_then(|x| x.checked_sub((diff.subsec_nanos() as i64 + 99) / 100));
+
+ match value {
+ Some(x) => *nt_timeout.QuadPart_mut() = x,
+ None => {
+ // Timeout overflowed, just sleep indefinitely
+ self.park(key);
+ return true;
+ }
+ };
+
+ let status = self.wait_for(key as *const _ as PVOID, &mut nt_timeout);
+ if status == STATUS_SUCCESS {
+ return true;
+ }
+ debug_assert_eq!(status, STATUS_TIMEOUT);
+
+ // If another thread unparked us, we need to call NtWaitForKeyedEvent
+ // otherwise that thread will stay stuck at NtReleaseKeyedEvent.
+ if key.swap(STATE_TIMED_OUT, Ordering::Relaxed) == STATE_UNPARKED {
+ self.park(key);
+ return true;
+ }
+ false
+ }
+
+ #[inline]
+ pub unsafe fn unpark_lock(&'static self, key: &AtomicUsize) -> UnparkHandle {
+ // If the state was STATE_PARKED then we need to wake up the thread
+ if key.swap(STATE_UNPARKED, Ordering::Relaxed) == STATE_PARKED {
+ UnparkHandle {
+ key: key,
+ keyed_event: self,
+ }
+ } else {
+ UnparkHandle {
+ key: ptr::null(),
+ keyed_event: self,
+ }
+ }
+ }
+}
+
+impl Drop for KeyedEvent {
+ #[inline]
+ fn drop(&mut self) {
+ unsafe {
+ let ok = CloseHandle(self.handle);
+ debug_assert_eq!(ok, TRUE);
+ }
+ }
+}
+
+// Handle for a thread that is about to be unparked. We need to mark the thread
+// as unparked while holding the queue lock, but we delay the actual unparking
+// until after the queue lock is released.
+pub struct UnparkHandle {
+ key: *const AtomicUsize,
+ keyed_event: &'static KeyedEvent,
+}
+
+impl UnparkHandle {
+ // Wakes up the parked thread. This should be called after the queue lock is
+ // released to avoid blocking the queue for too long.
+ #[inline]
+ pub unsafe fn unpark(self) {
+ if !self.key.is_null() {
+ let status = self.keyed_event.release(self.key as PVOID);
+ debug_assert_eq!(status, STATUS_SUCCESS);
+ }
+ }
+}