code reuse for sync ids

This commit is contained in:
DrMeepster 2022-10-18 18:24:16 -07:00 committed by Ralf Jung
parent 7ca6b175b4
commit 7cf32a7d47
4 changed files with 110 additions and 162 deletions

View file

@ -11,6 +11,11 @@ use super::thread::MachineCallback;
use super::vector_clock::VClock;
use crate::*;
pub trait SyncId {
fn from_u32(id: u32) -> Self;
fn to_u32_scalar(&self) -> Scalar<Provenance>;
}
/// We cannot use the `newtype_index!` macro because we have to use 0 as a
/// sentinel value meaning that the identifier is not assigned. This is because
/// the pthreads static initializers initialize memory with zeros (see the
@ -22,11 +27,14 @@ macro_rules! declare_id {
#[derive(Clone, Copy, Debug, PartialOrd, Ord, PartialEq, Eq, Hash)]
pub struct $name(NonZeroU32);
impl $name {
impl SyncId for $name {
// Panics if `id == 0`.
pub fn from_u32(id: u32) -> Self {
fn from_u32(id: u32) -> Self {
Self(NonZeroU32::new(id).unwrap())
}
fn to_u32_scalar(&self) -> Scalar<Provenance> {
Scalar::from_u32(self.0.get())
}
}
impl Idx for $name {
@ -166,7 +174,7 @@ impl<'mir, 'tcx> std::fmt::Debug for InitOnceWaiter<'mir, 'tcx> {
}
}
#[derive(Default, Debug, Copy, Clone, PartialEq, Eq,)]
#[derive(Default, Debug, Copy, Clone, PartialEq, Eq)]
/// The current status of a one time initialization.
pub enum InitOnceStatus {
#[default]
@ -212,6 +220,37 @@ impl<'mir, 'tcx> VisitTags for SynchronizationState<'mir, 'tcx> {
// Private extension trait for local helper methods
impl<'mir, 'tcx: 'mir> EvalContextExtPriv<'mir, 'tcx> for crate::MiriInterpCx<'mir, 'tcx> {}
trait EvalContextExtPriv<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
#[inline]
// Miri sync structures contain zero-initialized ids stored at some offset behind a pointer
fn get_or_create_id<Id: SyncId>(
&mut self,
next_id: Id,
lock_op: &OpTy<'tcx, Provenance>,
offset: u64,
) -> InterpResult<'tcx, Option<Id>> {
let this = self.eval_context_mut();
let value_place =
this.deref_operand_and_offset(lock_op, offset, this.machine.layouts.u32)?;
let (old, success) = this
.atomic_compare_exchange_scalar(
&value_place,
&ImmTy::from_uint(0u32, this.machine.layouts.u32),
next_id.to_u32_scalar(),
AtomicRwOrd::Relaxed,
AtomicReadOrd::Relaxed,
false,
)?
.to_scalar_pair();
Ok(if success.to_bool().expect("compare_exchange's second return value is a bool") {
// Caller of the closure needs to allocate next_id
None
} else {
Some(Id::from_u32(old.to_u32().expect("layout is u32")))
})
}
/// Take a reader out of the queue waiting for the lock.
/// Returns `true` if some thread got the rwlock.
#[inline]
@ -261,6 +300,48 @@ trait EvalContextExtPriv<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
// situations.
impl<'mir, 'tcx: 'mir> EvalContextExt<'mir, 'tcx> for crate::MiriInterpCx<'mir, 'tcx> {}
pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
fn mutex_get_or_create_id(
&mut self,
lock_op: &OpTy<'tcx, Provenance>,
offset: u64,
) -> InterpResult<'tcx, MutexId> {
let this = self.eval_context_mut();
this.mutex_get_or_create(|ecx, next_id| Ok(ecx.get_or_create_id(next_id, lock_op, offset)?))
}
fn rwlock_get_or_create_id(
&mut self,
lock_op: &OpTy<'tcx, Provenance>,
offset: u64,
) -> InterpResult<'tcx, RwLockId> {
let this = self.eval_context_mut();
this.rwlock_get_or_create(
|ecx, next_id| Ok(ecx.get_or_create_id(next_id, lock_op, offset)?),
)
}
fn condvar_get_or_create_id(
&mut self,
lock_op: &OpTy<'tcx, Provenance>,
offset: u64,
) -> InterpResult<'tcx, CondvarId> {
let this = self.eval_context_mut();
this.condvar_get_or_create(|ecx, next_id| {
Ok(ecx.get_or_create_id(next_id, lock_op, offset)?)
})
}
fn init_once_get_or_create_id(
&mut self,
lock_op: &OpTy<'tcx, Provenance>,
offset: u64,
) -> InterpResult<'tcx, InitOnceId> {
let this = self.eval_context_mut();
this.init_once_get_or_create(|ecx, next_id| {
Ok(ecx.get_or_create_id(next_id, lock_op, offset)?)
})
}
#[inline]
/// Create state for a new mutex.
fn mutex_create(&mut self) -> MutexId {

View file

@ -87,7 +87,7 @@ pub use crate::concurrency::{
AtomicFenceOrd, AtomicReadOrd, AtomicRwOrd, AtomicWriteOrd,
EvalContextExt as DataRaceEvalContextExt,
},
sync::{CondvarId, EvalContextExt as SyncEvalContextExt, InitOnceId, MutexId, RwLockId},
sync::{CondvarId, EvalContextExt as SyncEvalContextExt, InitOnceId, MutexId, RwLockId, SyncId},
thread::{
EvalContextExt as ThreadsEvalContextExt, SchedulingAction, ThreadId, ThreadManager,
ThreadState, Time,

View file

@ -108,33 +108,6 @@ fn mutex_set_id<'mir, 'tcx: 'mir>(
)
}
fn mutex_get_or_create_id<'mir, 'tcx: 'mir>(
ecx: &mut MiriInterpCx<'mir, 'tcx>,
mutex_op: &OpTy<'tcx, Provenance>,
) -> InterpResult<'tcx, MutexId> {
let value_place = ecx.deref_operand_and_offset(mutex_op, 4, ecx.machine.layouts.u32)?;
ecx.mutex_get_or_create(|ecx, next_id| {
let (old, success) = ecx
.atomic_compare_exchange_scalar(
&value_place,
&ImmTy::from_uint(0u32, ecx.machine.layouts.u32),
next_id.to_u32_scalar(),
AtomicRwOrd::Relaxed,
AtomicReadOrd::Relaxed,
false,
)?
.to_scalar_pair();
Ok(if success.to_bool().expect("compare_exchange's second return value is a bool") {
// Caller of the closure needs to allocate next_id
None
} else {
Some(MutexId::from_u32(old.to_u32().expect("layout is u32")))
})
})
}
// pthread_rwlock_t is between 32 and 56 bytes, depending on the platform.
// Our chosen memory layout for the emulated rwlock (does not have to match the platform layout!):
@ -149,33 +122,6 @@ fn rwlock_get_id<'mir, 'tcx: 'mir>(
ecx.read_scalar_at_offset_atomic(rwlock_op, 4, ecx.machine.layouts.u32, AtomicReadOrd::Relaxed)
}
fn rwlock_get_or_create_id<'mir, 'tcx: 'mir>(
ecx: &mut MiriInterpCx<'mir, 'tcx>,
rwlock_op: &OpTy<'tcx, Provenance>,
) -> InterpResult<'tcx, RwLockId> {
let value_place = ecx.deref_operand_and_offset(rwlock_op, 4, ecx.machine.layouts.u32)?;
ecx.rwlock_get_or_create(|ecx, next_id| {
let (old, success) = ecx
.atomic_compare_exchange_scalar(
&value_place,
&ImmTy::from_uint(0u32, ecx.machine.layouts.u32),
next_id.to_u32_scalar(),
AtomicRwOrd::Relaxed,
AtomicReadOrd::Relaxed,
false,
)?
.to_scalar_pair();
Ok(if success.to_bool().expect("compare_exchange's second return value is a bool") {
// Caller of the closure needs to allocate next_id
None
} else {
Some(RwLockId::from_u32(old.to_u32().expect("layout is u32")))
})
})
}
// pthread_condattr_t
// Our chosen memory layout for emulation (does not have to match the platform layout!):
@ -232,33 +178,6 @@ fn cond_set_id<'mir, 'tcx: 'mir>(
)
}
fn cond_get_or_create_id<'mir, 'tcx: 'mir>(
ecx: &mut MiriInterpCx<'mir, 'tcx>,
cond_op: &OpTy<'tcx, Provenance>,
) -> InterpResult<'tcx, CondvarId> {
let value_place = ecx.deref_operand_and_offset(cond_op, 4, ecx.machine.layouts.u32)?;
ecx.condvar_get_or_create(|ecx, next_id| {
let (old, success) = ecx
.atomic_compare_exchange_scalar(
&value_place,
&ImmTy::from_uint(0u32, ecx.machine.layouts.u32),
next_id.to_u32_scalar(),
AtomicRwOrd::Relaxed,
AtomicReadOrd::Relaxed,
false,
)?
.to_scalar_pair();
Ok(if success.to_bool().expect("compare_exchange's second return value is a bool") {
// Caller of the closure needs to allocate next_id
None
} else {
Some(CondvarId::from_u32(old.to_u32().expect("layout is u32")))
})
})
}
fn cond_get_clock_id<'mir, 'tcx: 'mir>(
ecx: &MiriInterpCx<'mir, 'tcx>,
cond_op: &OpTy<'tcx, Provenance>,
@ -435,7 +354,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
let this = self.eval_context_mut();
let kind = mutex_get_kind(this, mutex_op)?;
let id = mutex_get_or_create_id(this, mutex_op)?;
let id = this.mutex_get_or_create_id(mutex_op, 4)?;
let active_thread = this.get_active_thread();
if this.mutex_is_locked(id) {
@ -475,7 +394,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
let this = self.eval_context_mut();
let kind = mutex_get_kind(this, mutex_op)?;
let id = mutex_get_or_create_id(this, mutex_op)?;
let id = this.mutex_get_or_create_id(mutex_op, 4)?;
let active_thread = this.get_active_thread();
if this.mutex_is_locked(id) {
@ -511,7 +430,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
let this = self.eval_context_mut();
let kind = mutex_get_kind(this, mutex_op)?;
let id = mutex_get_or_create_id(this, mutex_op)?;
let id = this.mutex_get_or_create_id(mutex_op, 4)?;
let active_thread = this.get_active_thread();
if let Some(_old_locked_count) = this.mutex_unlock(id, active_thread) {
@ -545,7 +464,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
) -> InterpResult<'tcx, i32> {
let this = self.eval_context_mut();
let id = mutex_get_or_create_id(this, mutex_op)?;
let id = this.mutex_get_or_create_id(mutex_op, 4)?;
if this.mutex_is_locked(id) {
throw_ub_format!("destroyed a locked mutex");
@ -568,7 +487,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
) -> InterpResult<'tcx, i32> {
let this = self.eval_context_mut();
let id = rwlock_get_or_create_id(this, rwlock_op)?;
let id = this.rwlock_get_or_create_id(rwlock_op, 4)?;
let active_thread = this.get_active_thread();
if this.rwlock_is_write_locked(id) {
@ -586,7 +505,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
) -> InterpResult<'tcx, i32> {
let this = self.eval_context_mut();
let id = rwlock_get_or_create_id(this, rwlock_op)?;
let id = this.rwlock_get_or_create_id(rwlock_op, 4)?;
let active_thread = this.get_active_thread();
if this.rwlock_is_write_locked(id) {
@ -603,7 +522,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
) -> InterpResult<'tcx, i32> {
let this = self.eval_context_mut();
let id = rwlock_get_or_create_id(this, rwlock_op)?;
let id = this.rwlock_get_or_create_id(rwlock_op, 4)?;
let active_thread = this.get_active_thread();
if this.rwlock_is_locked(id) {
@ -633,7 +552,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
) -> InterpResult<'tcx, i32> {
let this = self.eval_context_mut();
let id = rwlock_get_or_create_id(this, rwlock_op)?;
let id = this.rwlock_get_or_create_id(rwlock_op, 4)?;
let active_thread = this.get_active_thread();
if this.rwlock_is_locked(id) {
@ -650,7 +569,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
) -> InterpResult<'tcx, i32> {
let this = self.eval_context_mut();
let id = rwlock_get_or_create_id(this, rwlock_op)?;
let id = this.rwlock_get_or_create_id(rwlock_op, 4)?;
let active_thread = this.get_active_thread();
#[allow(clippy::if_same_then_else)]
@ -669,7 +588,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
) -> InterpResult<'tcx, i32> {
let this = self.eval_context_mut();
let id = rwlock_get_or_create_id(this, rwlock_op)?;
let id = this.rwlock_get_or_create_id(rwlock_op, 4)?;
if this.rwlock_is_locked(id) {
throw_ub_format!("destroyed a locked rwlock");
@ -772,7 +691,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
fn pthread_cond_signal(&mut self, cond_op: &OpTy<'tcx, Provenance>) -> InterpResult<'tcx, i32> {
let this = self.eval_context_mut();
let id = cond_get_or_create_id(this, cond_op)?;
let id = this.condvar_get_or_create_id(cond_op, 4)?;
if let Some((thread, mutex)) = this.condvar_signal(id) {
post_cond_signal(this, thread, mutex)?;
}
@ -785,7 +704,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
cond_op: &OpTy<'tcx, Provenance>,
) -> InterpResult<'tcx, i32> {
let this = self.eval_context_mut();
let id = cond_get_or_create_id(this, cond_op)?;
let id = this.condvar_get_or_create_id(cond_op, 4)?;
while let Some((thread, mutex)) = this.condvar_signal(id) {
post_cond_signal(this, thread, mutex)?;
@ -801,8 +720,8 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
) -> InterpResult<'tcx, i32> {
let this = self.eval_context_mut();
let id = cond_get_or_create_id(this, cond_op)?;
let mutex_id = mutex_get_or_create_id(this, mutex_op)?;
let id = this.condvar_get_or_create_id(cond_op, 4)?;
let mutex_id = this.mutex_get_or_create_id(mutex_op, 4)?;
let active_thread = this.get_active_thread();
release_cond_mutex_and_block(this, active_thread, mutex_id)?;
@ -822,8 +741,8 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
this.check_no_isolation("`pthread_cond_timedwait`")?;
let id = cond_get_or_create_id(this, cond_op)?;
let mutex_id = mutex_get_or_create_id(this, mutex_op)?;
let id = this.condvar_get_or_create_id(cond_op, 4)?;
let mutex_id = this.mutex_get_or_create_id(mutex_op, 4)?;
let active_thread = this.get_active_thread();
// Extract the timeout.
@ -899,7 +818,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
) -> InterpResult<'tcx, i32> {
let this = self.eval_context_mut();
let id = cond_get_or_create_id(this, cond_op)?;
let id = this.condvar_get_or_create_id(cond_op, 4)?;
if this.condvar_is_awaited(id) {
throw_ub_format!("destroying an awaited conditional variable");
}

View file

@ -2,65 +2,13 @@ use crate::concurrency::sync::InitOnceStatus;
use crate::concurrency::thread::MachineCallback;
use crate::*;
impl<'mir, 'tcx> EvalContextExtPriv<'mir, 'tcx> for crate::MiriInterpCx<'mir, 'tcx> {}
trait EvalContextExtPriv<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
// These synchronization structures are pointer-sized pieces of data, initialized to 0.
// We use the first 4 bytes to store the id.
fn get_or_create_id(
&mut self,
next_id: Scalar<Provenance>,
lock_op: &OpTy<'tcx, Provenance>,
) -> InterpResult<'tcx, Option<u32>> {
let this = self.eval_context_mut();
let value_place = this.deref_operand_and_offset(lock_op, 0, this.machine.layouts.u32)?;
let (old, success) = this
.atomic_compare_exchange_scalar(
&value_place,
&ImmTy::from_uint(0u32, this.machine.layouts.u32),
next_id,
AtomicRwOrd::Relaxed,
AtomicReadOrd::Relaxed,
false,
)?
.to_scalar_pair();
Ok(if success.to_bool().expect("compare_exchange's second return value is a bool") {
// Caller of the closure needs to allocate next_id
None
} else {
Some(old.to_u32().expect("layout is u32"))
})
}
fn srwlock_get_or_create_id(
&mut self,
lock_op: &OpTy<'tcx, Provenance>,
) -> InterpResult<'tcx, RwLockId> {
let this = self.eval_context_mut();
this.rwlock_get_or_create(|ecx, next_id| {
Ok(ecx.get_or_create_id(next_id.to_u32_scalar(), lock_op)?.map(RwLockId::from_u32))
})
}
fn init_once_get_or_create_id(
&mut self,
lock_op: &OpTy<'tcx, Provenance>,
) -> InterpResult<'tcx, InitOnceId> {
let this = self.eval_context_mut();
this.init_once_get_or_create(|ecx, next_id| {
Ok(ecx.get_or_create_id(next_id.to_u32_scalar(), lock_op)?.map(InitOnceId::from_u32))
})
}
}
impl<'mir, 'tcx> EvalContextExt<'mir, 'tcx> for crate::MiriInterpCx<'mir, 'tcx> {}
#[allow(non_snake_case)]
pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
fn AcquireSRWLockExclusive(&mut self, lock_op: &OpTy<'tcx, Provenance>) -> InterpResult<'tcx> {
let this = self.eval_context_mut();
let id = this.srwlock_get_or_create_id(lock_op)?;
let id = this.rwlock_get_or_create_id(lock_op, 0)?;
let active_thread = this.get_active_thread();
if this.rwlock_is_locked(id) {
@ -84,7 +32,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
lock_op: &OpTy<'tcx, Provenance>,
) -> InterpResult<'tcx, Scalar<Provenance>> {
let this = self.eval_context_mut();
let id = this.srwlock_get_or_create_id(lock_op)?;
let id = this.rwlock_get_or_create_id(lock_op, 0)?;
let active_thread = this.get_active_thread();
if this.rwlock_is_locked(id) {
@ -98,7 +46,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
fn ReleaseSRWLockExclusive(&mut self, lock_op: &OpTy<'tcx, Provenance>) -> InterpResult<'tcx> {
let this = self.eval_context_mut();
let id = this.srwlock_get_or_create_id(lock_op)?;
let id = this.rwlock_get_or_create_id(lock_op, 0)?;
let active_thread = this.get_active_thread();
if !this.rwlock_writer_unlock(id, active_thread) {
@ -113,7 +61,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
fn AcquireSRWLockShared(&mut self, lock_op: &OpTy<'tcx, Provenance>) -> InterpResult<'tcx> {
let this = self.eval_context_mut();
let id = this.srwlock_get_or_create_id(lock_op)?;
let id = this.rwlock_get_or_create_id(lock_op, 0)?;
let active_thread = this.get_active_thread();
if this.rwlock_is_write_locked(id) {
@ -130,7 +78,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
lock_op: &OpTy<'tcx, Provenance>,
) -> InterpResult<'tcx, Scalar<Provenance>> {
let this = self.eval_context_mut();
let id = this.srwlock_get_or_create_id(lock_op)?;
let id = this.rwlock_get_or_create_id(lock_op, 0)?;
let active_thread = this.get_active_thread();
if this.rwlock_is_write_locked(id) {
@ -143,7 +91,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
fn ReleaseSRWLockShared(&mut self, lock_op: &OpTy<'tcx, Provenance>) -> InterpResult<'tcx> {
let this = self.eval_context_mut();
let id = this.srwlock_get_or_create_id(lock_op)?;
let id = this.rwlock_get_or_create_id(lock_op, 0)?;
let active_thread = this.get_active_thread();
if !this.rwlock_reader_unlock(id, active_thread) {
@ -166,7 +114,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
let this = self.eval_context_mut();
let active_thread = this.get_active_thread();
let id = this.init_once_get_or_create_id(init_once_op)?;
let id = this.init_once_get_or_create_id(init_once_op, 0)?;
let flags = this.read_scalar(flags_op)?.to_u32()?;
let pending_place = this.deref_operand(pending_op)?.into();
let context = this.read_pointer(context_op)?;
@ -232,7 +180,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
) -> InterpResult<'tcx, Scalar<Provenance>> {
let this = self.eval_context_mut();
let id = this.init_once_get_or_create_id(init_once_op)?;
let id = this.init_once_get_or_create_id(init_once_op, 0)?;
let flags = this.read_scalar(flags_op)?.to_u32()?;
let context = this.read_pointer(context_op)?;