Skip to content

Commit

Permalink
Futex
Browse files Browse the repository at this point in the history
  • Loading branch information
JonathanWoollett-Light committed Dec 4, 2022
1 parent e756c96 commit 53a2673
Show file tree
Hide file tree
Showing 3 changed files with 310 additions and 0 deletions.
3 changes: 3 additions & 0 deletions CHANGELOG.md
Expand Up @@ -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`.
Expand Down
304 changes: 304 additions & 0 deletions 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<libc::timespec>,
) -> 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<libc::timespec>,
) -> 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<u32> {
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<OwnedFd> {
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<u32> {
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<u32> {
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<u32> {
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<libc::timespec>,
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<u32> {
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<libc::timespec>) -> 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<libc::timespec>) -> 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<libc::timespec>,
) -> 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<u32> {
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<libc::timespec>,
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)
}
}
3 changes: 3 additions & 0 deletions src/sys/mod.rs
Expand Up @@ -226,3 +226,6 @@ feature! {
#![feature = "time"]
pub mod timer;
}

/// Fast user-space locking.
pub mod futex;

0 comments on commit 53a2673

Please sign in to comment.