shared_rwlock\sys\windows/
futex.rs

1// SPDX-License-Identifier: Apache-2.0 OR MIT
2//
3// # Forked rust (ver. 1.84.0)
4// See: https://github.com/rust-lang/rust/blob/1.84.0/library/std/src/sys/pal/windows/futex.rs
5// See Rust license detail: https://github.com/rust-lang/rust/pull/43498
6
7mod c {
8    use windows::Win32::Foundation::GetLastError;
9    pub use windows::Win32::System::Threading::{
10        WaitOnAddress, WakeByAddressAll, WakeByAddressSingle, INFINITE,
11    };
12
13    use std::time::Duration;
14
15    pub fn dur2timeout(dur: Duration) -> u32 {
16        // Note that a duration is a (u64, u32) (seconds, nanoseconds) pair, and the
17        // timeouts in windows APIs are typically u32 milliseconds. To translate, we
18        // have two pieces to take care of:
19        //
20        // * Nanosecond precision is rounded up
21        // * Greater than u32::MAX milliseconds (50 days) is rounded up to INFINITE
22        //   (never time out).
23        dur.as_secs()
24            .checked_mul(1000)
25            .and_then(|ms| ms.checked_add((dur.subsec_nanos() as u64) / 1_000_000))
26            .and_then(|ms| {
27                ms.checked_add(if dur.subsec_nanos() % 1_000_000 > 0 {
28                    1
29                } else {
30                    0
31                })
32            })
33            .map_or(INFINITE, |ms| {
34                if ms > <u32>::MAX as u64 {
35                    INFINITE
36                } else {
37                    ms as u32
38                }
39            })
40    }
41
42    /// Gets the error from the last function.
43    /// This must be called immediately after the function that sets the error to
44    /// avoid the risk of another function overwriting it.
45    pub fn get_last_error() -> u32 {
46        // SAFETY: This just returns a thread-local u32 and has no other effects.
47        unsafe { GetLastError().0 }
48    }
49
50    pub const TIMEOUT: u32 = 1460;
51}
52
53use core::ffi::c_void;
54use core::sync::atomic::{
55    AtomicBool, AtomicI16, AtomicI32, AtomicI64, AtomicI8, AtomicIsize, AtomicPtr, AtomicU16,
56    AtomicU32, AtomicU64, AtomicU8, AtomicUsize,
57};
58use core::time::Duration;
59use core::{mem, ptr};
60
61use c::dur2timeout;
62
63/// An atomic for use as a futex that is at least 32-bits but may be larger
64pub type Futex = AtomicU32;
65/// Must be the underlying type of Futex
66pub type Primitive = u32;
67
68/// # Safety
69/// inner trait
70pub unsafe trait Futexable {}
71/// # Safety
72/// inner trait
73pub unsafe trait Waitable {
74    type Futex;
75}
76macro_rules! unsafe_waitable_int {
77    ($(($int:ty, $atomic:ty)),*$(,)?) => {
78        $(
79            unsafe impl Waitable for $int {
80                type Futex = $atomic;
81            }
82            unsafe impl Futexable for $atomic {}
83        )*
84    };
85}
86unsafe_waitable_int! {
87    (bool, AtomicBool),
88    (i8, AtomicI8),
89    (i16, AtomicI16),
90    (i32, AtomicI32),
91    (i64, AtomicI64),
92    (isize, AtomicIsize),
93    (u8, AtomicU8),
94    (u16, AtomicU16),
95    (u32, AtomicU32),
96    (u64, AtomicU64),
97    (usize, AtomicUsize),
98}
99unsafe impl<T> Waitable for *const T {
100    type Futex = AtomicPtr<T>;
101}
102unsafe impl<T> Waitable for *mut T {
103    type Futex = AtomicPtr<T>;
104}
105unsafe impl<T> Futexable for AtomicPtr<T> {}
106
107pub fn wait_on_address<W: Waitable>(
108    address: &W::Futex,
109    compare: W,
110    timeout: Option<Duration>,
111) -> bool {
112    unsafe {
113        let addr = ptr::from_ref(address).cast::<c_void>();
114        let size = mem::size_of::<W>();
115        let compare_addr = (&raw const compare).cast::<c_void>();
116        let timeout = timeout.map_or(c::INFINITE, dur2timeout);
117        c::WaitOnAddress(addr, compare_addr, size, Some(timeout)).is_ok()
118    }
119}
120
121pub fn wake_by_address_single<T: Futexable>(address: &T) {
122    unsafe {
123        let addr = ptr::from_ref(address).cast::<c_void>();
124        c::WakeByAddressSingle(addr);
125    }
126}
127
128pub fn wake_by_address_all<T: Futexable>(address: &T) {
129    unsafe {
130        let addr = ptr::from_ref(address).cast::<c_void>();
131        c::WakeByAddressAll(addr);
132    }
133}
134
135pub fn futex_wait<W: Waitable>(futex: &W::Futex, expected: W, timeout: Option<Duration>) -> bool {
136    // return false only on timeout
137    wait_on_address(futex, expected, timeout) || c::get_last_error() != c::TIMEOUT
138}
139
140pub fn futex_wake<T: Futexable>(futex: &T) -> bool {
141    wake_by_address_single(futex);
142    false
143}
144
145pub fn futex_wake_all<T: Futexable>(futex: &T) {
146    wake_by_address_all(futex);
147}