commit
7137683543
5 changed files with 109 additions and 86 deletions
|
|
@ -1,6 +1,7 @@
|
|||
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;
|
||||
|
|
@ -46,8 +47,6 @@ macro_rules! declare_id {
|
|||
}
|
||||
pub(super) use declare_id;
|
||||
|
||||
declare_id!(MutexId);
|
||||
|
||||
/// The mutex state.
|
||||
#[derive(Default, Debug)]
|
||||
struct Mutex {
|
||||
|
|
@ -61,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.
|
||||
|
|
@ -144,7 +158,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>,
|
||||
|
|
@ -155,17 +168,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)?;
|
||||
}
|
||||
|
|
@ -174,10 +187,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())
|
||||
}
|
||||
|
|
@ -209,7 +221,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,
|
||||
|
|
@ -235,7 +247,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,
|
||||
|
|
@ -266,15 +281,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)
|
||||
}
|
||||
}
|
||||
|
|
@ -311,23 +326,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!(
|
||||
|
|
@ -347,9 +360,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() {
|
||||
|
|
@ -367,8 +380,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)
|
||||
|
|
@ -385,24 +402,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)?;
|
||||
|
|
@ -623,14 +641,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"
|
||||
|
|
@ -650,7 +668,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>,
|
||||
|
|
@ -665,7 +683,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.
|
||||
|
|
@ -673,7 +691,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)
|
||||
}
|
||||
),
|
||||
);
|
||||
|
|
|
|||
|
|
@ -141,7 +141,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.
|
||||
|
|
|
|||
|
|
@ -122,7 +122,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,
|
||||
|
|
|
|||
|
|
@ -14,10 +14,10 @@ use rustc_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)?;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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");
|
||||
}
|
||||
|
||||
|
|
@ -822,11 +823,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
|
||||
|
|
@ -846,7 +847,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
|
||||
|
|
@ -869,7 +870,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
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue