add a macro to declare thread unblock callbacks
This commit is contained in:
parent
e6bb468b53
commit
2e89443b93
6 changed files with 257 additions and 250 deletions
|
|
@ -75,7 +75,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
|
|||
fn init_once_enqueue_and_block(
|
||||
&mut self,
|
||||
id: InitOnceId,
|
||||
callback: impl UnblockCallback<'mir, 'tcx> + 'tcx,
|
||||
callback: impl UnblockCallback<'tcx> + 'tcx,
|
||||
) {
|
||||
let this = self.eval_context_mut();
|
||||
let thread = this.active_thread();
|
||||
|
|
|
|||
|
|
@ -35,6 +35,10 @@ macro_rules! declare_id {
|
|||
}
|
||||
}
|
||||
|
||||
impl $crate::VisitProvenance for $name {
|
||||
fn visit_provenance(&self, _visit: &mut VisitWith<'_>) {}
|
||||
}
|
||||
|
||||
impl Idx for $name {
|
||||
fn new(idx: usize) -> Self {
|
||||
// We use 0 as a sentinel value (see the comment above) and,
|
||||
|
|
@ -258,6 +262,25 @@ pub(super) trait EvalContextExtPriv<'mir, 'tcx: 'mir>:
|
|||
Ok(new_index)
|
||||
}
|
||||
}
|
||||
|
||||
fn condvar_reacquire_mutex(
|
||||
&mut self,
|
||||
mutex: MutexId,
|
||||
retval: Scalar<Provenance>,
|
||||
dest: MPlaceTy<'tcx, Provenance>,
|
||||
) -> 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, retval, dest);
|
||||
} else {
|
||||
// We can have it right now!
|
||||
this.mutex_lock(mutex);
|
||||
// Don't forget to write the return value.
|
||||
this.write_scalar(retval, &dest)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
// Public interface to synchronization primitives. Please note that in most
|
||||
|
|
@ -384,29 +407,23 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
|
|||
assert!(this.mutex_is_locked(id), "queing on unlocked mutex");
|
||||
let thread = this.active_thread();
|
||||
this.machine.sync.mutexes[id].queue.push_back(thread);
|
||||
this.block_thread(BlockReason::Mutex(id), None, Callback { id, retval, dest });
|
||||
|
||||
struct Callback<'tcx> {
|
||||
id: MutexId,
|
||||
retval: Scalar<Provenance>,
|
||||
dest: MPlaceTy<'tcx, Provenance>,
|
||||
}
|
||||
impl<'tcx> VisitProvenance for Callback<'tcx> {
|
||||
fn visit_provenance(&self, visit: &mut VisitWith<'_>) {
|
||||
let Callback { id: _, retval, dest } = self;
|
||||
retval.visit_provenance(visit);
|
||||
dest.visit_provenance(visit);
|
||||
}
|
||||
}
|
||||
impl<'mir, 'tcx: 'mir> UnblockCallback<'mir, 'tcx> for Callback<'tcx> {
|
||||
fn unblock(self: Box<Self>, this: &mut MiriInterpCx<'mir, 'tcx>) -> InterpResult<'tcx> {
|
||||
assert!(!this.mutex_is_locked(self.id));
|
||||
this.mutex_lock(self.id);
|
||||
|
||||
this.write_scalar(self.retval, &self.dest)?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
this.block_thread(
|
||||
BlockReason::Mutex(id),
|
||||
None,
|
||||
callback!(
|
||||
@capture<'tcx> {
|
||||
id: MutexId,
|
||||
retval: Scalar<Provenance>,
|
||||
dest: MPlaceTy<'tcx, Provenance>,
|
||||
}
|
||||
@unblock = |this| {
|
||||
assert!(!this.mutex_is_locked(id));
|
||||
this.mutex_lock(id);
|
||||
this.write_scalar(retval, &dest)?;
|
||||
Ok(())
|
||||
}
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
#[inline]
|
||||
|
|
@ -500,27 +517,22 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
|
|||
let thread = this.active_thread();
|
||||
assert!(this.rwlock_is_write_locked(id), "read-queueing on not write locked rwlock");
|
||||
this.machine.sync.rwlocks[id].reader_queue.push_back(thread);
|
||||
this.block_thread(BlockReason::RwLock(id), None, Callback { id, retval, dest });
|
||||
|
||||
struct Callback<'tcx> {
|
||||
id: RwLockId,
|
||||
retval: Scalar<Provenance>,
|
||||
dest: MPlaceTy<'tcx, Provenance>,
|
||||
}
|
||||
impl<'tcx> VisitProvenance for Callback<'tcx> {
|
||||
fn visit_provenance(&self, visit: &mut VisitWith<'_>) {
|
||||
let Callback { id: _, retval, dest } = self;
|
||||
retval.visit_provenance(visit);
|
||||
dest.visit_provenance(visit);
|
||||
}
|
||||
}
|
||||
impl<'mir, 'tcx: 'mir> UnblockCallback<'mir, 'tcx> for Callback<'tcx> {
|
||||
fn unblock(self: Box<Self>, this: &mut MiriInterpCx<'mir, 'tcx>) -> InterpResult<'tcx> {
|
||||
this.rwlock_reader_lock(self.id);
|
||||
this.write_scalar(self.retval, &self.dest)?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
this.block_thread(
|
||||
BlockReason::RwLock(id),
|
||||
None,
|
||||
callback!(
|
||||
@capture<'tcx> {
|
||||
id: RwLockId,
|
||||
retval: Scalar<Provenance>,
|
||||
dest: MPlaceTy<'tcx, Provenance>,
|
||||
}
|
||||
@unblock = |this| {
|
||||
this.rwlock_reader_lock(id);
|
||||
this.write_scalar(retval, &dest)?;
|
||||
Ok(())
|
||||
}
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
/// Lock by setting the writer that owns the lock.
|
||||
|
|
@ -588,27 +600,22 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
|
|||
assert!(this.rwlock_is_locked(id), "write-queueing on unlocked rwlock");
|
||||
let thread = this.active_thread();
|
||||
this.machine.sync.rwlocks[id].writer_queue.push_back(thread);
|
||||
this.block_thread(BlockReason::RwLock(id), None, Callback { id, retval, dest });
|
||||
|
||||
struct Callback<'tcx> {
|
||||
id: RwLockId,
|
||||
retval: Scalar<Provenance>,
|
||||
dest: MPlaceTy<'tcx, Provenance>,
|
||||
}
|
||||
impl<'tcx> VisitProvenance for Callback<'tcx> {
|
||||
fn visit_provenance(&self, visit: &mut VisitWith<'_>) {
|
||||
let Callback { id: _, retval, dest } = self;
|
||||
retval.visit_provenance(visit);
|
||||
dest.visit_provenance(visit);
|
||||
}
|
||||
}
|
||||
impl<'mir, 'tcx: 'mir> UnblockCallback<'mir, 'tcx> for Callback<'tcx> {
|
||||
fn unblock(self: Box<Self>, this: &mut MiriInterpCx<'mir, 'tcx>) -> InterpResult<'tcx> {
|
||||
this.rwlock_writer_lock(self.id);
|
||||
this.write_scalar(self.retval, &self.dest)?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
this.block_thread(
|
||||
BlockReason::RwLock(id),
|
||||
None,
|
||||
callback!(
|
||||
@capture<'tcx> {
|
||||
id: RwLockId,
|
||||
retval: Scalar<Provenance>,
|
||||
dest: MPlaceTy<'tcx, Provenance>,
|
||||
}
|
||||
@unblock = |this| {
|
||||
this.rwlock_writer_lock(id);
|
||||
this.write_scalar(retval, &dest)?;
|
||||
Ok(())
|
||||
}
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
/// Is the conditional variable awaited?
|
||||
|
|
@ -648,71 +655,37 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
|
|||
this.block_thread(
|
||||
BlockReason::Condvar(condvar),
|
||||
timeout,
|
||||
Callback { condvar, mutex, retval_succ, retval_timeout, dest },
|
||||
callback!(
|
||||
@capture<'tcx> {
|
||||
condvar: CondvarId,
|
||||
mutex: MutexId,
|
||||
retval_succ: Scalar<Provenance>,
|
||||
retval_timeout: Scalar<Provenance>,
|
||||
dest: MPlaceTy<'tcx, Provenance>,
|
||||
}
|
||||
@unblock = |this| {
|
||||
// The condvar was signaled. Make sure we get the clock for that.
|
||||
if let Some(data_race) = &this.machine.data_race {
|
||||
data_race.acquire_clock(
|
||||
&this.machine.sync.condvars[condvar].clock,
|
||||
&this.machine.threads,
|
||||
);
|
||||
}
|
||||
// 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)
|
||||
}
|
||||
@timeout = |this| {
|
||||
// We have to remove the waiter from the queue again.
|
||||
let thread = this.active_thread();
|
||||
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)
|
||||
}
|
||||
),
|
||||
);
|
||||
return Ok(());
|
||||
|
||||
struct Callback<'tcx> {
|
||||
condvar: CondvarId,
|
||||
mutex: MutexId,
|
||||
retval_succ: Scalar<Provenance>,
|
||||
retval_timeout: Scalar<Provenance>,
|
||||
dest: MPlaceTy<'tcx, Provenance>,
|
||||
}
|
||||
impl<'tcx> VisitProvenance for Callback<'tcx> {
|
||||
fn visit_provenance(&self, visit: &mut VisitWith<'_>) {
|
||||
let Callback { condvar: _, mutex: _, retval_succ, retval_timeout, dest } = self;
|
||||
retval_succ.visit_provenance(visit);
|
||||
retval_timeout.visit_provenance(visit);
|
||||
dest.visit_provenance(visit);
|
||||
}
|
||||
}
|
||||
impl<'tcx, 'mir> Callback<'tcx> {
|
||||
#[allow(clippy::boxed_local)]
|
||||
fn reacquire_mutex(
|
||||
self: Box<Self>,
|
||||
this: &mut MiriInterpCx<'mir, 'tcx>,
|
||||
retval: Scalar<Provenance>,
|
||||
) -> InterpResult<'tcx> {
|
||||
if this.mutex_is_locked(self.mutex) {
|
||||
assert_ne!(this.mutex_get_owner(self.mutex), this.active_thread());
|
||||
this.mutex_enqueue_and_block(self.mutex, retval, self.dest);
|
||||
} else {
|
||||
// We can have it right now!
|
||||
this.mutex_lock(self.mutex);
|
||||
// Don't forget to write the return value.
|
||||
this.write_scalar(retval, &self.dest)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
impl<'mir, 'tcx: 'mir> UnblockCallback<'mir, 'tcx> for Callback<'tcx> {
|
||||
fn unblock(self: Box<Self>, this: &mut MiriInterpCx<'mir, 'tcx>) -> InterpResult<'tcx> {
|
||||
// The condvar was signaled. Make sure we get the clock for that.
|
||||
if let Some(data_race) = &this.machine.data_race {
|
||||
data_race.acquire_clock(
|
||||
&this.machine.sync.condvars[self.condvar].clock,
|
||||
&this.machine.threads,
|
||||
);
|
||||
}
|
||||
// Try to acquire the mutex.
|
||||
// The timeout only applies to the first wait (until the signal), not for mutex acquisition.
|
||||
let retval = self.retval_succ;
|
||||
self.reacquire_mutex(this, retval)
|
||||
}
|
||||
fn timeout(
|
||||
self: Box<Self>,
|
||||
this: &mut InterpCx<'mir, 'tcx, MiriMachine<'mir, 'tcx>>,
|
||||
) -> InterpResult<'tcx> {
|
||||
// We have to remove the waiter from the queue again.
|
||||
let thread = this.active_thread();
|
||||
let waiters = &mut this.machine.sync.condvars[self.condvar].waiters;
|
||||
waiters.retain(|waiter| *waiter != thread);
|
||||
// Now get back the lock.
|
||||
let retval = self.retval_timeout;
|
||||
self.reacquire_mutex(this, retval)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Wake up some thread (if there is any) sleeping on the conditional
|
||||
|
|
@ -755,50 +728,36 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
|
|||
this.block_thread(
|
||||
BlockReason::Futex { addr },
|
||||
timeout,
|
||||
Callback { addr, retval_succ, retval_timeout, dest, errno_timeout },
|
||||
);
|
||||
|
||||
struct Callback<'tcx> {
|
||||
addr: u64,
|
||||
retval_succ: Scalar<Provenance>,
|
||||
retval_timeout: Scalar<Provenance>,
|
||||
dest: MPlaceTy<'tcx, Provenance>,
|
||||
errno_timeout: Scalar<Provenance>,
|
||||
}
|
||||
impl<'tcx> VisitProvenance for Callback<'tcx> {
|
||||
fn visit_provenance(&self, visit: &mut VisitWith<'_>) {
|
||||
let Callback { addr: _, retval_succ, retval_timeout, dest, errno_timeout } = self;
|
||||
retval_succ.visit_provenance(visit);
|
||||
retval_timeout.visit_provenance(visit);
|
||||
dest.visit_provenance(visit);
|
||||
errno_timeout.visit_provenance(visit);
|
||||
}
|
||||
}
|
||||
impl<'mir, 'tcx: 'mir> UnblockCallback<'mir, 'tcx> for Callback<'tcx> {
|
||||
fn unblock(self: Box<Self>, this: &mut MiriInterpCx<'mir, 'tcx>) -> InterpResult<'tcx> {
|
||||
let futex = this.machine.sync.futexes.get(&self.addr).unwrap();
|
||||
// Acquire the clock of the futex.
|
||||
if let Some(data_race) = &this.machine.data_race {
|
||||
data_race.acquire_clock(&futex.clock, &this.machine.threads);
|
||||
callback!(
|
||||
@capture<'tcx> {
|
||||
addr: u64,
|
||||
retval_succ: Scalar<Provenance>,
|
||||
retval_timeout: Scalar<Provenance>,
|
||||
dest: MPlaceTy<'tcx, Provenance>,
|
||||
errno_timeout: Scalar<Provenance>,
|
||||
}
|
||||
// Write the return value.
|
||||
this.write_scalar(self.retval_succ, &self.dest)?;
|
||||
Ok(())
|
||||
}
|
||||
fn timeout(
|
||||
self: Box<Self>,
|
||||
this: &mut InterpCx<'mir, 'tcx, MiriMachine<'mir, 'tcx>>,
|
||||
) -> InterpResult<'tcx> {
|
||||
// Remove the waiter from the futex.
|
||||
let thread = this.active_thread();
|
||||
let futex = this.machine.sync.futexes.get_mut(&self.addr).unwrap();
|
||||
futex.waiters.retain(|waiter| waiter.thread != thread);
|
||||
// Set errno and write return value.
|
||||
this.set_last_error(self.errno_timeout)?;
|
||||
this.write_scalar(self.retval_timeout, &self.dest)?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
@unblock = |this| {
|
||||
let futex = this.machine.sync.futexes.get(&addr).unwrap();
|
||||
// Acquire the clock of the futex.
|
||||
if let Some(data_race) = &this.machine.data_race {
|
||||
data_race.acquire_clock(&futex.clock, &this.machine.threads);
|
||||
}
|
||||
// Write the return value.
|
||||
this.write_scalar(retval_succ, &dest)?;
|
||||
Ok(())
|
||||
}
|
||||
@timeout = |this| {
|
||||
// Remove the waiter from the futex.
|
||||
let thread = this.active_thread();
|
||||
let futex = this.machine.sync.futexes.get_mut(&addr).unwrap();
|
||||
futex.waiters.retain(|waiter| waiter.thread != thread);
|
||||
// Set errno and write return value.
|
||||
this.set_last_error(errno_timeout)?;
|
||||
this.write_scalar(retval_timeout, &dest)?;
|
||||
Ok(())
|
||||
}
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
/// Returns whether anything was woken.
|
||||
|
|
|
|||
|
|
@ -41,22 +41,75 @@ pub enum TlsAllocAction {
|
|||
}
|
||||
|
||||
/// Trait for callbacks that are executed when a thread gets unblocked.
|
||||
pub trait UnblockCallback<'mir, 'tcx>: VisitProvenance {
|
||||
fn unblock(
|
||||
pub trait UnblockCallback<'tcx>: VisitProvenance {
|
||||
/// Will be invoked when the thread was unblocked the "regular" way,
|
||||
/// i.e. whatever event it was blocking on has happened.
|
||||
fn unblock<'mir>(
|
||||
self: Box<Self>,
|
||||
ecx: &mut InterpCx<'mir, 'tcx, MiriMachine<'mir, 'tcx>>,
|
||||
) -> InterpResult<'tcx>;
|
||||
|
||||
fn timeout(
|
||||
/// Will be invoked when the timeout ellapsed without the event the
|
||||
/// thread was blocking on having occurred.
|
||||
fn timeout<'mir>(
|
||||
self: Box<Self>,
|
||||
_ecx: &mut InterpCx<'mir, 'tcx, MiriMachine<'mir, 'tcx>>,
|
||||
) -> InterpResult<'tcx> {
|
||||
unreachable!(
|
||||
"timeout on a thread that was blocked without a timeout (or someone forgot to overwrite this method)"
|
||||
)
|
||||
}
|
||||
) -> InterpResult<'tcx>;
|
||||
}
|
||||
type DynUnblockCallback<'tcx> = Box<dyn UnblockCallback<'tcx> + 'tcx>;
|
||||
|
||||
#[macro_export]
|
||||
macro_rules! callback {
|
||||
(
|
||||
@capture<$tcx:lifetime $(,)? $($lft:lifetime),*> { $($name:ident: $type:ty),* $(,)? }
|
||||
@unblock = |$this:ident| $unblock:block
|
||||
) => {
|
||||
callback!(
|
||||
@capture<$tcx, $($lft),*> { $($name: $type),+ }
|
||||
@unblock = |$this| $unblock
|
||||
@timeout = |_this| {
|
||||
unreachable!(
|
||||
"timeout on a thread that was blocked without a timeout (or someone forgot to overwrite this method)"
|
||||
)
|
||||
}
|
||||
)
|
||||
};
|
||||
(
|
||||
@capture<$tcx:lifetime $(,)? $($lft:lifetime),*> { $($name:ident: $type:ty),* $(,)? }
|
||||
@unblock = |$this:ident| $unblock:block
|
||||
@timeout = |$this_timeout:ident| $timeout:block
|
||||
) => {{
|
||||
struct Callback<$tcx, $($lft),*> {
|
||||
$($name: $type,)*
|
||||
_phantom: std::marker::PhantomData<&$tcx ()>,
|
||||
}
|
||||
|
||||
impl<$tcx, $($lft),*> VisitProvenance for Callback<$tcx, $($lft),*> {
|
||||
#[allow(unused_variables)]
|
||||
fn visit_provenance(&self, visit: &mut VisitWith<'_>) {
|
||||
$(
|
||||
self.$name.visit_provenance(visit);
|
||||
)*
|
||||
}
|
||||
}
|
||||
|
||||
impl<$tcx, $($lft),*> UnblockCallback<$tcx> for Callback<$tcx, $($lft),*> {
|
||||
fn unblock<'mir>(self: Box<Self>, $this: &mut MiriInterpCx<'mir, $tcx>) -> InterpResult<$tcx> {
|
||||
#[allow(unused_variables)]
|
||||
let Callback { $($name,)* _phantom } = *self;
|
||||
$unblock
|
||||
}
|
||||
|
||||
fn timeout<'mir>(self: Box<Self>, $this_timeout: &mut MiriInterpCx<'mir, $tcx>) -> InterpResult<$tcx> {
|
||||
#[allow(unused_variables)]
|
||||
let Callback { $($name,)* _phantom } = *self;
|
||||
$timeout
|
||||
}
|
||||
}
|
||||
|
||||
Callback { $($name,)* _phantom: std::marker::PhantomData }
|
||||
}}
|
||||
}
|
||||
type DynUnblockCallback<'mir, 'tcx> = Box<dyn UnblockCallback<'mir, 'tcx> + 'tcx>;
|
||||
|
||||
/// A thread identifier.
|
||||
#[derive(Clone, Copy, Debug, PartialOrd, Ord, PartialEq, Eq, Hash)]
|
||||
|
|
@ -127,21 +180,17 @@ pub enum BlockReason {
|
|||
}
|
||||
|
||||
/// The state of a thread.
|
||||
enum ThreadState<'mir, 'tcx> {
|
||||
enum ThreadState<'tcx> {
|
||||
/// The thread is enabled and can be executed.
|
||||
Enabled,
|
||||
/// The thread is blocked on something.
|
||||
Blocked {
|
||||
reason: BlockReason,
|
||||
timeout: Option<Timeout>,
|
||||
callback: DynUnblockCallback<'mir, 'tcx>,
|
||||
},
|
||||
Blocked { reason: BlockReason, timeout: Option<Timeout>, callback: DynUnblockCallback<'tcx> },
|
||||
/// The thread has terminated its execution. We do not delete terminated
|
||||
/// threads (FIXME: why?).
|
||||
Terminated,
|
||||
}
|
||||
|
||||
impl<'mir, 'tcx> std::fmt::Debug for ThreadState<'mir, 'tcx> {
|
||||
impl<'tcx> std::fmt::Debug for ThreadState<'tcx> {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
Self::Enabled => write!(f, "Enabled"),
|
||||
|
|
@ -152,7 +201,7 @@ impl<'mir, 'tcx> std::fmt::Debug for ThreadState<'mir, 'tcx> {
|
|||
}
|
||||
}
|
||||
|
||||
impl<'mir, 'tcx> ThreadState<'mir, 'tcx> {
|
||||
impl<'tcx> ThreadState<'tcx> {
|
||||
fn is_enabled(&self) -> bool {
|
||||
matches!(self, ThreadState::Enabled)
|
||||
}
|
||||
|
|
@ -180,7 +229,7 @@ enum ThreadJoinStatus {
|
|||
|
||||
/// A thread.
|
||||
pub struct Thread<'mir, 'tcx> {
|
||||
state: ThreadState<'mir, 'tcx>,
|
||||
state: ThreadState<'tcx>,
|
||||
|
||||
/// Name of the thread.
|
||||
thread_name: Option<Vec<u8>>,
|
||||
|
|
@ -582,26 +631,18 @@ impl<'mir, 'tcx: 'mir> ThreadManager<'mir, 'tcx> {
|
|||
self.block_thread(
|
||||
BlockReason::Join(joined_thread_id),
|
||||
None,
|
||||
Callback { joined_thread_id },
|
||||
);
|
||||
|
||||
struct Callback {
|
||||
joined_thread_id: ThreadId,
|
||||
}
|
||||
impl VisitProvenance for Callback {
|
||||
fn visit_provenance(&self, _visit: &mut VisitWith<'_>) {}
|
||||
}
|
||||
impl<'mir, 'tcx: 'mir> UnblockCallback<'mir, 'tcx> for Callback {
|
||||
fn unblock(
|
||||
self: Box<Self>,
|
||||
this: &mut MiriInterpCx<'mir, 'tcx>,
|
||||
) -> InterpResult<'tcx> {
|
||||
if let Some(data_race) = &mut this.machine.data_race {
|
||||
data_race.thread_joined(&this.machine.threads, self.joined_thread_id);
|
||||
callback!(
|
||||
@capture<'tcx> {
|
||||
joined_thread_id: ThreadId,
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
@unblock = |this| {
|
||||
if let Some(data_race) = &mut this.machine.data_race {
|
||||
data_race.thread_joined(&this.machine.threads, joined_thread_id);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
),
|
||||
);
|
||||
} else {
|
||||
// The thread has already terminated - establish happens-before
|
||||
if let Some(data_race) = data_race {
|
||||
|
|
@ -656,7 +697,7 @@ impl<'mir, 'tcx: 'mir> ThreadManager<'mir, 'tcx> {
|
|||
&mut self,
|
||||
reason: BlockReason,
|
||||
timeout: Option<Timeout>,
|
||||
callback: impl UnblockCallback<'mir, 'tcx> + 'tcx,
|
||||
callback: impl UnblockCallback<'tcx> + 'tcx,
|
||||
) {
|
||||
let state = &mut self.threads[self.active_thread].state;
|
||||
assert!(state.is_enabled());
|
||||
|
|
@ -963,7 +1004,7 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
|
|||
&mut self,
|
||||
reason: BlockReason,
|
||||
timeout: Option<Timeout>,
|
||||
callback: impl UnblockCallback<'mir, 'tcx> + 'tcx,
|
||||
callback: impl UnblockCallback<'tcx> + 'tcx,
|
||||
) {
|
||||
let this = self.eval_context_mut();
|
||||
if !this.machine.communicate() && matches!(timeout, Some(Timeout::RealTime(..))) {
|
||||
|
|
|
|||
|
|
@ -10,6 +10,18 @@ pub trait VisitProvenance {
|
|||
fn visit_provenance(&self, visit: &mut VisitWith<'_>);
|
||||
}
|
||||
|
||||
// Trivial impls for types that do not contain any provenance
|
||||
macro_rules! no_provenance {
|
||||
($($ty:ident)+) => {
|
||||
$(
|
||||
impl VisitProvenance for $ty {
|
||||
fn visit_provenance(&self, _visit: &mut VisitWith<'_>) {}
|
||||
}
|
||||
)+
|
||||
}
|
||||
}
|
||||
no_provenance!(i8 i16 i32 i64 isize u8 u16 u32 u64 usize ThreadId);
|
||||
|
||||
impl<T: VisitProvenance> VisitProvenance for Option<T> {
|
||||
fn visit_provenance(&self, visit: &mut VisitWith<'_>) {
|
||||
if let Some(x) = self {
|
||||
|
|
|
|||
|
|
@ -337,7 +337,15 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
|
|||
.unwrap_or_else(|| now.checked_add(Duration::from_secs(3600)).unwrap());
|
||||
let timeout_time = Timeout::Monotonic(timeout_time);
|
||||
|
||||
this.block_thread(BlockReason::Sleep, Some(timeout_time), SleepCallback);
|
||||
this.block_thread(
|
||||
BlockReason::Sleep,
|
||||
Some(timeout_time),
|
||||
callback!(
|
||||
@capture<'tcx> {}
|
||||
@unblock = |_this| { panic!("sleeping thread unblocked before time is up") }
|
||||
@timeout = |_this| { Ok(()) }
|
||||
),
|
||||
);
|
||||
Ok(0)
|
||||
}
|
||||
|
||||
|
|
@ -353,23 +361,15 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
|
|||
let timeout_time = this.machine.clock.now().checked_add(duration).unwrap();
|
||||
let timeout_time = Timeout::Monotonic(timeout_time);
|
||||
|
||||
this.block_thread(BlockReason::Sleep, Some(timeout_time), SleepCallback);
|
||||
this.block_thread(
|
||||
BlockReason::Sleep,
|
||||
Some(timeout_time),
|
||||
callback!(
|
||||
@capture<'tcx> {}
|
||||
@unblock = |_this| { panic!("sleeping thread unblocked before time is up") }
|
||||
@timeout = |_this| { Ok(()) }
|
||||
),
|
||||
);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
struct SleepCallback;
|
||||
impl VisitProvenance for SleepCallback {
|
||||
fn visit_provenance(&self, _visit: &mut VisitWith<'_>) {}
|
||||
}
|
||||
impl<'mir, 'tcx: 'mir> UnblockCallback<'mir, 'tcx> for SleepCallback {
|
||||
fn timeout(self: Box<Self>, _this: &mut MiriInterpCx<'mir, 'tcx>) -> InterpResult<'tcx> {
|
||||
Ok(())
|
||||
}
|
||||
fn unblock(
|
||||
self: Box<Self>,
|
||||
_this: &mut InterpCx<'mir, 'tcx, MiriMachine<'mir, 'tcx>>,
|
||||
) -> InterpResult<'tcx> {
|
||||
panic!("a sleeping thread should only ever be woken up via the timeout")
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -76,28 +76,23 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
|
|||
}
|
||||
|
||||
// We have to block, and then try again when we are woken up.
|
||||
this.init_once_enqueue_and_block(id, Callback { id, pending_place, dest: dest.clone() });
|
||||
let dest = dest.clone();
|
||||
this.init_once_enqueue_and_block(
|
||||
id,
|
||||
callback!(
|
||||
@capture<'tcx> {
|
||||
id: InitOnceId,
|
||||
pending_place: MPlaceTy<'tcx, Provenance>,
|
||||
dest: MPlaceTy<'tcx, Provenance>,
|
||||
}
|
||||
@unblock = |this| {
|
||||
let ret = this.init_once_try_begin(id, &pending_place, &dest)?;
|
||||
assert!(ret, "we were woken up but init_once_try_begin still failed");
|
||||
Ok(())
|
||||
}
|
||||
),
|
||||
);
|
||||
return Ok(());
|
||||
|
||||
struct Callback<'tcx> {
|
||||
id: InitOnceId,
|
||||
pending_place: MPlaceTy<'tcx, Provenance>,
|
||||
dest: MPlaceTy<'tcx, Provenance>,
|
||||
}
|
||||
impl<'tcx> VisitProvenance for Callback<'tcx> {
|
||||
fn visit_provenance(&self, visit: &mut VisitWith<'_>) {
|
||||
let Callback { id: _, dest, pending_place } = self;
|
||||
pending_place.visit_provenance(visit);
|
||||
dest.visit_provenance(visit);
|
||||
}
|
||||
}
|
||||
impl<'mir, 'tcx> UnblockCallback<'mir, 'tcx> for Callback<'tcx> {
|
||||
fn unblock(self: Box<Self>, this: &mut MiriInterpCx<'mir, 'tcx>) -> InterpResult<'tcx> {
|
||||
let ret = this.init_once_try_begin(self.id, &self.pending_place, &self.dest)?;
|
||||
assert!(ret, "we were woken up but init_once_try_begin still failed");
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn InitOnceComplete(
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue