Skip to content

Commit 4fa5376

Browse files
committed
Implement futex_wait and futex_wait.
This does not support futex_wait with a timeout yet.
1 parent 266b75f commit 4fa5376

File tree

2 files changed

+94
-0
lines changed

2 files changed

+94
-0
lines changed

src/shims/posix/linux/foreign_items.rs

+67
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
use rustc_middle::mir;
2+
use rustc_target::abi::{Align, Size};
23

34
use crate::*;
45
use crate::helpers::check_arg_count;
@@ -120,6 +121,10 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
120121
.eval_libc("SYS_statx")?
121122
.to_machine_usize(this)?;
122123

124+
let sys_futex = this
125+
.eval_libc("SYS_futex")?
126+
.to_machine_usize(this)?;
127+
123128
if args.is_empty() {
124129
throw_ub_format!("incorrect number of arguments for syscall: got 0, expected at least 1");
125130
}
@@ -139,6 +144,10 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
139144
let result = this.linux_statx(dirfd, pathname, flags, mask, statxbuf)?;
140145
this.write_scalar(Scalar::from_machine_isize(result.into(), this), dest)?;
141146
}
147+
// `futex` is used by some synchonization primitives.
148+
id if id == sys_futex => {
149+
futex(this, args, dest)?;
150+
}
142151
id => throw_unsup_format!("miri does not support syscall ID {}", id),
143152
}
144153
}
@@ -192,3 +201,61 @@ fn getrandom<'tcx>(
192201
this.write_scalar(Scalar::from_machine_usize(len, this), dest)?;
193202
Ok(())
194203
}
204+
205+
fn futex<'tcx>(
206+
this: &mut MiriEvalContext<'_, 'tcx>,
207+
args: &[OpTy<'tcx, Tag>],
208+
dest: PlaceTy<'tcx, Tag>,
209+
) -> InterpResult<'tcx> {
210+
if args.len() < 4 {
211+
throw_ub_format!("incorrect number of arguments for futex syscall: got {}, expected at least 4", args.len());
212+
}
213+
let addr = this.read_scalar(args[1])?.check_init()?;
214+
let op = this.read_scalar(args[2])?.to_i32()?;
215+
let val = this.read_scalar(args[3])?.to_i32()?;
216+
217+
this.memory.check_ptr_access(addr, Size::from_bytes(4), Align::from_bytes(4).unwrap())?;
218+
219+
let addr = addr.assert_ptr();
220+
221+
let thread = this.get_active_thread();
222+
223+
let futex_private = this.eval_libc_i32("FUTEX_PRIVATE_FLAG")?;
224+
let futex_wait = this.eval_libc_i32("FUTEX_WAIT")?;
225+
let futex_wake = this.eval_libc_i32("FUTEX_WAKE")?;
226+
227+
match op & !futex_private {
228+
op if op == futex_wait => {
229+
if args.len() < 5 {
230+
throw_ub_format!("incorrect number of arguments for FUTEX_WAIT syscall: got {}, expected at least 5", args.len());
231+
}
232+
let timeout = this.read_scalar(args[4])?.check_init()?;
233+
if !this.is_null(timeout)? {
234+
throw_ub_format!("miri does not support timeouts for futex operations");
235+
}
236+
let futex_val = this.read_scalar_at_offset(args[1], 0, this.machine.layouts.i32)?.to_i32()?;
237+
if val == futex_val {
238+
this.block_thread(thread);
239+
this.futex_wait(addr, thread);
240+
} else {
241+
let eagain = this.eval_libc("EAGAIN")?;
242+
this.set_last_error(eagain)?;
243+
}
244+
}
245+
op if op == futex_wake => {
246+
let mut n = 0;
247+
for _ in 0..val {
248+
if let Some(thread) = this.futex_wake(addr) {
249+
this.unblock_thread(thread);
250+
n += 1;
251+
} else {
252+
break;
253+
}
254+
}
255+
this.write_scalar(Scalar::from_i32(n), dest)?;
256+
}
257+
op => throw_unsup_format!("miri does not support SYS_futex operation {}", op),
258+
}
259+
260+
Ok(())
261+
}

src/sync.rs

+27
Original file line numberDiff line numberDiff line change
@@ -96,12 +96,26 @@ struct Condvar {
9696
waiters: VecDeque<CondvarWaiter>,
9797
}
9898

99+
/// The futex state.
100+
#[derive(Default, Debug)]
101+
struct Futex {
102+
waiters: VecDeque<FutexWaiter>,
103+
}
104+
105+
/// A thread waiting on a futex.
106+
#[derive(Debug)]
107+
struct FutexWaiter {
108+
/// The thread that is waiting on this futex.
109+
thread: ThreadId,
110+
}
111+
99112
/// The state of all synchronization variables.
100113
#[derive(Default, Debug)]
101114
pub(super) struct SynchronizationState {
102115
mutexes: IndexVec<MutexId, Mutex>,
103116
rwlocks: IndexVec<RwLockId, RwLock>,
104117
condvars: IndexVec<CondvarId, Condvar>,
118+
futexes: HashMap<Pointer<stacked_borrows::Tag>, Futex>,
105119
}
106120

107121
// Private extension trait for local helper methods
@@ -403,4 +417,17 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
403417
let this = self.eval_context_mut();
404418
this.machine.threads.sync.condvars[id].waiters.retain(|waiter| waiter.thread != thread);
405419
}
420+
421+
fn futex_wait(&mut self, addr: Pointer<stacked_borrows::Tag>, thread: ThreadId) {
422+
let this = self.eval_context_mut();
423+
let waiters = &mut this.machine.threads.sync.futexes.entry(addr).or_default().waiters;
424+
assert!(waiters.iter().all(|waiter| waiter.thread != thread), "thread is already waiting");
425+
waiters.push_back(FutexWaiter { thread });
426+
}
427+
428+
fn futex_wake(&mut self, addr: Pointer<stacked_borrows::Tag>) -> Option<ThreadId> {
429+
let this = self.eval_context_mut();
430+
let waiters = &mut this.machine.threads.sync.futexes.get_mut(&addr)?.waiters;
431+
waiters.pop_front().map(|waiter| waiter.thread)
432+
}
406433
}

0 commit comments

Comments
 (0)