summaryrefslogtreecommitdiff
path: root/src/futex
diff options
context:
space:
mode:
Diffstat (limited to 'src/futex')
-rwxr-xr-xsrc/futex/linux.rs61
-rwxr-xr-xsrc/futex/windows.rs114
2 files changed, 175 insertions, 0 deletions
diff --git a/src/futex/linux.rs b/src/futex/linux.rs
new file mode 100755
index 0000000..80ca5a6
--- /dev/null
+++ b/src/futex/linux.rs
@@ -0,0 +1,61 @@
+use core::ops::Deref;
+use core::sync::atomic::{AtomicU32, Ordering};
+
+use libc::{syscall, SYS_futex};
+
+#[repr(C)]
+pub struct Futex(AtomicU32);
+
+pub type Atomic = AtomicU32;
+pub type Primitive = u32;
+
+pub type SmallFutex = Futex;
+pub type SmallAtomic = Atomic;
+pub type SmallPrimitive = Primitive;
+
+impl Futex {
+ #[inline]
+ pub const fn new(initial_value: u32) -> Self {
+ Self(AtomicU32::new(initial_value))
+ }
+
+ #[inline]
+ pub fn wait(&self, expected_start_value: u32) {
+ unsafe {
+ syscall(
+ SYS_futex,
+ core::ptr::from_ref(&self.0),
+ libc::FUTEX_WAIT_BITSET | libc::FUTEX_PRIVATE_FLAG,
+ expected_start_value,
+ core::ptr::null::<()>(),
+ core::ptr::null::<u32>(), // This argument is unused for FUTEX_WAIT_BITSET.
+ !0u32, // A full bitmask, to make it behave like a regular FUTEX_WAIT.
+ );
+ }
+ }
+
+ #[inline]
+ pub fn wake(&self) -> bool {
+ let ptr = &self.0 as *const AtomicU32;
+ const OP: libc::c_int = libc::FUTEX_WAKE | libc::FUTEX_PRIVATE_FLAG;
+ unsafe { libc::syscall(libc::SYS_futex, ptr, OP, 1) > 0 }
+ }
+
+ #[inline]
+ pub fn wake_all(&self) {
+ let ptr = &raw const self.0;
+ let op = libc::FUTEX_WAKE | libc::FUTEX_PRIVATE_FLAG;
+ unsafe {
+ syscall(libc::SYS_futex, ptr, op, i32::MAX);
+ }
+ }
+}
+
+impl Deref for Futex {
+ type Target = Atomic;
+
+ #[inline]
+ fn deref(&self) -> &Self::Target {
+ &self.0
+ }
+}
diff --git a/src/futex/windows.rs b/src/futex/windows.rs
new file mode 100755
index 0000000..153d9ef
--- /dev/null
+++ b/src/futex/windows.rs
@@ -0,0 +1,114 @@
+use core::ffi::c_void;
+
+#[cfg(not(target_vendor = "win7"))]
+// Use raw-dylib to import synchronization functions to workaround issues with the older mingw import library.
+#[cfg_attr(
+ target_arch = "x86",
+ link(
+ name = "api-ms-win-core-synch-l1-2-0",
+ kind = "raw-dylib",
+ import_name_type = "undecorated"
+ )
+)]
+#[cfg_attr(
+ not(target_arch = "x86"),
+ link(name = "api-ms-win-core-synch-l1-2-0", kind = "raw-dylib")
+)]
+extern "system" {
+ type BOOL = i32;
+
+ pub const TRUE: BOOL = 1;
+ pub const INFINITE: u32 = 4294967295;
+
+ pub fn WaitOnAddress(
+ address: *const c_void,
+ compareaddress: *const c_void,
+ addresssize: usize,
+ dwmilliseconds: u32,
+ ) -> BOOL;
+ pub fn WakeByAddressSingle(address: *const c_void);
+ pub fn WakeByAddressAll(address: *const c_void);
+}
+
+pub struct Futex(AtomicU32);
+pub type Primitive = u32;
+
+impl Futex {
+ #[inline]
+ pub const fn new(initial_value: Primitive) -> Self {
+ Self(AtomicU32::new(initial_value))
+ }
+
+ #[inline]
+ pub fn wait(&self, expected_start_value: Primitive) {
+ const SIZE: u32 = core::mem::size_of::<Atomic>();
+ let addr = core::ptr::from_ref(&self.0).cast::<c_void>();
+ let compare_addr = ptr::addr_of!(compare).cast::<c_void>();
+ WaitOnAddress(addr, expected_start_value, SIZE, INFINITE);
+ }
+
+ #[inline]
+ pub fn wake(&self) -> bool {
+ WakeByAddressSingle(core::ptr::from_ref(&self.0).cast::<c_void>());
+ false
+ }
+
+ #[inline]
+ pub fn wake_all(&self) {
+ unsafe {
+ let addr = core::ptr::from_ref(address).cast::<c_void>();
+ WakeByAddressAll(addr);
+ }
+ }
+}
+
+impl Deref for Futex {
+ type Target = Atomic;
+
+ #[inline]
+ fn deref(&self) -> &Self::Target {
+ &self.0
+ }
+}
+
+pub struct SmallFutex(AtomicU8);
+pub type SmallAtomic = AtomicU8;
+pub type SmallPrimitive = u8;
+
+impl SmallFutex {
+ #[inline]
+ pub const fn new(initial_value: SmallPrimitive) -> Self {
+ Self(AtomicU32::new(initial_value))
+ }
+
+ #[inline]
+ pub fn wait(&self, expected_start_value: SmallPrimitive) {
+ const SIZE: u32 = core::mem::size_of::<SmallAtomic>();
+ let addr = core::ptr::from_ref(&self.0).cast::<c_void>();
+ let compare_addr = ptr::addr_of!(compare).cast::<c_void>();
+ WaitOnAddress(addr, expected_start_value, SIZE, INFINITE);
+ }
+
+ #[inline]
+ pub fn wake(&self) -> bool {
+ WakeByAddressSingle(core::ptr::from_ref(&self.0).cast::<c_void>());
+ false
+ }
+
+ #[inline]
+ pub fn wake_all(&self) {
+ unsafe {
+ let addr = core::ptr::from_ref(address).cast::<c_void>();
+ WakeByAddressAll(addr);
+ }
+ }
+}
+
+impl Deref for SmallFutex {
+ type Target = SmallAtomic;
+
+ #[inline]
+ fn deref(&self) -> &Self::Target {
+ &self.0
+ }
+}