Implement futex_wait and futex_wake.
This does not support futex_wait with a timeout yet.
This commit is contained in:
parent
266b75faec
commit
1ffc5bb563
2 changed files with 94 additions and 0 deletions
|
|
@ -1,4 +1,5 @@
|
|||
use rustc_middle::mir;
|
||||
use rustc_target::abi::{Align, Size};
|
||||
|
||||
use crate::*;
|
||||
use crate::helpers::check_arg_count;
|
||||
|
|
@ -120,6 +121,10 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
|
|||
.eval_libc("SYS_statx")?
|
||||
.to_machine_usize(this)?;
|
||||
|
||||
let sys_futex = this
|
||||
.eval_libc("SYS_futex")?
|
||||
.to_machine_usize(this)?;
|
||||
|
||||
if args.is_empty() {
|
||||
throw_ub_format!("incorrect number of arguments for syscall: got 0, expected at least 1");
|
||||
}
|
||||
|
|
@ -139,6 +144,10 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
|
|||
let result = this.linux_statx(dirfd, pathname, flags, mask, statxbuf)?;
|
||||
this.write_scalar(Scalar::from_machine_isize(result.into(), this), dest)?;
|
||||
}
|
||||
// `futex` is used by some synchonization primitives.
|
||||
id if id == sys_futex => {
|
||||
futex(this, args, dest)?;
|
||||
}
|
||||
id => throw_unsup_format!("miri does not support syscall ID {}", id),
|
||||
}
|
||||
}
|
||||
|
|
@ -192,3 +201,61 @@ fn getrandom<'tcx>(
|
|||
this.write_scalar(Scalar::from_machine_usize(len, this), dest)?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn futex<'tcx>(
|
||||
this: &mut MiriEvalContext<'_, 'tcx>,
|
||||
args: &[OpTy<'tcx, Tag>],
|
||||
dest: PlaceTy<'tcx, Tag>,
|
||||
) -> InterpResult<'tcx> {
|
||||
if args.len() < 4 {
|
||||
throw_ub_format!("incorrect number of arguments for futex syscall: got {}, expected at least 4", args.len());
|
||||
}
|
||||
let addr = this.read_scalar(args[1])?.check_init()?;
|
||||
let op = this.read_scalar(args[2])?.to_i32()?;
|
||||
let val = this.read_scalar(args[3])?.to_i32()?;
|
||||
|
||||
this.memory.check_ptr_access(addr, Size::from_bytes(4), Align::from_bytes(4).unwrap())?;
|
||||
|
||||
let addr = addr.assert_ptr();
|
||||
|
||||
let thread = this.get_active_thread();
|
||||
|
||||
let futex_private = this.eval_libc_i32("FUTEX_PRIVATE_FLAG")?;
|
||||
let futex_wait = this.eval_libc_i32("FUTEX_WAIT")?;
|
||||
let futex_wake = this.eval_libc_i32("FUTEX_WAKE")?;
|
||||
|
||||
match op & !futex_private {
|
||||
op if op == futex_wait => {
|
||||
if args.len() < 5 {
|
||||
throw_ub_format!("incorrect number of arguments for FUTEX_WAIT syscall: got {}, expected at least 5", args.len());
|
||||
}
|
||||
let timeout = this.read_scalar(args[4])?.check_init()?;
|
||||
if !this.is_null(timeout)? {
|
||||
throw_ub_format!("miri does not support timeouts for futex operations");
|
||||
}
|
||||
let futex_val = this.read_scalar_at_offset(args[1], 0, this.machine.layouts.i32)?.to_i32()?;
|
||||
if val == futex_val {
|
||||
this.block_thread(thread);
|
||||
this.futex_wait(addr, thread);
|
||||
} else {
|
||||
let eagain = this.eval_libc("EAGAIN")?;
|
||||
this.set_last_error(eagain)?;
|
||||
}
|
||||
}
|
||||
op if op == futex_wake => {
|
||||
let mut n = 0;
|
||||
for _ in 0..val {
|
||||
if let Some(thread) = this.futex_wake(addr) {
|
||||
this.unblock_thread(thread);
|
||||
n += 1;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
this.write_scalar(Scalar::from_i32(n), dest)?;
|
||||
}
|
||||
op => throw_unsup_format!("miri does not support SYS_futex operation {}", op),
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
|
|
|||
27
src/sync.rs
27
src/sync.rs
|
|
@ -96,12 +96,26 @@ struct Condvar {
|
|||
waiters: VecDeque<CondvarWaiter>,
|
||||
}
|
||||
|
||||
/// The futex state.
|
||||
#[derive(Default, Debug)]
|
||||
struct Futex {
|
||||
waiters: VecDeque<FutexWaiter>,
|
||||
}
|
||||
|
||||
/// A thread waiting on a futex.
|
||||
#[derive(Debug)]
|
||||
struct FutexWaiter {
|
||||
/// The thread that is waiting on this futex.
|
||||
thread: ThreadId,
|
||||
}
|
||||
|
||||
/// The state of all synchronization variables.
|
||||
#[derive(Default, Debug)]
|
||||
pub(super) struct SynchronizationState {
|
||||
mutexes: IndexVec<MutexId, Mutex>,
|
||||
rwlocks: IndexVec<RwLockId, RwLock>,
|
||||
condvars: IndexVec<CondvarId, Condvar>,
|
||||
futexes: HashMap<Pointer<stacked_borrows::Tag>, Futex>,
|
||||
}
|
||||
|
||||
// Private extension trait for local helper methods
|
||||
|
|
@ -403,4 +417,17 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriEvalContextExt<'mir, 'tcx
|
|||
let this = self.eval_context_mut();
|
||||
this.machine.threads.sync.condvars[id].waiters.retain(|waiter| waiter.thread != thread);
|
||||
}
|
||||
|
||||
fn futex_wait(&mut self, addr: Pointer<stacked_borrows::Tag>, thread: ThreadId) {
|
||||
let this = self.eval_context_mut();
|
||||
let waiters = &mut this.machine.threads.sync.futexes.entry(addr).or_default().waiters;
|
||||
assert!(waiters.iter().all(|waiter| waiter.thread != thread), "thread is already waiting");
|
||||
waiters.push_back(FutexWaiter { thread });
|
||||
}
|
||||
|
||||
fn futex_wake(&mut self, addr: Pointer<stacked_borrows::Tag>) -> Option<ThreadId> {
|
||||
let this = self.eval_context_mut();
|
||||
let waiters = &mut this.machine.threads.sync.futexes.get_mut(&addr)?.waiters;
|
||||
waiters.pop_front().map(|waiter| waiter.thread)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue