Skip to content

Commit af02bb7

Browse files
committed
Implements miri shims for FUTEX_LOCK_PI, FUTEX_UNLOCK_PI
1 parent 59f8948 commit af02bb7

File tree

5 files changed

+339
-55
lines changed

5 files changed

+339
-55
lines changed

library/std/src/sys/pal/unix/futex.rs

+19-11
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ pub fn futex_wait(futex: &AtomicU32, expected: u32, timeout: Option<Duration>) -
2626
use super::time::Timespec;
2727
use crate::ptr::null;
2828
use crate::sync::atomic::Ordering::Relaxed;
29+
use crate::sys::cvt;
2930

3031
// Calculate the timeout as an absolute timespec.
3132
//
@@ -40,7 +41,7 @@ pub fn futex_wait(futex: &AtomicU32, expected: u32, timeout: Option<Duration>) -
4041
return true;
4142
}
4243

43-
let r = unsafe {
44+
let r = cvt(unsafe {
4445
cfg_if::cfg_if! {
4546
if #[cfg(target_os = "freebsd")] {
4647
// FreeBSD doesn't have futex(), but it has
@@ -77,12 +78,16 @@ pub fn futex_wait(futex: &AtomicU32, expected: u32, timeout: Option<Duration>) -
7778
compile_error!("unknown target_os");
7879
}
7980
}
80-
};
81-
82-
match (r < 0).then(super::os::errno) {
83-
Some(libc::ETIMEDOUT) => return false,
84-
Some(libc::EINTR) => continue,
85-
_ => return true,
81+
});
82+
83+
match r {
84+
Ok(_) => return true,
85+
Err(e) => match e.raw_os_error() {
86+
Some(libc::ETIMEDOUT) => return false,
87+
Some(libc::EINTR) => continue,
88+
Some(libc::EAGAIN) => return true,
89+
_ => panic!("failed to wait on futex: {e:?}"),
90+
},
8691
}
8792
}
8893
}
@@ -95,19 +100,22 @@ pub fn futex_wait(futex: &AtomicU32, expected: u32, timeout: Option<Duration>) -
95100
/// On some platforms, this always returns false.
96101
#[cfg(any(target_os = "linux", target_os = "android"))]
97102
pub fn futex_wake(futex: &AtomicU32) -> bool {
103+
use crate::sys::cvt;
98104
let ptr = futex as *const AtomicU32;
99105
let op = libc::FUTEX_WAKE | libc::FUTEX_PRIVATE_FLAG;
100-
unsafe { libc::syscall(libc::SYS_futex, ptr, op, 1) > 0 }
106+
cvt(unsafe { libc::syscall(libc::SYS_futex, ptr, op, 1) })
107+
.expect("failed to wake futex waiters")
108+
> 0
101109
}
102110

103111
/// Wakes up all threads that are waiting on `futex_wait` on this futex.
104112
#[cfg(any(target_os = "linux", target_os = "android"))]
105113
pub fn futex_wake_all(futex: &AtomicU32) {
114+
use crate::sys::cvt;
106115
let ptr = futex as *const AtomicU32;
107116
let op = libc::FUTEX_WAKE | libc::FUTEX_PRIVATE_FLAG;
108-
unsafe {
109-
libc::syscall(libc::SYS_futex, ptr, op, i32::MAX);
110-
}
117+
cvt(unsafe { libc::syscall(libc::SYS_futex, ptr, op, i32::MAX) })
118+
.expect("failed to wake futex waiters");
111119
}
112120

113121
// FreeBSD doesn't tell us how many threads are woken up, so this always returns false.

src/tools/miri/src/concurrency/sync.rs

+22-1
Original file line numberDiff line numberDiff line change
@@ -128,6 +128,8 @@ struct FutexWaiter {
128128
thread: ThreadId,
129129
/// The bitset used by FUTEX_*_BITSET, or u32::MAX for other operations.
130130
bitset: u32,
131+
/// Extra info stored for this waiter.
132+
extra: Option<u32>,
131133
}
132134

133135
/// The state of all synchronization objects.
@@ -695,6 +697,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
695697
timeout: Option<(TimeoutClock, TimeoutAnchor, Duration)>,
696698
retval_succ: Scalar,
697699
retval_timeout: Scalar,
700+
waiter_extra: Option<u32>,
698701
dest: MPlaceTy<'tcx>,
699702
errno_timeout: Scalar,
700703
) {
@@ -703,7 +706,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
703706
let futex = &mut this.machine.sync.futexes.entry(addr).or_default();
704707
let waiters = &mut futex.waiters;
705708
assert!(waiters.iter().all(|waiter| waiter.thread != thread), "thread is already waiting");
706-
waiters.push_back(FutexWaiter { thread, bitset });
709+
waiters.push_back(FutexWaiter { thread, bitset, extra: waiter_extra });
707710
this.block_thread(
708711
BlockReason::Futex { addr },
709712
timeout,
@@ -760,4 +763,22 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
760763
this.unblock_thread(waiter.thread, BlockReason::Futex { addr })?;
761764
interp_ok(true)
762765
}
766+
767+
/// Returns the extra info of the top waiter.
768+
/// The caller must ensure there are waiters.
769+
fn futex_top_waiter_extra(&mut self, addr: u64) -> Option<u32> {
770+
let this = self.eval_context_mut();
771+
let futex = &mut this.machine.sync.futexes.get(&addr)?;
772+
futex.waiters.front().unwrap().extra
773+
}
774+
775+
/// Returns the number of waiters
776+
fn futex_waiter_count(&mut self, addr: u64) -> usize {
777+
let this = self.eval_context_mut();
778+
if let Some(futex) = &mut this.machine.sync.futexes.get(&addr) {
779+
futex.waiters.len()
780+
} else {
781+
0
782+
}
783+
}
763784
}

0 commit comments

Comments
 (0)