blob: cfa0a6b3815bd480b7083a33c40fd266804ca7c8 [file] [log] [blame]
use core::ffi::c_void;
use core::ptr;
use core::sync::atomic::{
Atomic, AtomicBool, AtomicI8, AtomicI16, AtomicI32, AtomicI64, AtomicIsize, AtomicPtr,
AtomicU8, AtomicU16, AtomicU32, AtomicU64, AtomicUsize,
};
use core::time::Duration;
use super::api::{self, WinError};
use crate::sys::{c, dur2timeout};
/// An atomic for use as a futex that is at least 32-bits but may be larger
pub type Futex = Atomic<Primitive>;
/// Must be the underlying type of Futex
pub type Primitive = u32;
/// An atomic for use as a futex that is at least 8-bits but may be larger.
pub type SmallFutex = Atomic<SmallPrimitive>;
/// Must be the underlying type of SmallFutex
pub type SmallPrimitive = u8;
pub unsafe trait Futexable {}
pub unsafe trait Waitable {
type Futex;
}
macro_rules! unsafe_waitable_int {
($(($int:ty, $atomic:ty)),*$(,)?) => {
$(
unsafe impl Waitable for $int {
type Futex = $atomic;
}
unsafe impl Futexable for $atomic {}
)*
};
}
unsafe_waitable_int! {
(bool, AtomicBool),
(i8, AtomicI8),
(i16, AtomicI16),
(i32, AtomicI32),
(i64, AtomicI64),
(isize, AtomicIsize),
(u8, AtomicU8),
(u16, AtomicU16),
(u32, AtomicU32),
(u64, AtomicU64),
(usize, AtomicUsize),
}
unsafe impl<T> Waitable for *const T {
type Futex = Atomic<*mut T>;
}
unsafe impl<T> Waitable for *mut T {
type Futex = Atomic<*mut T>;
}
unsafe impl<T> Futexable for AtomicPtr<T> {}
pub fn wait_on_address<W: Waitable>(
address: &W::Futex,
compare: W,
timeout: Option<Duration>,
) -> bool {
unsafe {
let addr = ptr::from_ref(address).cast::<c_void>();
let size = size_of::<W>();
let compare_addr = (&raw const compare).cast::<c_void>();
let timeout = timeout.map(dur2timeout).unwrap_or(c::INFINITE);
c::WaitOnAddress(addr, compare_addr, size, timeout) == c::TRUE
}
}
pub fn wake_by_address_single<T: Futexable>(address: &T) {
unsafe {
let addr = ptr::from_ref(address).cast::<c_void>();
c::WakeByAddressSingle(addr);
}
}
pub fn wake_by_address_all<T: Futexable>(address: &T) {
unsafe {
let addr = ptr::from_ref(address).cast::<c_void>();
c::WakeByAddressAll(addr);
}
}
pub fn futex_wait<W: Waitable>(futex: &W::Futex, expected: W, timeout: Option<Duration>) -> bool {
// return false only on timeout
wait_on_address(futex, expected, timeout) || api::get_last_error() != WinError::TIMEOUT
}
pub fn futex_wake<T: Futexable>(futex: &T) -> bool {
wake_by_address_single(futex);
false
}
pub fn futex_wake_all<T: Futexable>(futex: &T) {
wake_by_address_all(futex)
}