simplify synchronization object creation logic

This commit is contained in:
Ralf Jung 2024-08-17 17:38:27 +02:00
parent 0058752986
commit 7c811203cd
2 changed files with 28 additions and 103 deletions

View File

@ -26,27 +26,6 @@ pub(super) struct InitOnce {
clock: VClock, clock: VClock,
} }
impl<'tcx> EvalContextExtPriv<'tcx> for crate::MiriInterpCx<'tcx> {}
trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> {
/// Provides the closure with the next InitOnceId. Creates that InitOnce if the closure returns None,
/// otherwise returns the value from the closure.
#[inline]
fn init_once_get_or_create<F>(&mut self, existing: F) -> InterpResult<'tcx, InitOnceId>
where
F: FnOnce(&mut MiriInterpCx<'tcx>, InitOnceId) -> InterpResult<'tcx, Option<InitOnceId>>,
{
let this = self.eval_context_mut();
let next_index = this.machine.sync.init_onces.next_index();
if let Some(old) = existing(this, next_index)? {
Ok(old)
} else {
let new_index = this.machine.sync.init_onces.push(Default::default());
assert_eq!(next_index, new_index);
Ok(new_index)
}
}
}
impl<'tcx> EvalContextExt<'tcx> for crate::MiriInterpCx<'tcx> {} impl<'tcx> EvalContextExt<'tcx> for crate::MiriInterpCx<'tcx> {}
pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> { pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
fn init_once_get_or_create_id( fn init_once_get_or_create_id(
@ -56,9 +35,8 @@ fn init_once_get_or_create_id(
offset: u64, offset: u64,
) -> InterpResult<'tcx, InitOnceId> { ) -> InterpResult<'tcx, InitOnceId> {
let this = self.eval_context_mut(); let this = self.eval_context_mut();
this.init_once_get_or_create(|ecx, next_id| { this.get_or_create_id(lock_op, lock_layout, offset, |ecx| &mut ecx.machine.sync.init_onces)?
ecx.get_or_create_id(next_id, lock_op, lock_layout, offset) .ok_or_else(|| err_ub_format!("init_once has invalid ID").into())
})
} }
#[inline] #[inline]

View File

@ -164,25 +164,29 @@ pub struct SynchronizationObjects {
impl<'tcx> EvalContextExtPriv<'tcx> for crate::MiriInterpCx<'tcx> {} impl<'tcx> EvalContextExtPriv<'tcx> for crate::MiriInterpCx<'tcx> {}
pub(super) trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> { pub(super) trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> {
/// Lazily initialize the ID of this Miri sync structure. /// Lazily initialize the ID of this Miri sync structure.
/// ('0' indicates uninit.) /// If memory stores '0', that indicates uninit and we generate a new instance.
/// Returns `None` if memory stores a non-zero invalid ID.
///
/// `get_objs` must return the `IndexVec` that stores all the objects of this type.
#[inline] #[inline]
fn get_or_create_id<Id: SyncId>( fn get_or_create_id<Id: SyncId + Idx, T: Default>(
&mut self, &mut self,
next_id: Id,
lock_op: &OpTy<'tcx>, lock_op: &OpTy<'tcx>,
lock_layout: TyAndLayout<'tcx>, lock_layout: TyAndLayout<'tcx>,
offset: u64, offset: u64,
get_objs: impl for<'a> Fn(&'a mut MiriInterpCx<'tcx>) -> &'a mut IndexVec<Id, T>,
) -> InterpResult<'tcx, Option<Id>> { ) -> InterpResult<'tcx, Option<Id>> {
let this = self.eval_context_mut(); let this = self.eval_context_mut();
let value_place = let value_place =
this.deref_pointer_and_offset(lock_op, offset, lock_layout, this.machine.layouts.u32)?; this.deref_pointer_and_offset(lock_op, offset, lock_layout, this.machine.layouts.u32)?;
let next_index = get_objs(this).next_index();
// Since we are lazy, this update has to be atomic. // Since we are lazy, this update has to be atomic.
let (old, success) = this let (old, success) = this
.atomic_compare_exchange_scalar( .atomic_compare_exchange_scalar(
&value_place, &value_place,
&ImmTy::from_uint(0u32, this.machine.layouts.u32), &ImmTy::from_uint(0u32, this.machine.layouts.u32),
Scalar::from_u32(next_id.to_u32()), Scalar::from_u32(next_index.to_u32()),
AtomicRwOrd::Relaxed, // deliberately *no* synchronization AtomicRwOrd::Relaxed, // deliberately *no* synchronization
AtomicReadOrd::Relaxed, AtomicReadOrd::Relaxed,
false, false,
@ -190,76 +194,22 @@ fn get_or_create_id<Id: SyncId>(
.to_scalar_pair(); .to_scalar_pair();
Ok(if success.to_bool().expect("compare_exchange's second return value is a bool") { Ok(if success.to_bool().expect("compare_exchange's second return value is a bool") {
// Caller of the closure needs to allocate next_id // We set the in-memory ID to `next_index`, now also create this object in the machine
None // state.
let new_index = get_objs(this).push(T::default());
assert_eq!(next_index, new_index);
Some(new_index)
} else { } else {
Some(Id::from_u32(old.to_u32().expect("layout is u32"))) let id = Id::from_u32(old.to_u32().expect("layout is u32"));
if get_objs(this).get(id).is_none() {
// The in-memory ID is invalid.
None
} else {
Some(id)
}
}) })
} }
/// Provides the closure with the next MutexId. Creates that mutex if the closure returns None,
/// otherwise returns the value from the closure.
#[inline]
fn mutex_get_or_create<F>(&mut self, existing: F) -> InterpResult<'tcx, MutexId>
where
F: FnOnce(&mut MiriInterpCx<'tcx>, MutexId) -> InterpResult<'tcx, Option<MutexId>>,
{
let this = self.eval_context_mut();
let next_index = this.machine.sync.mutexes.next_index();
if let Some(old) = existing(this, next_index)? {
if this.machine.sync.mutexes.get(old).is_none() {
throw_ub_format!("mutex has invalid ID");
}
Ok(old)
} else {
let new_index = this.machine.sync.mutexes.push(Default::default());
assert_eq!(next_index, new_index);
Ok(new_index)
}
}
/// Provides the closure with the next RwLockId. Creates that RwLock if the closure returns None,
/// otherwise returns the value from the closure.
#[inline]
fn rwlock_get_or_create<F>(&mut self, existing: F) -> InterpResult<'tcx, RwLockId>
where
F: FnOnce(&mut MiriInterpCx<'tcx>, RwLockId) -> InterpResult<'tcx, Option<RwLockId>>,
{
let this = self.eval_context_mut();
let next_index = this.machine.sync.rwlocks.next_index();
if let Some(old) = existing(this, next_index)? {
if this.machine.sync.rwlocks.get(old).is_none() {
throw_ub_format!("rwlock has invalid ID");
}
Ok(old)
} else {
let new_index = this.machine.sync.rwlocks.push(Default::default());
assert_eq!(next_index, new_index);
Ok(new_index)
}
}
/// Provides the closure with the next CondvarId. Creates that Condvar if the closure returns None,
/// otherwise returns the value from the closure.
#[inline]
fn condvar_get_or_create<F>(&mut self, existing: F) -> InterpResult<'tcx, CondvarId>
where
F: FnOnce(&mut MiriInterpCx<'tcx>, CondvarId) -> InterpResult<'tcx, Option<CondvarId>>,
{
let this = self.eval_context_mut();
let next_index = this.machine.sync.condvars.next_index();
if let Some(old) = existing(this, next_index)? {
if this.machine.sync.condvars.get(old).is_none() {
throw_ub_format!("condvar has invalid ID");
}
Ok(old)
} else {
let new_index = this.machine.sync.condvars.push(Default::default());
assert_eq!(next_index, new_index);
Ok(new_index)
}
}
fn condvar_reacquire_mutex( fn condvar_reacquire_mutex(
&mut self, &mut self,
mutex: MutexId, mutex: MutexId,
@ -293,9 +243,8 @@ fn mutex_get_or_create_id(
offset: u64, offset: u64,
) -> InterpResult<'tcx, MutexId> { ) -> InterpResult<'tcx, MutexId> {
let this = self.eval_context_mut(); let this = self.eval_context_mut();
this.mutex_get_or_create(|ecx, next_id| { this.get_or_create_id(lock_op, lock_layout, offset, |ecx| &mut ecx.machine.sync.mutexes)?
ecx.get_or_create_id(next_id, lock_op, lock_layout, offset) .ok_or_else(|| err_ub_format!("mutex has invalid ID").into())
})
} }
fn rwlock_get_or_create_id( fn rwlock_get_or_create_id(
@ -305,9 +254,8 @@ fn rwlock_get_or_create_id(
offset: u64, offset: u64,
) -> InterpResult<'tcx, RwLockId> { ) -> InterpResult<'tcx, RwLockId> {
let this = self.eval_context_mut(); let this = self.eval_context_mut();
this.rwlock_get_or_create(|ecx, next_id| { this.get_or_create_id(lock_op, lock_layout, offset, |ecx| &mut ecx.machine.sync.rwlocks)?
ecx.get_or_create_id(next_id, lock_op, lock_layout, offset) .ok_or_else(|| err_ub_format!("rwlock has invalid ID").into())
})
} }
fn condvar_get_or_create_id( fn condvar_get_or_create_id(
@ -317,9 +265,8 @@ fn condvar_get_or_create_id(
offset: u64, offset: u64,
) -> InterpResult<'tcx, CondvarId> { ) -> InterpResult<'tcx, CondvarId> {
let this = self.eval_context_mut(); let this = self.eval_context_mut();
this.condvar_get_or_create(|ecx, next_id| { this.get_or_create_id(lock_op, lock_layout, offset, |ecx| &mut ecx.machine.sync.condvars)?
ecx.get_or_create_id(next_id, lock_op, lock_layout, offset) .ok_or_else(|| err_ub_format!("condvar has invalid ID").into())
})
} }
#[inline] #[inline]