1 // Copyright 2016 Amanieu d'Antras
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.
10 sync
::atomic
::{AtomicUsize, Ordering}
,
12 use std
::time
::Instant
;
16 minwindef
::{BOOL, DWORD, FALSE, TRUE}
,
17 winerror
::ERROR_TIMEOUT
,
20 errhandlingapi
::GetLastError
,
21 libloaderapi
::{GetModuleHandleA, GetProcAddress}
,
23 winnt
::{LPCSTR, PVOID}
,
27 #[allow(non_snake_case)]
28 pub struct WaitAddress
{
29 WaitOnAddress
: extern "system" fn(
31 CompareAddress
: PVOID
,
33 dwMilliseconds
: DWORD
,
35 WakeByAddressSingle
: extern "system" fn(Address
: PVOID
),
39 #[allow(non_snake_case)]
40 pub fn create() -> Option
<WaitAddress
> {
42 // MSDN claims that that WaitOnAddress and WakeByAddressSingle are
43 // located in kernel32.dll, but they are lying...
45 GetModuleHandleA(b
"api-ms-win-core-synch-l1-2-0.dll\0".as_ptr() as LPCSTR
);
46 if synch_dll
.is_null() {
50 let WaitOnAddress
= GetProcAddress(synch_dll
, b
"WaitOnAddress\0".as_ptr() as LPCSTR
);
51 if WaitOnAddress
.is_null() {
54 let WakeByAddressSingle
=
55 GetProcAddress(synch_dll
, b
"WakeByAddressSingle\0".as_ptr() as LPCSTR
);
56 if WakeByAddressSingle
.is_null() {
60 WaitOnAddress
: mem
::transmute(WaitOnAddress
),
61 WakeByAddressSingle
: mem
::transmute(WakeByAddressSingle
),
67 pub fn prepare_park(&'
static self, key
: &AtomicUsize
) {
68 key
.store(1, Ordering
::Relaxed
);
72 pub fn timed_out(&'
static self, key
: &AtomicUsize
) -> bool
{
73 key
.load(Ordering
::Relaxed
) != 0
77 pub fn park(&'
static self, key
: &AtomicUsize
) {
78 while key
.load(Ordering
::Acquire
) != 0 {
79 let r
= self.wait_on_address(key
, INFINITE
);
80 debug_assert
!(r
== TRUE
);
85 pub fn park_until(&'
static self, key
: &AtomicUsize
, timeout
: Instant
) -> bool
{
86 while key
.load(Ordering
::Acquire
) != 0 {
87 let now
= Instant
::now();
91 let diff
= timeout
- now
;
95 .and_then(|x
| x
.checked_add((diff
.subsec_nanos() as u64 + 999999) / 1000000))
97 if ms
> <DWORD
>::max_value() as u64 {
103 .unwrap_or(INFINITE
);
104 if self.wait_on_address(key
, timeout
) == FALSE
{
105 debug_assert_eq
!(unsafe { GetLastError() }
, ERROR_TIMEOUT
);
112 pub fn unpark_lock(&'
static self, key
: &AtomicUsize
) -> UnparkHandle
{
113 // We don't need to lock anything, just clear the state
114 key
.store(0, Ordering
::Release
);
123 fn wait_on_address(&'
static self, key
: &AtomicUsize
, timeout
: DWORD
) -> BOOL
{
125 (self.WaitOnAddress
)(
126 key
as *const _
as PVOID
,
127 &cmp
as *const _
as PVOID
,
128 mem
::size_of
::<usize>() as SIZE_T
,
134 // Handle for a thread that is about to be unparked. We need to mark the thread
135 // as unparked while holding the queue lock, but we delay the actual unparking
136 // until after the queue lock is released.
137 pub struct UnparkHandle
{
138 key
: *const AtomicUsize
,
139 waitaddress
: &'
static WaitAddress
,
143 // Wakes up the parked thread. This should be called after the queue lock is
144 // released to avoid blocking the queue for too long.
146 pub fn unpark(self) {
147 (self.waitaddress
.WakeByAddressSingle
)(self.key
as PVOID
);