parking_lot_core\thread_parker\windows/
waitaddress.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
8use core::{
9    mem,
10    sync::atomic::{AtomicUsize, Ordering},
11};
12use std::{ffi, time::Instant};
13
14use super::bindings::*;
15
16#[allow(non_snake_case)]
17pub struct WaitAddress {
18    WaitOnAddress: WaitOnAddress,
19    WakeByAddressSingle: WakeByAddressSingle,
20}
21
22impl WaitAddress {
23    #[allow(non_snake_case)]
24    pub fn create() -> Option<WaitAddress> {
25        let synch_dll = unsafe { GetModuleHandleA(b"api-ms-win-core-synch-l1-2-0.dll\0".as_ptr()) };
26        if synch_dll == 0 {
27            return None;
28        }
29
30        let WaitOnAddress = unsafe { GetProcAddress(synch_dll, b"WaitOnAddress\0".as_ptr())? };
31        let WakeByAddressSingle =
32            unsafe { GetProcAddress(synch_dll, b"WakeByAddressSingle\0".as_ptr())? };
33
34        Some(WaitAddress {
35            WaitOnAddress: unsafe { mem::transmute(WaitOnAddress) },
36            WakeByAddressSingle: unsafe { mem::transmute(WakeByAddressSingle) },
37        })
38    }
39
40    #[inline]
41    pub fn prepare_park(&'static self, key: &AtomicUsize) {
42        key.store(1, Ordering::Relaxed);
43    }
44
45    #[inline]
46    pub fn timed_out(&'static self, key: &AtomicUsize) -> bool {
47        key.load(Ordering::Relaxed) != 0
48    }
49
50    #[inline]
51    pub fn park(&'static self, key: &AtomicUsize) {
52        while key.load(Ordering::Acquire) != 0 {
53            let r = self.wait_on_address(key, INFINITE);
54            debug_assert!(r == true.into());
55        }
56    }
57
58    #[inline]
59    pub fn park_until(&'static self, key: &AtomicUsize, timeout: Instant) -> bool {
60        while key.load(Ordering::Acquire) != 0 {
61            let now = Instant::now();
62            if timeout <= now {
63                return false;
64            }
65            let diff = timeout - now;
66            let timeout = diff
67                .as_secs()
68                .checked_mul(1000)
69                .and_then(|x| x.checked_add((diff.subsec_nanos() as u64 + 999999) / 1000000))
70                .map(|ms| {
71                    if ms > std::u32::MAX as u64 {
72                        INFINITE
73                    } else {
74                        ms as u32
75                    }
76                })
77                .unwrap_or(INFINITE);
78            if self.wait_on_address(key, timeout) == false.into() {
79                debug_assert_eq!(unsafe { GetLastError() }, ERROR_TIMEOUT);
80            }
81        }
82        true
83    }
84
85    #[inline]
86    pub fn unpark_lock(&'static self, key: &AtomicUsize) -> UnparkHandle {
87        // We don't need to lock anything, just clear the state
88        key.store(0, Ordering::Release);
89
90        UnparkHandle {
91            key: key,
92            waitaddress: self,
93        }
94    }
95
96    #[inline]
97    fn wait_on_address(&'static self, key: &AtomicUsize, timeout: u32) -> BOOL {
98        let cmp = 1usize;
99        unsafe {
100            (self.WaitOnAddress)(
101                key as *const _ as *mut ffi::c_void,
102                &cmp as *const _ as *mut ffi::c_void,
103                mem::size_of::<usize>(),
104                timeout,
105            )
106        }
107    }
108}
109
110// Handle for a thread that is about to be unparked. We need to mark the thread
111// as unparked while holding the queue lock, but we delay the actual unparking
112// until after the queue lock is released.
113pub struct UnparkHandle {
114    key: *const AtomicUsize,
115    waitaddress: &'static WaitAddress,
116}
117
118impl UnparkHandle {
119    // Wakes up the parked thread. This should be called after the queue lock is
120    // released to avoid blocking the queue for too long.
121    #[inline]
122    pub fn unpark(self) {
123        unsafe { (self.waitaddress.WakeByAddressSingle)(self.key as *mut ffi::c_void) };
124    }
125}