diff --git a/CHANGELOG.md b/CHANGELOG.md index 4f7855d380..e546a4e83a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,9 @@ This project adheres to [Semantic Versioning](https://semver.org/). ## [Unreleased] - ReleaseDate ### Added + +- Added futex interface. + ([#1907](https://github.com/nix-rust/nix/pull/1907)) - Add `PF_ROUTE` to `SockType` on macOS, iOS, all of the BSDs, Fuchsia, Haiku, Illumos. ([#1867](https://github.com/nix-rust/nix/pull/1867)) - Added `nix::ucontext` module on `aarch64-unknown-linux-gnu`. diff --git a/src/sys/futex.rs b/src/sys/futex.rs new file mode 100644 index 0000000000..f1887cf406 --- /dev/null +++ b/src/sys/futex.rs @@ -0,0 +1,304 @@ +use crate::{Errno, Result}; +use libc::{syscall, SYS_futex}; +use std::convert::TryFrom; +use std::os::unix::io::{FromRawFd, OwnedFd}; + +libc_bitflags! { + pub struct FutexOp: libc::c_int { + FUTEX_PRIVATE_FLAG; + FUTEX_CLOCK_REALTIME; + FUTEX_WAIT; + FUTEX_WAKE; + FUTEX_FD; + FUTEX_REQUEUE; + FUTEX_CMP_REQUEUE; + FUTEX_WAKE_OP; + FUTEX_WAIT_BITSET; + FUTEX_WAKE_BITSET; + FUTEX_LOCK_PI; + FUTEX_LOCK_PI2; + FUTEX_TRYLOCK_PI; + FUTEX_UNLOCK_PI; + FUTEX_CMP_REQUEUE_PI; + FUTEX_WAIT_REQUEUE_PI; + } +} + +/// Handle a return result which: +/// +/// - Returns `Err(errno())` if `-1` +/// - Returns `Ok(())` if `0` +/// - Cannot be any other value. +#[inline] +fn result(res: i64) -> Result<()> { + match res { + -1 => Err(Errno::last()), + 0 => Ok(()), + _ => unreachable!(), + } +} + +/// Fast user-space locking. +#[derive(Debug, Clone, Copy)] +pub struct Futex(pub u32); +impl Futex { + /// Creates new futex. + pub fn new(val: u32) -> Self { + Self(val) + } + /// `FutexOp::FUTEX_WAIT` + pub fn wait( + &self, + val: u32, + timeout: Option, + ) -> Result<()> { + let timeout = match timeout { + Some(t) => &t, + None => std::ptr::null(), + }; + let res = unsafe { + syscall( + SYS_futex, + &self.0, + FutexOp::FUTEX_WAIT.bits(), + val, + timeout, + ) + }; + result(res) + } + /// `FutexOp::FUTEX_WAIT | FutexOp::FUTEX_CLOCK_REALTIME` + pub fn wait_realtime( + &self, + val: u32, + timeout: Option, + ) -> Result<()> { + let timeout = match timeout { + Some(t) => &t, + None => std::ptr::null(), + }; + let res = unsafe { + syscall( + SYS_futex, + &self.0, + (FutexOp::FUTEX_WAIT | FutexOp::FUTEX_CLOCK_REALTIME).bits(), + val, + timeout, + ) + }; + result(res) + } + /// `FutexOp::FUTEX_WAKE` + pub fn wake(&self, val: u32) -> Result { + let res = unsafe { + syscall(SYS_futex, &self.0, FutexOp::FUTEX_WAKE.bits(), val) + }; + Errno::result(res).map(|x| u32::try_from(x).unwrap()) + } + /// `FutexOp::FUTEX_FD` + pub fn fd(&self, val: u32) -> Result { + let res = unsafe { + syscall(SYS_futex, &self.0, FutexOp::FUTEX_WAKE.bits(), val) + }; + + Errno::result(res) + .map(|x| unsafe { OwnedFd::from_raw_fd(i32::try_from(x).unwrap()) }) + } + /// `FutexOp::FUTEX_REQUEUE` + pub fn requeue(&self, val: u32, val2: u32, uaddr2: &Self) -> Result { + let res = unsafe { + syscall( + SYS_futex, + &self.0, + FutexOp::FUTEX_CMP_REQUEUE.bits(), + val, + val2, + &uaddr2.0, + ) + }; + Errno::result(res).map(|x| u32::try_from(x).unwrap()) + } + /// `FutexOp::FUTEX_CMP_REQUEUE` + pub fn cmp_requeue( + &self, + val: u32, + val2: u32, + uaddr2: &Self, + val3: u32, + ) -> Result { + let res = unsafe { + syscall( + SYS_futex, + &self.0, + FutexOp::FUTEX_CMP_REQUEUE.bits(), + val, + val2, + &uaddr2.0, + val3, + ) + }; + Errno::result(res).map(|x| u32::try_from(x).unwrap()) + } + /// `FutexOp::FUTEX_WAKE_OP` + pub fn wake_op( + &self, + val: u32, + val2: u32, + uaddr2: &Self, + val3: u32, + ) -> Result { + let res = unsafe { + syscall( + SYS_futex, + &self.0, + FutexOp::FUTEX_WAKE_OP.bits(), + val, + val2, + &uaddr2.0, + val3, + ) + }; + Errno::result(res).map(|x| u32::try_from(x).unwrap()) + } + /// `FutexOp::FUTEX_WAIT_BITSET` + pub fn wait_bitset( + &self, + val: u32, + timeout: Option, + val3: u32, + ) -> Result<()> { + let timeout = match timeout { + Some(t) => &t, + None => std::ptr::null(), + }; + let res = unsafe { + syscall( + SYS_futex, + &self.0, + FutexOp::FUTEX_WAIT_BITSET.bits(), + val, + timeout, + val3, + ) + }; + result(res) + } + /// `FutexOp::FUTEX_WAKE_BITSET` + pub fn wake_bitset(&self, val: u32, val3: u32) -> Result { + let res = unsafe { + syscall( + SYS_futex, + &self.0, + FutexOp::FUTEX_WAKE_BITSET.bits(), + val, + val3, + ) + }; + Errno::result(res).map(|x| u32::try_from(x).unwrap()) + } + /// `FutexOp::FUTEX_LOCK_PI` + pub fn lock_pi(&self, timeout: Option) -> Result<()> { + let timeout = match timeout { + Some(t) => &t, + None => std::ptr::null(), + }; + let res = unsafe { + syscall(SYS_futex, &self.0, FutexOp::FUTEX_LOCK_PI.bits(), timeout) + }; + result(res) + } + /// `FutexOp::FUTEX_LOCK_PI2` + pub fn lock_pi2(&self, timeout: Option) -> Result<()> { + let timeout = match timeout { + Some(t) => &t, + None => std::ptr::null(), + }; + let res = unsafe { + syscall(SYS_futex, &self.0, FutexOp::FUTEX_LOCK_PI2.bits(), timeout) + }; + result(res) + } + /// `FutexOp::FUTEX_LOCK_PI2 | FutexOp::FUTEX_CLOCK_REALTIME` + pub fn lock_pi2_realtime( + &self, + timeout: Option, + ) -> Result<()> { + let timeout = match timeout { + Some(t) => &t, + None => std::ptr::null(), + }; + let res = unsafe { + syscall( + SYS_futex, + &self.0, + (FutexOp::FUTEX_LOCK_PI2 | FutexOp::FUTEX_CLOCK_REALTIME) + .bits(), + timeout, + ) + }; + result(res) + } + /// `FutexOp::FUTEX_TRYLOCK_PI` + pub fn trylock_pi(&self) -> Result<()> { + let res = unsafe { + syscall(SYS_futex, &self.0, FutexOp::FUTEX_TRYLOCK_PI.bits()) + }; + result(res) + } + /// `FutexOp::FUTEX_UNLOCK_PI` + pub fn unlock_pi(&self) -> Result<()> { + let res = unsafe { + syscall(SYS_futex, &self.0, FutexOp::FUTEX_UNLOCK_PI.bits()) + }; + result(res) + } + /// `FutexOp::FUTEX_CMP_REQUEUE_PI` + pub fn cmp_requeue_pi( + &self, + val: u32, + val2: u32, + uaddr2: &Self, + val3: u32, + ) -> Result { + let res = unsafe { + syscall( + SYS_futex, + &self.0, + FutexOp::FUTEX_CMP_REQUEUE_PI.bits(), + val, + val2, + &uaddr2.0, + val3, + ) + }; + Errno::result(res).map(|x| u32::try_from(x).unwrap()) + } + /// `FutexOp::FUTEX_WAIT_REQUEUE_PI` + pub fn wait_requeue_pi( + &self, + val: u32, + timeout: Option, + uaddr2: &Self, + ) -> Result<()> { + let timeout = match timeout { + Some(t) => &t, + None => std::ptr::null(), + }; + let res = unsafe { + syscall( + SYS_futex, + &self.0, + FutexOp::FUTEX_WAIT_REQUEUE_PI.bits(), + val, + timeout, + &uaddr2.0, + ) + }; + result(res) + } +} +impl Default for Futex { + fn default() -> Self { + Self(0) + } +} diff --git a/src/sys/mod.rs b/src/sys/mod.rs index 2065059de8..25127731da 100644 --- a/src/sys/mod.rs +++ b/src/sys/mod.rs @@ -226,3 +226,6 @@ feature! { #![feature = "time"] pub mod timer; } + +/// Fast user-space locking. +pub mod futex;