diff --git a/src/tools/miri/src/concurrency/init_once.rs b/src/tools/miri/src/concurrency/init_once.rs index 1ee84273b58c7..3d6cf7ecca67b 100644 --- a/src/tools/miri/src/concurrency/init_once.rs +++ b/src/tools/miri/src/concurrency/init_once.rs @@ -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(); diff --git a/src/tools/miri/src/concurrency/sync.rs b/src/tools/miri/src/concurrency/sync.rs index df10e15fe1015..8925409ecc244 100644 --- a/src/tools/miri/src/concurrency/sync.rs +++ b/src/tools/miri/src/concurrency/sync.rs @@ -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, + 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, - 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, 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, + 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, - 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, 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, + 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, - 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, 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, + 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 }, - ); - return Ok(()); - - struct Callback<'tcx> { - condvar: CondvarId, - mutex: MutexId, - retval_succ: Scalar, - retval_timeout: Scalar, - 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, - this: &mut MiriInterpCx<'mir, 'tcx>, - retval: Scalar, - ) -> 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)?; + callback!( + @capture<'tcx> { + condvar: CondvarId, + mutex: MutexId, + retval_succ: Scalar, + retval_timeout: Scalar, + dest: MPlaceTy<'tcx, Provenance>, } - Ok(()) - } - } - impl<'mir, 'tcx: 'mir> UnblockCallback<'mir, 'tcx> for Callback<'tcx> { - fn unblock(self: Box, 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, - ); + @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) } - // 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, - 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) - } - } + @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(()); } /// 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, - retval_timeout: Scalar, - dest: MPlaceTy<'tcx, Provenance>, - errno_timeout: Scalar, - } - 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, 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, + retval_timeout: Scalar, + dest: MPlaceTy<'tcx, Provenance>, + errno_timeout: Scalar, } - // Write the return value. - this.write_scalar(self.retval_succ, &self.dest)?; - Ok(()) - } - fn timeout( - self: Box, - 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. diff --git a/src/tools/miri/src/concurrency/thread.rs b/src/tools/miri/src/concurrency/thread.rs index d663ff5a3be2a..c948383d597de 100644 --- a/src/tools/miri/src/concurrency/thread.rs +++ b/src/tools/miri/src/concurrency/thread.rs @@ -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, 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, _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 + '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, $this: &mut MiriInterpCx<'mir, $tcx>) -> InterpResult<$tcx> { + #[allow(unused_variables)] + let Callback { $($name,)* _phantom } = *self; + $unblock + } + + fn timeout<'mir>(self: Box, $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 + '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, - callback: DynUnblockCallback<'mir, 'tcx>, - }, + Blocked { reason: BlockReason, timeout: Option, 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>, @@ -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, - 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, - 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, - 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(..))) { diff --git a/src/tools/miri/src/provenance_gc.rs b/src/tools/miri/src/provenance_gc.rs index ecd614bf46725..5cc14e25ebdfe 100644 --- a/src/tools/miri/src/provenance_gc.rs +++ b/src/tools/miri/src/provenance_gc.rs @@ -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 VisitProvenance for Option { fn visit_provenance(&self, visit: &mut VisitWith<'_>) { if let Some(x) = self { diff --git a/src/tools/miri/src/shims/time.rs b/src/tools/miri/src/shims/time.rs index a99006f397090..5c43563fac424 100644 --- a/src/tools/miri/src/shims/time.rs +++ b/src/tools/miri/src/shims/time.rs @@ -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, _this: &mut MiriInterpCx<'mir, 'tcx>) -> InterpResult<'tcx> { - Ok(()) - } - fn unblock( - self: Box, - _this: &mut InterpCx<'mir, 'tcx, MiriMachine<'mir, 'tcx>>, - ) -> InterpResult<'tcx> { - panic!("a sleeping thread should only ever be woken up via the timeout") - } -} diff --git a/src/tools/miri/src/shims/windows/sync.rs b/src/tools/miri/src/shims/windows/sync.rs index 1e71fc92400d5..e4307c33bb40f 100644 --- a/src/tools/miri/src/shims/windows/sync.rs +++ b/src/tools/miri/src/shims/windows/sync.rs @@ -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, 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(