Remove MutexID list

This commit is contained in:
tiif 2024-10-29 00:20:45 +08:00
parent f736269dd5
commit bfb36e35b9
5 changed files with 111 additions and 86 deletions

View file

@ -1,6 +1,9 @@
use std::cell::RefCell;
use std::collections::VecDeque;
use std::collections::hash_map::Entry;
use std::default::Default;
use std::ops::Not;
use std::rc::Rc;
use std::time::Duration;
use rustc_data_structures::fx::FxHashMap;
@ -44,8 +47,6 @@ macro_rules! declare_id {
}
pub(super) use declare_id;
declare_id!(MutexId);
/// The mutex state.
#[derive(Default, Debug)]
struct Mutex {
@ -59,6 +60,21 @@ struct Mutex {
clock: VClock,
}
#[derive(Default, Clone, Debug)]
pub struct MutexRef(Rc<RefCell<Mutex>>);
impl MutexRef {
fn new() -> Self {
MutexRef(Rc::new(RefCell::new(Mutex::default())))
}
}
impl VisitProvenance for MutexRef {
fn visit_provenance(&self, _visit: &mut VisitWith<'_>) {
// Mutex contains no provenance.
}
}
declare_id!(RwLockId);
/// The read-write lock state.
@ -133,7 +149,6 @@ struct FutexWaiter {
/// The state of all synchronization objects.
#[derive(Default, Debug)]
pub struct SynchronizationObjects {
mutexes: IndexVec<MutexId, Mutex>,
rwlocks: IndexVec<RwLockId, RwLock>,
condvars: IndexVec<CondvarId, Condvar>,
pub(super) init_onces: IndexVec<InitOnceId, InitOnce>,
@ -147,17 +162,17 @@ impl<'tcx> EvalContextExtPriv<'tcx> for crate::MiriInterpCx<'tcx> {}
pub(super) trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> {
fn condvar_reacquire_mutex(
&mut self,
mutex: MutexId,
mutex_ref: &MutexRef,
retval: Scalar,
dest: MPlaceTy<'tcx>,
) -> InterpResult<'tcx> {
let this = self.eval_context_mut();
if this.mutex_is_locked(mutex) {
assert_ne!(this.mutex_get_owner(mutex), this.active_thread());
this.mutex_enqueue_and_block(mutex, Some((retval, dest)));
if this.mutex_is_locked(mutex_ref) {
assert_ne!(this.mutex_get_owner(mutex_ref), this.active_thread());
this.mutex_enqueue_and_block(mutex_ref, Some((retval, dest)));
} else {
// We can have it right now!
this.mutex_lock(mutex);
this.mutex_lock(mutex_ref);
// Don't forget to write the return value.
this.write_scalar(retval, &dest)?;
}
@ -166,10 +181,9 @@ pub(super) trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> {
}
impl SynchronizationObjects {
pub fn mutex_create(&mut self) -> MutexId {
self.mutexes.push(Default::default())
pub fn mutex_create(&mut self) -> MutexRef {
MutexRef::new()
}
pub fn rwlock_create(&mut self) -> RwLockId {
self.rwlocks.push(Default::default())
}
@ -201,7 +215,7 @@ impl<'tcx> EvalContextExt<'tcx> for crate::MiriInterpCx<'tcx> {}
pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
/// Helper for lazily initialized `alloc_extra.sync` data:
/// this forces an immediate init.
fn lazy_sync_init<T: 'static + Copy>(
fn lazy_sync_init<T: 'static>(
&mut self,
primitive: &MPlaceTy<'tcx>,
init_offset: Size,
@ -227,7 +241,10 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
/// - If yes, fetches the data from `alloc_extra.sync`, or calls `missing_data` if that fails
/// and stores that in `alloc_extra.sync`.
/// - Otherwise, calls `new_data` to initialize the primitive.
fn lazy_sync_get_data<T: 'static + Copy>(
///
/// The return value is a *clone* of the stored data, so if you intend to mutate it
/// better wrap everything into an `Rc`.
fn lazy_sync_get_data<T: 'static + Clone>(
&mut self,
primitive: &MPlaceTy<'tcx>,
init_offset: Size,
@ -258,15 +275,15 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
let (alloc, offset, _) = this.ptr_get_alloc_id(primitive.ptr(), 0)?;
let (alloc_extra, _machine) = this.get_alloc_extra_mut(alloc)?;
if let Some(data) = alloc_extra.get_sync::<T>(offset) {
interp_ok(*data)
interp_ok(data.clone())
} else {
let data = missing_data()?;
alloc_extra.sync.insert(offset, Box::new(data));
alloc_extra.sync.insert(offset, Box::new(data.clone()));
interp_ok(data)
}
} else {
let data = new_data(this)?;
this.lazy_sync_init(primitive, init_offset, data)?;
this.lazy_sync_init(primitive, init_offset, data.clone())?;
interp_ok(data)
}
}
@ -298,23 +315,21 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
#[inline]
/// Get the id of the thread that currently owns this lock.
fn mutex_get_owner(&mut self, id: MutexId) -> ThreadId {
let this = self.eval_context_ref();
this.machine.sync.mutexes[id].owner.unwrap()
fn mutex_get_owner(&mut self, mutex_ref: &MutexRef) -> ThreadId {
mutex_ref.0.borrow().owner.unwrap()
}
#[inline]
/// Check if locked.
fn mutex_is_locked(&self, id: MutexId) -> bool {
let this = self.eval_context_ref();
this.machine.sync.mutexes[id].owner.is_some()
fn mutex_is_locked(&self, mutex_ref: &MutexRef) -> bool {
mutex_ref.0.borrow().owner.is_some()
}
/// Lock by setting the mutex owner and increasing the lock count.
fn mutex_lock(&mut self, id: MutexId) {
fn mutex_lock(&mut self, mutex_ref: &MutexRef) {
let this = self.eval_context_mut();
let thread = this.active_thread();
let mutex = &mut this.machine.sync.mutexes[id];
let mut mutex = mutex_ref.0.borrow_mut();
if let Some(current_owner) = mutex.owner {
assert_eq!(thread, current_owner, "mutex already locked by another thread");
assert!(
@ -334,9 +349,9 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
/// count. If the lock count reaches 0, release the lock and potentially
/// give to a new owner. If the lock was not locked by the current thread,
/// return `None`.
fn mutex_unlock(&mut self, id: MutexId) -> InterpResult<'tcx, Option<usize>> {
fn mutex_unlock(&mut self, mutex_ref: &MutexRef) -> InterpResult<'tcx, Option<usize>> {
let this = self.eval_context_mut();
let mutex = &mut this.machine.sync.mutexes[id];
let mut mutex = mutex_ref.0.borrow_mut();
interp_ok(if let Some(current_owner) = mutex.owner {
// Mutex is locked.
if current_owner != this.machine.threads.active_thread() {
@ -354,8 +369,12 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
mutex.clock.clone_from(clock)
});
}
if let Some(thread) = this.machine.sync.mutexes[id].queue.pop_front() {
this.unblock_thread(thread, BlockReason::Mutex(id))?;
let thread_id = mutex.queue.pop_front();
// We need to drop our mutex borrow before unblock_thread
// because it will be borrowed again in the unblock callback.
drop(mutex);
if thread_id.is_some() {
this.unblock_thread(thread_id.unwrap(), BlockReason::Mutex)?;
}
}
Some(old_lock_count)
@ -372,24 +391,25 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
#[inline]
fn mutex_enqueue_and_block(
&mut self,
id: MutexId,
mutex_ref: &MutexRef,
retval_dest: Option<(Scalar, MPlaceTy<'tcx>)>,
) {
let this = self.eval_context_mut();
assert!(this.mutex_is_locked(id), "queing on unlocked mutex");
assert!(this.mutex_is_locked(mutex_ref), "queuing on unlocked mutex");
let thread = this.active_thread();
this.machine.sync.mutexes[id].queue.push_back(thread);
mutex_ref.0.borrow_mut().queue.push_back(thread);
let mutex_ref = mutex_ref.clone();
this.block_thread(
BlockReason::Mutex(id),
BlockReason::Mutex,
None,
callback!(
@capture<'tcx> {
id: MutexId,
mutex_ref: MutexRef,
retval_dest: Option<(Scalar, MPlaceTy<'tcx>)>,
}
@unblock = |this| {
assert!(!this.mutex_is_locked(id));
this.mutex_lock(id);
assert!(!this.mutex_is_locked(&mutex_ref));
this.mutex_lock(&mutex_ref);
if let Some((retval, dest)) = retval_dest {
this.write_scalar(retval, &dest)?;
@ -610,14 +630,14 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
fn condvar_wait(
&mut self,
condvar: CondvarId,
mutex: MutexId,
mutex_ref: MutexRef,
timeout: Option<(TimeoutClock, TimeoutAnchor, Duration)>,
retval_succ: Scalar,
retval_timeout: Scalar,
dest: MPlaceTy<'tcx>,
) -> InterpResult<'tcx> {
let this = self.eval_context_mut();
if let Some(old_locked_count) = this.mutex_unlock(mutex)? {
if let Some(old_locked_count) = this.mutex_unlock(&mutex_ref)? {
if old_locked_count != 1 {
throw_unsup_format!(
"awaiting a condvar on a mutex acquired multiple times is not supported"
@ -637,7 +657,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
callback!(
@capture<'tcx> {
condvar: CondvarId,
mutex: MutexId,
mutex_ref: MutexRef,
retval_succ: Scalar,
retval_timeout: Scalar,
dest: MPlaceTy<'tcx>,
@ -652,7 +672,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
}
// Try to acquire the mutex.
// The timeout only applies to the first wait (until the signal), not for mutex acquisition.
this.condvar_reacquire_mutex(mutex, retval_succ, dest)
this.condvar_reacquire_mutex(&mutex_ref, retval_succ, dest)
}
@timeout = |this| {
// We have to remove the waiter from the queue again.
@ -660,7 +680,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
let waiters = &mut this.machine.sync.condvars[condvar].waiters;
waiters.retain(|waiter| *waiter != thread);
// Now get back the lock.
this.condvar_reacquire_mutex(mutex, retval_timeout, dest)
this.condvar_reacquire_mutex(&mutex_ref, retval_timeout, dest)
}
),
);

View file

@ -162,7 +162,7 @@ pub enum BlockReason {
/// Waiting for time to pass.
Sleep,
/// Blocked on a mutex.
Mutex(MutexId),
Mutex,
/// Blocked on a condition variable.
Condvar(CondvarId),
/// Blocked on a reader-writer lock.

View file

@ -121,7 +121,7 @@ pub use crate::concurrency::data_race::{
};
pub use crate::concurrency::init_once::{EvalContextExt as _, InitOnceId};
pub use crate::concurrency::sync::{
CondvarId, EvalContextExt as _, MutexId, RwLockId, SynchronizationObjects,
CondvarId, EvalContextExt as _, MutexRef, RwLockId, SynchronizationObjects,
};
pub use crate::concurrency::thread::{
BlockReason, EvalContextExt as _, StackEmptyCallback, ThreadId, ThreadManager, TimeoutAnchor,

View file

@ -14,10 +14,10 @@ use rustc_target::abi::Size;
use crate::*;
#[derive(Copy, Clone)]
#[derive(Clone)]
enum MacOsUnfairLock {
Poisoned,
Active { id: MutexId },
Active { mutex_ref: MutexRef },
}
impl<'tcx> EvalContextExtPriv<'tcx> for crate::MiriInterpCx<'tcx> {}
@ -42,8 +42,8 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> {
interp_ok(MacOsUnfairLock::Poisoned)
},
|ecx| {
let id = ecx.machine.sync.mutex_create();
interp_ok(MacOsUnfairLock::Active { id })
let mutex_ref = ecx.machine.sync.mutex_create();
interp_ok(MacOsUnfairLock::Active { mutex_ref })
},
)
}
@ -54,7 +54,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
fn os_unfair_lock_lock(&mut self, lock_op: &OpTy<'tcx>) -> InterpResult<'tcx> {
let this = self.eval_context_mut();
let MacOsUnfairLock::Active { id } = this.os_unfair_lock_get_data(lock_op)? else {
let MacOsUnfairLock::Active { mutex_ref } = this.os_unfair_lock_get_data(lock_op)? else {
// Trying to get a poisoned lock. Just block forever...
this.block_thread(
BlockReason::Sleep,
@ -69,17 +69,17 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
return interp_ok(());
};
if this.mutex_is_locked(id) {
if this.mutex_get_owner(id) == this.active_thread() {
if this.mutex_is_locked(&mutex_ref) {
if this.mutex_get_owner(&mutex_ref) == this.active_thread() {
// Matching the current macOS implementation: abort on reentrant locking.
throw_machine_stop!(TerminationInfo::Abort(
"attempted to lock an os_unfair_lock that is already locked by the current thread".to_owned()
));
}
this.mutex_enqueue_and_block(id, None);
this.mutex_enqueue_and_block(&mutex_ref, None);
} else {
this.mutex_lock(id);
this.mutex_lock(&mutex_ref);
}
interp_ok(())
@ -92,18 +92,18 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
) -> InterpResult<'tcx> {
let this = self.eval_context_mut();
let MacOsUnfairLock::Active { id } = this.os_unfair_lock_get_data(lock_op)? else {
let MacOsUnfairLock::Active { mutex_ref } = this.os_unfair_lock_get_data(lock_op)? else {
// Trying to get a poisoned lock. That never works.
this.write_scalar(Scalar::from_bool(false), dest)?;
return interp_ok(());
};
if this.mutex_is_locked(id) {
if this.mutex_is_locked(&mutex_ref) {
// Contrary to the blocking lock function, this does not check for
// reentrancy.
this.write_scalar(Scalar::from_bool(false), dest)?;
} else {
this.mutex_lock(id);
this.mutex_lock(&mutex_ref);
this.write_scalar(Scalar::from_bool(true), dest)?;
}
@ -113,7 +113,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
fn os_unfair_lock_unlock(&mut self, lock_op: &OpTy<'tcx>) -> InterpResult<'tcx> {
let this = self.eval_context_mut();
let MacOsUnfairLock::Active { id } = this.os_unfair_lock_get_data(lock_op)? else {
let MacOsUnfairLock::Active { mutex_ref } = this.os_unfair_lock_get_data(lock_op)? else {
// The lock is poisoned, who knows who owns it... we'll pretend: someone else.
throw_machine_stop!(TerminationInfo::Abort(
"attempted to unlock an os_unfair_lock not owned by the current thread".to_owned()
@ -121,7 +121,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
};
// Now, unlock.
if this.mutex_unlock(id)?.is_none() {
if this.mutex_unlock(&mutex_ref)?.is_none() {
// Matching the current macOS implementation: abort.
throw_machine_stop!(TerminationInfo::Abort(
"attempted to unlock an os_unfair_lock not owned by the current thread".to_owned()
@ -130,7 +130,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
// If the lock is not locked by anyone now, it went quer.
// Reset to zero so that it can be moved and initialized again for the next phase.
if !this.mutex_is_locked(id) {
if !this.mutex_is_locked(&mutex_ref) {
let lock_place = this.deref_pointer_as(lock_op, this.machine.layouts.u32)?;
this.write_scalar_atomic(Scalar::from_u32(0), &lock_place, AtomicWriteOrd::Relaxed)?;
}
@ -141,13 +141,15 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
fn os_unfair_lock_assert_owner(&mut self, lock_op: &OpTy<'tcx>) -> InterpResult<'tcx> {
let this = self.eval_context_mut();
let MacOsUnfairLock::Active { id } = this.os_unfair_lock_get_data(lock_op)? else {
let MacOsUnfairLock::Active { mutex_ref } = this.os_unfair_lock_get_data(lock_op)? else {
// The lock is poisoned, who knows who owns it... we'll pretend: someone else.
throw_machine_stop!(TerminationInfo::Abort(
"called os_unfair_lock_assert_owner on an os_unfair_lock not owned by the current thread".to_owned()
));
};
if !this.mutex_is_locked(id) || this.mutex_get_owner(id) != this.active_thread() {
if !this.mutex_is_locked(&mutex_ref)
|| this.mutex_get_owner(&mutex_ref) != this.active_thread()
{
throw_machine_stop!(TerminationInfo::Abort(
"called os_unfair_lock_assert_owner on an os_unfair_lock not owned by the current thread".to_owned()
));
@ -161,11 +163,13 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
fn os_unfair_lock_assert_not_owner(&mut self, lock_op: &OpTy<'tcx>) -> InterpResult<'tcx> {
let this = self.eval_context_mut();
let MacOsUnfairLock::Active { id } = this.os_unfair_lock_get_data(lock_op)? else {
let MacOsUnfairLock::Active { mutex_ref } = this.os_unfair_lock_get_data(lock_op)? else {
// The lock is poisoned, who knows who owns it... we'll pretend: someone else.
return interp_ok(());
};
if this.mutex_is_locked(id) && this.mutex_get_owner(id) == this.active_thread() {
if this.mutex_is_locked(&mutex_ref)
&& this.mutex_get_owner(&mutex_ref) == this.active_thread()
{
throw_machine_stop!(TerminationInfo::Abort(
"called os_unfair_lock_assert_not_owner on an os_unfair_lock owned by the current thread".to_owned()
));
@ -173,7 +177,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
// If the lock is not locked by anyone now, it went quer.
// Reset to zero so that it can be moved and initialized again for the next phase.
if !this.mutex_is_locked(id) {
if !this.mutex_is_locked(&mutex_ref) {
let lock_place = this.deref_pointer_as(lock_op, this.machine.layouts.u32)?;
this.write_scalar_atomic(Scalar::from_u32(0), &lock_place, AtomicWriteOrd::Relaxed)?;
}

View file

@ -116,9 +116,9 @@ enum MutexKind {
ErrorCheck,
}
#[derive(Debug, Clone, Copy)]
#[derive(Debug, Clone)]
struct PthreadMutex {
id: MutexId,
mutex_ref: MutexRef,
kind: MutexKind,
}
@ -175,15 +175,13 @@ fn mutex_create<'tcx>(
) -> InterpResult<'tcx, PthreadMutex> {
let mutex = ecx.deref_pointer(mutex_ptr)?;
let id = ecx.machine.sync.mutex_create();
let data = PthreadMutex { id, kind };
ecx.lazy_sync_init(&mutex, mutex_init_offset(ecx)?, data)?;
let data = PthreadMutex { mutex_ref: id, kind };
ecx.lazy_sync_init(&mutex, mutex_init_offset(ecx)?, data.clone())?;
interp_ok(data)
}
/// Returns the `MutexId` of the mutex stored at `mutex_op`.
///
/// `mutex_get_id` will also check if the mutex has been moved since its first use and
/// return an error if it has.
/// Returns the mutex data stored at the address that `mutex_ptr` points to.
/// Will raise an error if the mutex has been moved since its first use.
fn mutex_get_data<'tcx, 'a>(
ecx: &'a mut MiriInterpCx<'tcx>,
mutex_ptr: &OpTy<'tcx>,
@ -196,7 +194,7 @@ fn mutex_get_data<'tcx, 'a>(
|ecx| {
let kind = mutex_kind_from_static_initializer(ecx, &mutex)?;
let id = ecx.machine.sync.mutex_create();
interp_ok(PthreadMutex { id, kind })
interp_ok(PthreadMutex { mutex_ref: id, kind })
},
)
}
@ -502,10 +500,13 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
let mutex = mutex_get_data(this, mutex_op)?;
let ret = if this.mutex_is_locked(mutex.id) {
let owner_thread = this.mutex_get_owner(mutex.id);
let ret = if this.mutex_is_locked(&mutex.mutex_ref) {
let owner_thread = this.mutex_get_owner(&mutex.mutex_ref);
if owner_thread != this.active_thread() {
this.mutex_enqueue_and_block(mutex.id, Some((Scalar::from_i32(0), dest.clone())));
this.mutex_enqueue_and_block(
&mutex.mutex_ref,
Some((Scalar::from_i32(0), dest.clone())),
);
return interp_ok(());
} else {
// Trying to acquire the same mutex again.
@ -517,14 +518,14 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
MutexKind::Normal => throw_machine_stop!(TerminationInfo::Deadlock),
MutexKind::ErrorCheck => this.eval_libc_i32("EDEADLK"),
MutexKind::Recursive => {
this.mutex_lock(mutex.id);
this.mutex_lock(&mutex.mutex_ref);
0
}
}
}
} else {
// The mutex is unlocked. Let's lock it.
this.mutex_lock(mutex.id);
this.mutex_lock(&mutex.mutex_ref);
0
};
this.write_scalar(Scalar::from_i32(ret), dest)?;
@ -536,8 +537,8 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
let mutex = mutex_get_data(this, mutex_op)?;
interp_ok(Scalar::from_i32(if this.mutex_is_locked(mutex.id) {
let owner_thread = this.mutex_get_owner(mutex.id);
interp_ok(Scalar::from_i32(if this.mutex_is_locked(&mutex.mutex_ref) {
let owner_thread = this.mutex_get_owner(&mutex.mutex_ref);
if owner_thread != this.active_thread() {
this.eval_libc_i32("EBUSY")
} else {
@ -545,14 +546,14 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
MutexKind::Default | MutexKind::Normal | MutexKind::ErrorCheck =>
this.eval_libc_i32("EBUSY"),
MutexKind::Recursive => {
this.mutex_lock(mutex.id);
this.mutex_lock(&mutex.mutex_ref);
0
}
}
}
} else {
// The mutex is unlocked. Let's lock it.
this.mutex_lock(mutex.id);
this.mutex_lock(&mutex.mutex_ref);
0
}))
}
@ -562,7 +563,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
let mutex = mutex_get_data(this, mutex_op)?;
if let Some(_old_locked_count) = this.mutex_unlock(mutex.id)? {
if let Some(_old_locked_count) = this.mutex_unlock(&mutex.mutex_ref)? {
// The mutex was locked by the current thread.
interp_ok(Scalar::from_i32(0))
} else {
@ -591,7 +592,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
// since we make the field unint below.
let mutex = mutex_get_data(this, mutex_op)?;
if this.mutex_is_locked(mutex.id) {
if this.mutex_is_locked(&mutex.mutex_ref) {
throw_ub_format!("destroyed a locked mutex");
}
@ -823,11 +824,11 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
let this = self.eval_context_mut();
let data = cond_get_data(this, cond_op)?;
let mutex_id = mutex_get_data(this, mutex_op)?.id;
let mutex_ref = mutex_get_data(this, mutex_op)?.mutex_ref;
this.condvar_wait(
data.id,
mutex_id,
mutex_ref,
None, // no timeout
Scalar::from_i32(0),
Scalar::from_i32(0), // retval_timeout -- unused
@ -847,7 +848,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
let this = self.eval_context_mut();
let data = cond_get_data(this, cond_op)?;
let mutex_id = mutex_get_data(this, mutex_op)?.id;
let mutex_ref = mutex_get_data(this, mutex_op)?.mutex_ref;
// Extract the timeout.
let duration = match this
@ -870,7 +871,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
this.condvar_wait(
data.id,
mutex_id,
mutex_ref,
Some((timeout_clock, TimeoutAnchor::Absolute, duration)),
Scalar::from_i32(0),
this.eval_libc("ETIMEDOUT"), // retval_timeout