1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
/// Fast user-space locking.
///
/// Parameters
/// - `uaddr`: futex user address
/// - `op`: futex operations
/// - `val`: expected value
/// - `utime`: waiting timeout
/// - `uaddr2`: target futext user address used for requeue
///
/// # Exampless
///
/// ```rust
/// use std::sync::atomic::{AtomicU32, Ordering};
/// use std::thread;
/// use std::time::Duration;
///
/// const NOTIFY_WAIT: u32 = 0;
/// const NOTIFY_WAKE: u32 = 1;
///
/// fn wake_one(count: &AtomicU32) {
/// let ret = unsafe { nc::futex(count, nc::FUTEX_WAKE, NOTIFY_WAKE, None, None, 0) };
/// assert!(ret.is_ok());
/// }
///
/// fn wait(count: &AtomicU32, expected: u32) {
/// let ret = unsafe { nc::futex(count, nc::FUTEX_WAIT, expected, None, None, 0) };
/// assert!(ret.is_ok());
/// }
///
/// fn main() {
/// let notify = AtomicU32::new(0);
///
/// thread::scope(|s| {
/// // Create the notify thread.
/// s.spawn(|| {
/// // Wake up some other threads after one second.
/// println!("[notify] Sleep for 1s");
/// thread::sleep(Duration::from_secs(1));
/// println!("[notify] Wake up main thread");
/// notify.store(NOTIFY_WAKE, Ordering::Relaxed);
/// wake_one(¬ify);
/// });
///
/// // Main thread will wait until the notify thread wakes it up.
/// println!("[main] Waiting for notification..");
/// while notify.load(Ordering::Relaxed) == NOTIFY_WAIT {
/// wait(¬ify, NOTIFY_WAIT);
/// }
/// println!("[main] Got wake up");
/// });
/// }
/// ```
pub unsafe fn futex(
uaddr: &core::sync::atomic::AtomicU32,
op: i32,
val: u32,
utime: Option<×pec_t>,
uaddr2: Option<&core::sync::atomic::AtomicU32>,
val3: u32,
) -> Result<i32, Errno> {
use core::sync::atomic::AtomicU32;
let uaddr_ptr = uaddr as *const AtomicU32 as usize;
let op = op as usize;
let val = val as usize;
let utime_ptr = utime.map_or(core::ptr::null::<timespec_t>() as usize, |time_ref| {
time_ref as *const timespec_t as usize
});
let uaddr2_ptr = uaddr2.map_or(core::ptr::null::<AtomicU32>() as usize, |uaddr2_ref| {
uaddr2_ref as *const AtomicU32 as usize
});
let val3 = val3 as usize;
syscall6(SYS_FUTEX, uaddr_ptr, op, val, utime_ptr, uaddr2_ptr, val3).map(|ret| ret as i32)
}