Skip to content

Commit 46236db

Browse files
Futex
1 parent 1fd5fe7 commit 46236db

File tree

3 files changed

+310
-0
lines changed

3 files changed

+310
-0
lines changed

CHANGELOG.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,9 @@ This project adheres to [Semantic Versioning](https://semver.org/).
55

66
## [Unreleased] - ReleaseDate
77
### Added
8+
9+
- Added futex interface.
10+
([#1907](https://github.com/nix-rust/nix/pull/1907))
811
- Add `PF_ROUTE` to `SockType` on macOS, iOS, all of the BSDs, Fuchsia, Haiku, Illumos.
912
([#1867](https://github.com/nix-rust/nix/pull/1867))
1013
- Added `nix::ucontext` module on `aarch64-unknown-linux-gnu`.

src/sys/futex.rs

Lines changed: 304 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,304 @@
1+
use crate::{Errno, Result};
2+
use libc::{syscall, SYS_futex};
3+
use std::convert::TryFrom;
4+
use std::os::unix::io::{FromRawFd, OwnedFd};
5+
6+
libc_bitflags! {
7+
pub struct FutexOp: libc::c_int {
8+
FUTEX_PRIVATE_FLAG;
9+
FUTEX_CLOCK_REALTIME;
10+
FUTEX_WAIT;
11+
FUTEX_WAKE;
12+
FUTEX_FD;
13+
FUTEX_REQUEUE;
14+
FUTEX_CMP_REQUEUE;
15+
FUTEX_WAKE_OP;
16+
FUTEX_WAIT_BITSET;
17+
FUTEX_WAKE_BITSET;
18+
FUTEX_LOCK_PI;
19+
FUTEX_LOCK_PI2;
20+
FUTEX_TRYLOCK_PI;
21+
FUTEX_UNLOCK_PI;
22+
FUTEX_CMP_REQUEUE_PI;
23+
FUTEX_WAIT_REQUEUE_PI;
24+
}
25+
}
26+
27+
/// Handle a return result which:
28+
///
29+
/// - Returns `Err(errno())` if `-1`
30+
/// - Returns `Ok(())` if `0`
31+
/// - Cannot be any other value.
32+
#[inline]
33+
fn result(res: i64) -> Result<()> {
34+
match res {
35+
-1 => Err(Errno::last()),
36+
0 => Ok(()),
37+
_ => unreachable!(),
38+
}
39+
}
40+
41+
/// Fast user-space locking.
42+
#[derive(Debug, Clone, Copy)]
43+
pub struct Futex(pub u32);
44+
impl Futex {
45+
/// Creates new futex.
46+
pub fn new(val: u32) -> Self {
47+
Self(val)
48+
}
49+
/// `FutexOp::FUTEX_WAIT`
50+
pub fn wait(
51+
&self,
52+
val: u32,
53+
timeout: Option<libc::timespec>,
54+
) -> Result<()> {
55+
let timeout = match timeout {
56+
Some(t) => &t,
57+
None => std::ptr::null(),
58+
};
59+
let res = unsafe {
60+
syscall(
61+
SYS_futex,
62+
&self.0,
63+
FutexOp::FUTEX_WAIT.bits(),
64+
val,
65+
timeout,
66+
)
67+
};
68+
result(res)
69+
}
70+
/// `FutexOp::FUTEX_WAIT | FutexOp::FUTEX_CLOCK_REALTIME`
71+
pub fn wait_realtime(
72+
&self,
73+
val: u32,
74+
timeout: Option<libc::timespec>,
75+
) -> Result<()> {
76+
let timeout = match timeout {
77+
Some(t) => &t,
78+
None => std::ptr::null(),
79+
};
80+
let res = unsafe {
81+
syscall(
82+
SYS_futex,
83+
&self.0,
84+
(FutexOp::FUTEX_WAIT | FutexOp::FUTEX_CLOCK_REALTIME).bits(),
85+
val,
86+
timeout,
87+
)
88+
};
89+
result(res)
90+
}
91+
/// `FutexOp::FUTEX_WAKE`
92+
pub fn wake(&self, val: u32) -> Result<u32> {
93+
let res = unsafe {
94+
syscall(SYS_futex, &self.0, FutexOp::FUTEX_WAKE.bits(), val)
95+
};
96+
Errno::result(res).map(|x| u32::try_from(x).unwrap())
97+
}
98+
/// `FutexOp::FUTEX_FD`
99+
pub fn fd(&self, val: u32) -> Result<OwnedFd> {
100+
let res = unsafe {
101+
syscall(SYS_futex, &self.0, FutexOp::FUTEX_WAKE.bits(), val)
102+
};
103+
104+
Errno::result(res)
105+
.map(|x| unsafe { OwnedFd::from_raw_fd(i32::try_from(x).unwrap()) })
106+
}
107+
/// `FutexOp::FUTEX_REQUEUE`
108+
pub fn requeue(&self, val: u32, val2: u32, uaddr2: &Self) -> Result<u32> {
109+
let res = unsafe {
110+
syscall(
111+
SYS_futex,
112+
&self.0,
113+
FutexOp::FUTEX_CMP_REQUEUE.bits(),
114+
val,
115+
val2,
116+
&uaddr2.0,
117+
)
118+
};
119+
Errno::result(res).map(|x| u32::try_from(x).unwrap())
120+
}
121+
/// `FutexOp::FUTEX_CMP_REQUEUE`
122+
pub fn cmp_requeue(
123+
&self,
124+
val: u32,
125+
val2: u32,
126+
uaddr2: &Self,
127+
val3: u32,
128+
) -> Result<u32> {
129+
let res = unsafe {
130+
syscall(
131+
SYS_futex,
132+
&self.0,
133+
FutexOp::FUTEX_CMP_REQUEUE.bits(),
134+
val,
135+
val2,
136+
&uaddr2.0,
137+
val3,
138+
)
139+
};
140+
Errno::result(res).map(|x| u32::try_from(x).unwrap())
141+
}
142+
/// `FutexOp::FUTEX_WAKE_OP`
143+
pub fn wake_op(
144+
&self,
145+
val: u32,
146+
val2: u32,
147+
uaddr2: &Self,
148+
val3: u32,
149+
) -> Result<u32> {
150+
let res = unsafe {
151+
syscall(
152+
SYS_futex,
153+
&self.0,
154+
FutexOp::FUTEX_WAKE_OP.bits(),
155+
val,
156+
val2,
157+
&uaddr2.0,
158+
val3,
159+
)
160+
};
161+
Errno::result(res).map(|x| u32::try_from(x).unwrap())
162+
}
163+
/// `FutexOp::FUTEX_WAIT_BITSET`
164+
pub fn wait_bitset(
165+
&self,
166+
val: u32,
167+
timeout: Option<libc::timespec>,
168+
val3: u32,
169+
) -> Result<()> {
170+
let timeout = match timeout {
171+
Some(t) => &t,
172+
None => std::ptr::null(),
173+
};
174+
let res = unsafe {
175+
syscall(
176+
SYS_futex,
177+
&self.0,
178+
FutexOp::FUTEX_WAIT_BITSET.bits(),
179+
val,
180+
timeout,
181+
val3,
182+
)
183+
};
184+
result(res)
185+
}
186+
/// `FutexOp::FUTEX_WAKE_BITSET`
187+
pub fn wake_bitset(&self, val: u32, val3: u32) -> Result<u32> {
188+
let res = unsafe {
189+
syscall(
190+
SYS_futex,
191+
&self.0,
192+
FutexOp::FUTEX_WAKE_BITSET.bits(),
193+
val,
194+
val3,
195+
)
196+
};
197+
Errno::result(res).map(|x| u32::try_from(x).unwrap())
198+
}
199+
/// `FutexOp::FUTEX_LOCK_PI`
200+
pub fn lock_pi(&self, timeout: Option<libc::timespec>) -> Result<()> {
201+
let timeout = match timeout {
202+
Some(t) => &t,
203+
None => std::ptr::null(),
204+
};
205+
let res = unsafe {
206+
syscall(SYS_futex, &self.0, FutexOp::FUTEX_LOCK_PI.bits(), timeout)
207+
};
208+
result(res)
209+
}
210+
/// `FutexOp::FUTEX_LOCK_PI2`
211+
pub fn lock_pi2(&self, timeout: Option<libc::timespec>) -> Result<()> {
212+
let timeout = match timeout {
213+
Some(t) => &t,
214+
None => std::ptr::null(),
215+
};
216+
let res = unsafe {
217+
syscall(SYS_futex, &self.0, FutexOp::FUTEX_LOCK_PI2.bits(), timeout)
218+
};
219+
result(res)
220+
}
221+
/// `FutexOp::FUTEX_LOCK_PI2 | FutexOp::FUTEX_CLOCK_REALTIME`
222+
pub fn lock_pi2_realtime(
223+
&self,
224+
timeout: Option<libc::timespec>,
225+
) -> Result<()> {
226+
let timeout = match timeout {
227+
Some(t) => &t,
228+
None => std::ptr::null(),
229+
};
230+
let res = unsafe {
231+
syscall(
232+
SYS_futex,
233+
&self.0,
234+
(FutexOp::FUTEX_LOCK_PI2 | FutexOp::FUTEX_CLOCK_REALTIME)
235+
.bits(),
236+
timeout,
237+
)
238+
};
239+
result(res)
240+
}
241+
/// `FutexOp::FUTEX_TRYLOCK_PI`
242+
pub fn trylock_pi(&self) -> Result<()> {
243+
let res = unsafe {
244+
syscall(SYS_futex, &self.0, FutexOp::FUTEX_TRYLOCK_PI.bits())
245+
};
246+
result(res)
247+
}
248+
/// `FutexOp::FUTEX_UNLOCK_PI`
249+
pub fn unlock_pi(&self) -> Result<()> {
250+
let res = unsafe {
251+
syscall(SYS_futex, &self.0, FutexOp::FUTEX_UNLOCK_PI.bits())
252+
};
253+
result(res)
254+
}
255+
/// `FutexOp::FUTEX_CMP_REQUEUE_PI`
256+
pub fn cmp_requeue_pi(
257+
&self,
258+
val: u32,
259+
val2: u32,
260+
uaddr2: &Self,
261+
val3: u32,
262+
) -> Result<u32> {
263+
let res = unsafe {
264+
syscall(
265+
SYS_futex,
266+
&self.0,
267+
FutexOp::FUTEX_CMP_REQUEUE_PI.bits(),
268+
val,
269+
val2,
270+
&uaddr2.0,
271+
val3,
272+
)
273+
};
274+
Errno::result(res).map(|x| u32::try_from(x).unwrap())
275+
}
276+
/// `FutexOp::FUTEX_WAIT_REQUEUE_PI`
277+
pub fn wait_requeue_pi(
278+
&self,
279+
val: u32,
280+
timeout: Option<libc::timespec>,
281+
uaddr2: &Self,
282+
) -> Result<()> {
283+
let timeout = match timeout {
284+
Some(t) => &t,
285+
None => std::ptr::null(),
286+
};
287+
let res = unsafe {
288+
syscall(
289+
SYS_futex,
290+
&self.0,
291+
FutexOp::FUTEX_WAIT_REQUEUE_PI.bits(),
292+
val,
293+
timeout,
294+
&uaddr2.0,
295+
)
296+
};
297+
result(res)
298+
}
299+
}
300+
impl Default for Futex {
301+
fn default() -> Self {
302+
Self(0)
303+
}
304+
}

src/sys/mod.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -226,3 +226,6 @@ feature! {
226226
#![feature = "time"]
227227
pub mod timer;
228228
}
229+
230+
/// Fast user-space locking.
231+
pub mod futex;

0 commit comments

Comments
 (0)