add a macro to declare thread unblock callbacks · rust-lang/rust@2e89443
@@ -35,6 +35,10 @@ macro_rules! declare_id {
3535}
3636}
373738+impl $crate::VisitProvenance for $name {
39+fn visit_provenance(&self, _visit: &mut VisitWith<'_>) {}
40+}
41+3842impl Idx for $name {
3943fn new(idx: usize) -> Self {
4044// We use 0 as a sentinel value (see the comment above) and,
@@ -258,6 +262,25 @@ pub(super) trait EvalContextExtPriv<'mir, 'tcx: 'mir>:
258262Ok(new_index)
259263}
260264}
265+266+fn condvar_reacquire_mutex(
267+&mut self,
268+mutex: MutexId,
269+retval: Scalar<Provenance>,
270+dest: MPlaceTy<'tcx, Provenance>,
271+) -> InterpResult<'tcx> {
272+let this = self.eval_context_mut();
273+if this.mutex_is_locked(mutex) {
274+assert_ne!(this.mutex_get_owner(mutex), this.active_thread());
275+ this.mutex_enqueue_and_block(mutex, retval, dest);
276+} else {
277+// We can have it right now!
278+ this.mutex_lock(mutex);
279+// Don't forget to write the return value.
280+ this.write_scalar(retval, &dest)?;
281+}
282+Ok(())
283+}
261284}
262285263286// Public interface to synchronization primitives. Please note that in most
@@ -384,29 +407,23 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
384407assert!(this.mutex_is_locked(id), "queing on unlocked mutex");
385408let thread = this.active_thread();
386409 this.machine.sync.mutexes[id].queue.push_back(thread);
387- this.block_thread(BlockReason::Mutex(id), None, Callback { id, retval, dest });
388-389-struct Callback<'tcx> {
390-id: MutexId,
391-retval: Scalar<Provenance>,
392-dest: MPlaceTy<'tcx, Provenance>,
393-}
394-impl<'tcx> VisitProvenance for Callback<'tcx> {
395-fn visit_provenance(&self, visit: &mut VisitWith<'_>) {
396-let Callback { id: _, retval, dest } = self;
397- retval.visit_provenance(visit);
398- dest.visit_provenance(visit);
399-}
400-}
401-impl<'mir, 'tcx: 'mir> UnblockCallback<'mir, 'tcx> for Callback<'tcx> {
402-fn unblock(self: Box<Self>, this: &mut MiriInterpCx<'mir, 'tcx>) -> InterpResult<'tcx> {
403-assert!(!this.mutex_is_locked(self.id));
404- this.mutex_lock(self.id);
405-406- this.write_scalar(self.retval, &self.dest)?;
407-Ok(())
408-}
409-}
410+ this.block_thread(
411+BlockReason::Mutex(id),
412+None,
413+callback!(
414+ @capture<'tcx> {
415+ id: MutexId,
416+ retval: Scalar<Provenance>,
417+ dest: MPlaceTy<'tcx, Provenance>,
418+}
419+ @unblock = |this| {
420+ assert!(!this.mutex_is_locked(id));
421+ this.mutex_lock(id);
422+ this.write_scalar(retval, &dest)?;
423+Ok(())
424+}
425+),
426+);
410427}
411428412429#[inline]
@@ -500,27 +517,22 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
500517let thread = this.active_thread();
501518assert!(this.rwlock_is_write_locked(id), "read-queueing on not write locked rwlock");
502519 this.machine.sync.rwlocks[id].reader_queue.push_back(thread);
503- this.block_thread(BlockReason::RwLock(id), None, Callback { id, retval, dest });
504-505-struct Callback<'tcx> {
506-id: RwLockId,
507-retval: Scalar<Provenance>,
508-dest: MPlaceTy<'tcx, Provenance>,
509-}
510-impl<'tcx> VisitProvenance for Callback<'tcx> {
511-fn visit_provenance(&self, visit: &mut VisitWith<'_>) {
512-let Callback { id: _, retval, dest } = self;
513- retval.visit_provenance(visit);
514- dest.visit_provenance(visit);
515-}
516-}
517-impl<'mir, 'tcx: 'mir> UnblockCallback<'mir, 'tcx> for Callback<'tcx> {
518-fn unblock(self: Box<Self>, this: &mut MiriInterpCx<'mir, 'tcx>) -> InterpResult<'tcx> {
519- this.rwlock_reader_lock(self.id);
520- this.write_scalar(self.retval, &self.dest)?;
521-Ok(())
522-}
523-}
520+ this.block_thread(
521+BlockReason::RwLock(id),
522+None,
523+callback!(
524+ @capture<'tcx> {
525+ id: RwLockId,
526+ retval: Scalar<Provenance>,
527+ dest: MPlaceTy<'tcx, Provenance>,
528+}
529+ @unblock = |this| {
530+ this.rwlock_reader_lock(id);
531+ this.write_scalar(retval, &dest)?;
532+Ok(())
533+}
534+),
535+);
524536}
525537526538/// Lock by setting the writer that owns the lock.
@@ -588,27 +600,22 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
588600assert!(this.rwlock_is_locked(id), "write-queueing on unlocked rwlock");
589601let thread = this.active_thread();
590602 this.machine.sync.rwlocks[id].writer_queue.push_back(thread);
591- this.block_thread(BlockReason::RwLock(id), None, Callback { id, retval, dest });
592-593-struct Callback<'tcx> {
594-id: RwLockId,
595-retval: Scalar<Provenance>,
596-dest: MPlaceTy<'tcx, Provenance>,
597-}
598-impl<'tcx> VisitProvenance for Callback<'tcx> {
599-fn visit_provenance(&self, visit: &mut VisitWith<'_>) {
600-let Callback { id: _, retval, dest } = self;
601- retval.visit_provenance(visit);
602- dest.visit_provenance(visit);
603-}
604-}
605-impl<'mir, 'tcx: 'mir> UnblockCallback<'mir, 'tcx> for Callback<'tcx> {
606-fn unblock(self: Box<Self>, this: &mut MiriInterpCx<'mir, 'tcx>) -> InterpResult<'tcx> {
607- this.rwlock_writer_lock(self.id);
608- this.write_scalar(self.retval, &self.dest)?;
609-Ok(())
610-}
611-}
603+ this.block_thread(
604+BlockReason::RwLock(id),
605+None,
606+callback!(
607+ @capture<'tcx> {
608+ id: RwLockId,
609+ retval: Scalar<Provenance>,
610+ dest: MPlaceTy<'tcx, Provenance>,
611+}
612+ @unblock = |this| {
613+ this.rwlock_writer_lock(id);
614+ this.write_scalar(retval, &dest)?;
615+Ok(())
616+}
617+),
618+);
612619}
613620614621/// Is the conditional variable awaited?
@@ -648,71 +655,37 @@ pub trait EvalContextExt<'mir, 'tcx: 'mir>: crate::MiriInterpCxExt<'mir, 'tcx> {
648655 this.block_thread(
649656BlockReason::Condvar(condvar),
650657 timeout,
651-Callback { condvar, mutex, retval_succ, retval_timeout, dest },
652-);
653-return Ok(());
654-655-struct Callback<'tcx> {
656-condvar: CondvarId,
657-mutex: MutexId,
658-retval_succ: Scalar<Provenance>,
659-retval_timeout: Scalar<Provenance>,
660-dest: MPlaceTy<'tcx, Provenance>,
661-}
662-impl<'tcx> VisitProvenance for Callback<'tcx> {
663-fn visit_provenance(&self, visit: &mut VisitWith<'_>) {
664-let Callback { condvar: _, mutex: _, retval_succ, retval_timeout, dest } = self;
665- retval_succ.visit_provenance(visit);
666- retval_timeout.visit_provenance(visit);
667- dest.visit_provenance(visit);
668-}
669-}
670-impl<'tcx, 'mir> Callback<'tcx> {
671-#[allow(clippy::boxed_local)]
672-fn reacquire_mutex(
673-self: Box<Self>,
674-this: &mut MiriInterpCx<'mir, 'tcx>,
675-retval: Scalar<Provenance>,
676-) -> InterpResult<'tcx> {
677-if this.mutex_is_locked(self.mutex) {
678-assert_ne!(this.mutex_get_owner(self.mutex), this.active_thread());
679- this.mutex_enqueue_and_block(self.mutex, retval, self.dest);
680-} else {
681-// We can have it right now!
682- this.mutex_lock(self.mutex);
683-// Don't forget to write the return value.
684- this.write_scalar(retval, &self.dest)?;
658+callback!(
659+ @capture<'tcx> {
660+ condvar: CondvarId,
661+ mutex: MutexId,
662+ retval_succ: Scalar<Provenance>,
663+ retval_timeout: Scalar<Provenance>,
664+ dest: MPlaceTy<'tcx, Provenance>,
685665}
686-Ok(())
687-}
688-}
689-impl<'mir, 'tcx: 'mir> UnblockCallback<'mir, 'tcx> for Callback<'tcx> {
690-fn unblock(self: Box<Self>, this: &mut MiriInterpCx<'mir, 'tcx>) -> InterpResult<'tcx> {
691-// The condvar was signaled. Make sure we get the clock for that.
692-if let Some(data_race) = &this.machine.data_race {
693-data_race.acquire_clock(
694- &this.machine.sync.condvars[self.condvar].clock,
695- &this.machine.threads,
696-);
666+@unblock = |this| {
667+ // The condvar was signaled. Make sure we get the clock for that.
668+ if let Some(data_race) = &this.machine.data_race {
669+ data_race.acquire_clock(
670+ &this.machine.sync.condvars[condvar].clock,
671+ &this.machine.threads,
672+ );
673+}
674+// Try to acquire the mutex.
675+// The timeout only applies to the first wait (until the signal), not for mutex acquisition.
676+this.condvar_reacquire_mutex(mutex, retval_succ, dest)
697677}
698-// Try to acquire the mutex.
699-// The timeout only applies to the first wait (until the signal), not for mutex acquisition.
700-let retval = self.retval_succ;
701-self.reacquire_mutex(this, retval)
702-}
703-fn timeout(
704-self: Box<Self>,
705-this: &mut InterpCx<'mir, 'tcx, MiriMachine<'mir, 'tcx>>,
706-) -> InterpResult<'tcx> {
707-// We have to remove the waiter from the queue again.
708-let thread = this.active_thread();
709-let waiters = &mut this.machine.sync.condvars[self.condvar].waiters;
710- waiters.retain(|waiter| *waiter != thread);
711-// Now get back the lock.
712-let retval = self.retval_timeout;
713-self.reacquire_mutex(this, retval)
714-}
715-}
678+ @timeout = |this| {
679+// We have to remove the waiter from the queue again.
680+let thread = this.active_thread();
681+let waiters = &mut this.machine.sync.condvars[condvar].waiters;
682+ waiters.retain(|waiter| *waiter != thread);
683+// Now get back the lock.
684+ this.condvar_reacquire_mutex(mutex, retval_timeout, dest)
685+}
686+),
687+);
688+return Ok(());
716689}
717690718691/// 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> {
755728 this.block_thread(
756729BlockReason::Futex { addr },
757730 timeout,
758-Callback { addr, retval_succ, retval_timeout, dest, errno_timeout },
759-);
760-761-struct Callback<'tcx> {
762-addr: u64,
763-retval_succ: Scalar<Provenance>,
764-retval_timeout: Scalar<Provenance>,
765-dest: MPlaceTy<'tcx, Provenance>,
766-errno_timeout: Scalar<Provenance>,
767-}
768-impl<'tcx> VisitProvenance for Callback<'tcx> {
769-fn visit_provenance(&self, visit: &mut VisitWith<'_>) {
770-let Callback { addr: _, retval_succ, retval_timeout, dest, errno_timeout } = self;
771- retval_succ.visit_provenance(visit);
772- retval_timeout.visit_provenance(visit);
773- dest.visit_provenance(visit);
774- errno_timeout.visit_provenance(visit);
775-}
776-}
777-impl<'mir, 'tcx: 'mir> UnblockCallback<'mir, 'tcx> for Callback<'tcx> {
778-fn unblock(self: Box<Self>, this: &mut MiriInterpCx<'mir, 'tcx>) -> InterpResult<'tcx> {
779-let futex = this.machine.sync.futexes.get(&self.addr).unwrap();
780-// Acquire the clock of the futex.
781-if let Some(data_race) = &this.machine.data_race {
782- data_race.acquire_clock(&futex.clock, &this.machine.threads);
731+callback!(
732+ @capture<'tcx> {
733+ addr: u64,
734+ retval_succ: Scalar<Provenance>,
735+ retval_timeout: Scalar<Provenance>,
736+ dest: MPlaceTy<'tcx, Provenance>,
737+ errno_timeout: Scalar<Provenance>,
783738}
784-// Write the return value.
785- this.write_scalar(self.retval_succ, &self.dest)?;
786-Ok(())
787-}
788-fn timeout(
789-self: Box<Self>,
790-this: &mut InterpCx<'mir, 'tcx, MiriMachine<'mir, 'tcx>>,
791-) -> InterpResult<'tcx> {
792-// Remove the waiter from the futex.
793-let thread = this.active_thread();
794-let futex = this.machine.sync.futexes.get_mut(&self.addr).unwrap();
795- futex.waiters.retain(|waiter| waiter.thread != thread);
796-// Set errno and write return value.
797- this.set_last_error(self.errno_timeout)?;
798- this.write_scalar(self.retval_timeout, &self.dest)?;
799-Ok(())
800-}
801-}
739+ @unblock = |this| {
740+let futex = this.machine.sync.futexes.get(&addr).unwrap();
741+// Acquire the clock of the futex.
742+if let Some(data_race) = &this.machine.data_race {
743+ data_race.acquire_clock(&futex.clock, &this.machine.threads);
744+}
745+// Write the return value.
746+ this.write_scalar(retval_succ, &dest)?;
747+Ok(())
748+}
749+ @timeout = |this| {
750+// Remove the waiter from the futex.
751+let thread = this.active_thread();
752+let futex = this.machine.sync.futexes.get_mut(&addr).unwrap();
753+ futex.waiters.retain(|waiter| waiter.thread != thread);
754+// Set errno and write return value.
755+ this.set_last_error(errno_timeout)?;
756+ this.write_scalar(retval_timeout, &dest)?;
757+Ok(())
758+}
759+),
760+);
802761}
803762804763/// Returns whether anything was woken.