summaryrefslogtreecommitdiff
path: root/src/futex/linux.rs
blob: 80ca5a66326a07ee7b7594b42b9bb92f5e7ee73b (plain)
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
	}
}