sync support: dont implicitly clone inside the general sync machinery

This commit is contained in:
Ralf Jung 2024-11-11 08:02:28 +01:00
parent 7137683543
commit 424bc60a13
4 changed files with 64 additions and 38 deletions

View file

@ -221,12 +221,16 @@ 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>(
&mut self,
/// Return a reference to the data in the machine state.
fn lazy_sync_init<'a, T: 'static>(
&'a mut self,
primitive: &MPlaceTy<'tcx>,
init_offset: Size,
data: T,
) -> InterpResult<'tcx> {
) -> InterpResult<'tcx, &'a T>
where
'tcx: 'a,
{
let this = self.eval_context_mut();
let (alloc, offset, _) = this.ptr_get_alloc_id(primitive.ptr(), 0)?;
@ -239,7 +243,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
&init_field,
AtomicWriteOrd::Relaxed,
)?;
interp_ok(())
interp_ok(this.get_alloc_extra(alloc)?.get_sync::<T>(offset).unwrap())
}
/// Helper for lazily initialized `alloc_extra.sync` data:
@ -248,15 +252,17 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
/// and stores that in `alloc_extra.sync`.
/// - Otherwise, calls `new_data` to initialize the primitive.
///
/// 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,
/// Return a reference to the data in the machine state.
fn lazy_sync_get_data<'a, T: 'static>(
&'a mut self,
primitive: &MPlaceTy<'tcx>,
init_offset: Size,
missing_data: impl FnOnce() -> InterpResult<'tcx, T>,
new_data: impl FnOnce(&mut MiriInterpCx<'tcx>) -> InterpResult<'tcx, T>,
) -> InterpResult<'tcx, T> {
) -> InterpResult<'tcx, &'a T>
where
'tcx: 'a,
{
let this = self.eval_context_mut();
// Check if this is already initialized. Needs to be atomic because we can race with another
@ -280,17 +286,15 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
// or else it has been moved illegally.
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.clone())
} else {
// Due to borrow checker reasons, we have to do the lookup twice.
if alloc_extra.get_sync::<T>(offset).is_none() {
let data = missing_data()?;
alloc_extra.sync.insert(offset, Box::new(data.clone()));
interp_ok(data)
alloc_extra.sync.insert(offset, Box::new(data));
}
interp_ok(alloc_extra.get_sync::<T>(offset).unwrap())
} else {
let data = new_data(this)?;
this.lazy_sync_init(primitive, init_offset, data.clone())?;
interp_ok(data)
this.lazy_sync_init(primitive, init_offset, data)
}
}
@ -326,7 +330,7 @@ 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, mutex_ref: &MutexRef) -> ThreadId {
fn mutex_get_owner(&self, mutex_ref: &MutexRef) -> ThreadId {
mutex_ref.0.borrow().owner.unwrap()
}

View file

@ -22,10 +22,13 @@ enum MacOsUnfairLock {
impl<'tcx> EvalContextExtPriv<'tcx> for crate::MiriInterpCx<'tcx> {}
trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> {
fn os_unfair_lock_get_data(
&mut self,
fn os_unfair_lock_get_data<'a>(
&'a mut self,
lock_ptr: &OpTy<'tcx>,
) -> InterpResult<'tcx, MacOsUnfairLock> {
) -> InterpResult<'tcx, &'a MacOsUnfairLock>
where
'tcx: 'a,
{
let this = self.eval_context_mut();
let lock = this.deref_pointer(lock_ptr)?;
this.lazy_sync_get_data(
@ -68,6 +71,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
);
return interp_ok(());
};
let mutex_ref = mutex_ref.clone();
if this.mutex_is_locked(&mutex_ref) {
if this.mutex_get_owner(&mutex_ref) == this.active_thread() {
@ -97,6 +101,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
this.write_scalar(Scalar::from_bool(false), dest)?;
return interp_ok(());
};
let mutex_ref = mutex_ref.clone();
if this.mutex_is_locked(&mutex_ref) {
// Contrary to the blocking lock function, this does not check for
@ -119,6 +124,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
"attempted to unlock an os_unfair_lock not owned by the current thread".to_owned()
));
};
let mutex_ref = mutex_ref.clone();
// Now, unlock.
if this.mutex_unlock(&mutex_ref)?.is_none() {
@ -147,6 +153,8 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
"called os_unfair_lock_assert_owner on an os_unfair_lock not owned by the current thread".to_owned()
));
};
let mutex_ref = mutex_ref.clone();
if !this.mutex_is_locked(&mutex_ref)
|| this.mutex_get_owner(&mutex_ref) != this.active_thread()
{
@ -167,6 +175,8 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
// The lock is poisoned, who knows who owns it... we'll pretend: someone else.
return interp_ok(());
};
let mutex_ref = mutex_ref.clone();
if this.mutex_is_locked(&mutex_ref)
&& this.mutex_get_owner(&mutex_ref) == this.active_thread()
{

View file

@ -185,7 +185,10 @@ fn mutex_create<'tcx>(
fn mutex_get_data<'tcx, 'a>(
ecx: &'a mut MiriInterpCx<'tcx>,
mutex_ptr: &OpTy<'tcx>,
) -> InterpResult<'tcx, PthreadMutex> {
) -> InterpResult<'tcx, &'a PthreadMutex>
where
'tcx: 'a,
{
let mutex = ecx.deref_pointer(mutex_ptr)?;
ecx.lazy_sync_get_data(
&mutex,
@ -259,10 +262,13 @@ fn rwlock_init_offset<'tcx>(ecx: &MiriInterpCx<'tcx>) -> InterpResult<'tcx, Size
interp_ok(offset)
}
fn rwlock_get_data<'tcx>(
ecx: &mut MiriInterpCx<'tcx>,
fn rwlock_get_data<'tcx, 'a>(
ecx: &'a mut MiriInterpCx<'tcx>,
rwlock_ptr: &OpTy<'tcx>,
) -> InterpResult<'tcx, PthreadRwLock> {
) -> InterpResult<'tcx, &'a PthreadRwLock>
where
'tcx: 'a,
{
let rwlock = ecx.deref_pointer(rwlock_ptr)?;
ecx.lazy_sync_get_data(
&rwlock,
@ -389,10 +395,13 @@ fn cond_create<'tcx>(
interp_ok(data)
}
fn cond_get_data<'tcx>(
ecx: &mut MiriInterpCx<'tcx>,
fn cond_get_data<'tcx, 'a>(
ecx: &'a mut MiriInterpCx<'tcx>,
cond_ptr: &OpTy<'tcx>,
) -> InterpResult<'tcx, PthreadCondvar> {
) -> InterpResult<'tcx, &'a PthreadCondvar>
where
'tcx: 'a,
{
let cond = ecx.deref_pointer(cond_ptr)?;
ecx.lazy_sync_get_data(
&cond,
@ -498,7 +507,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
) -> InterpResult<'tcx> {
let this = self.eval_context_mut();
let mutex = mutex_get_data(this, mutex_op)?;
let mutex = mutex_get_data(this, mutex_op)?.clone();
let ret = if this.mutex_is_locked(&mutex.mutex_ref) {
let owner_thread = this.mutex_get_owner(&mutex.mutex_ref);
@ -535,7 +544,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
fn pthread_mutex_trylock(&mut self, mutex_op: &OpTy<'tcx>) -> InterpResult<'tcx, Scalar> {
let this = self.eval_context_mut();
let mutex = mutex_get_data(this, mutex_op)?;
let mutex = mutex_get_data(this, mutex_op)?.clone();
interp_ok(Scalar::from_i32(if this.mutex_is_locked(&mutex.mutex_ref) {
let owner_thread = this.mutex_get_owner(&mutex.mutex_ref);
@ -561,7 +570,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
fn pthread_mutex_unlock(&mut self, mutex_op: &OpTy<'tcx>) -> InterpResult<'tcx, Scalar> {
let this = self.eval_context_mut();
let mutex = mutex_get_data(this, mutex_op)?;
let mutex = mutex_get_data(this, mutex_op)?.clone();
if let Some(_old_locked_count) = this.mutex_unlock(&mutex.mutex_ref)? {
// The mutex was locked by the current thread.
@ -590,7 +599,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
// Reading the field also has the side-effect that we detect double-`destroy`
// since we make the field unint below.
let mutex = mutex_get_data(this, mutex_op)?;
let mutex = mutex_get_data(this, mutex_op)?.clone();
if this.mutex_is_locked(&mutex.mutex_ref) {
throw_ub_format!("destroyed a locked mutex");
@ -822,8 +831,8 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
) -> InterpResult<'tcx> {
let this = self.eval_context_mut();
let data = cond_get_data(this, cond_op)?;
let mutex_ref = mutex_get_data(this, mutex_op)?.mutex_ref;
let data = *cond_get_data(this, cond_op)?;
let mutex_ref = mutex_get_data(this, mutex_op)?.mutex_ref.clone();
this.condvar_wait(
data.id,
@ -846,8 +855,8 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
) -> InterpResult<'tcx> {
let this = self.eval_context_mut();
let data = cond_get_data(this, cond_op)?;
let mutex_ref = mutex_get_data(this, mutex_op)?.mutex_ref;
let data = *cond_get_data(this, cond_op)?;
let mutex_ref = mutex_get_data(this, mutex_op)?.mutex_ref.clone();
// Extract the timeout.
let duration = match this

View file

@ -20,10 +20,13 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> {
// Windows sync primitives are pointer sized.
// We only use the first 4 bytes for the id.
fn init_once_get_data(
&mut self,
fn init_once_get_data<'a>(
&'a mut self,
init_once_ptr: &OpTy<'tcx>,
) -> InterpResult<'tcx, WindowsInitOnce> {
) -> InterpResult<'tcx, &'a WindowsInitOnce>
where
'tcx: 'a,
{
let this = self.eval_context_mut();
let init_once = this.deref_pointer(init_once_ptr)?;