1use std::cell::{Cell, Ref, RefCell, RefMut};
44use std::fmt::Debug;
45use std::mem;
46
47use rand::RngExt;
48use rustc_abi::{Align, HasDataLayout, Size};
49use rustc_ast::Mutability;
50use rustc_data_structures::fx::{FxHashMap, FxHashSet};
51use rustc_index::{Idx, IndexVec};
52use rustc_log::tracing;
53use rustc_middle::mir;
54use rustc_middle::ty::Ty;
55use rustc_span::Span;
56
57use super::vector_clock::{VClock, VTimestamp, VectorIdx};
58use super::weak_memory::EvalContextExt as _;
59use crate::concurrency::GlobalDataRaceHandler;
60use crate::diagnostics::RacingOp;
61use crate::intrinsics::AtomicRmwOp;
62use crate::*;
63
64pub type AllocState = VClockAlloc;
65
66#[derive(Copy, Clone, PartialEq, Eq, Debug)]
68pub enum AtomicRwOrd {
69 Relaxed,
70 Acquire,
71 Release,
72 AcqRel,
73 SeqCst,
74}
75
76#[derive(Copy, Clone, PartialEq, Eq, Debug)]
78pub enum AtomicReadOrd {
79 Relaxed,
80 Acquire,
81 SeqCst,
82}
83
84#[derive(Copy, Clone, PartialEq, Eq, Debug)]
86pub enum AtomicWriteOrd {
87 Relaxed,
88 Release,
89 SeqCst,
90}
91
92#[derive(Copy, Clone, PartialEq, Eq, Debug)]
94pub enum AtomicFenceOrd {
95 Acquire,
96 Release,
97 AcqRel,
98 SeqCst,
99}
100
101#[derive(Clone, Default, Debug)]
105pub(super) struct ThreadClockSet {
106 pub(super) clock: VClock,
109
110 fence_acquire: VClock,
113
114 fence_release: VClock,
117
118 pub(super) write_seqcst: VClock,
123
124 pub(super) read_seqcst: VClock,
129}
130
131impl ThreadClockSet {
132 #[inline]
135 fn apply_release_fence(&mut self) {
136 self.fence_release.clone_from(&self.clock);
137 }
138
139 #[inline]
142 fn apply_acquire_fence(&mut self) {
143 self.clock.join(&self.fence_acquire);
144 }
145
146 #[inline]
149 fn increment_clock(&mut self, index: VectorIdx, current_span: Span) {
150 self.clock.increment_index(index, current_span);
151 }
152
153 fn join_with(&mut self, other: &ThreadClockSet) {
157 self.clock.join(&other.clock);
158 }
159}
160
161#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
164pub struct DataRace;
165
166#[derive(Clone, PartialEq, Eq, Debug)]
171struct AtomicMemoryCellClocks {
172 read_vector: VClock,
177
178 write_vector: VClock,
183
184 sync_vector: VClock,
192
193 size: Option<Size>,
198}
199
200#[derive(Copy, Clone, PartialEq, Eq, Debug)]
201enum AtomicAccessType {
202 Load(AtomicReadOrd),
203 Store,
204 Rmw,
205}
206
207#[derive(Copy, Clone, PartialEq, Eq, Debug)]
209pub enum NaReadType {
210 Read,
212
213 Retag,
215}
216
217impl NaReadType {
218 fn description(self) -> &'static str {
219 match self {
220 NaReadType::Read => "non-atomic read",
221 NaReadType::Retag => "retag read",
222 }
223 }
224}
225
226#[derive(Copy, Clone, PartialEq, Eq, Debug)]
229pub enum NaWriteType {
230 Allocate,
232
233 Write,
235
236 Retag,
238
239 Deallocate,
244}
245
246impl NaWriteType {
247 fn description(self) -> &'static str {
248 match self {
249 NaWriteType::Allocate => "creating a new allocation",
250 NaWriteType::Write => "non-atomic write",
251 NaWriteType::Retag => "retag write",
252 NaWriteType::Deallocate => "deallocation",
253 }
254 }
255}
256
257#[derive(Copy, Clone, PartialEq, Eq, Debug)]
258enum AccessType {
259 NaRead(NaReadType),
260 NaWrite(NaWriteType),
261 AtomicLoad,
262 AtomicStore,
263 AtomicRmw,
264}
265
266#[derive(Clone, PartialEq, Eq, Debug)]
268struct MemoryCellClocks {
269 write: (VectorIdx, VTimestamp),
273
274 write_type: NaWriteType,
278
279 read: VClock,
283
284 atomic_ops: Option<Box<AtomicMemoryCellClocks>>,
288}
289
290#[derive(Debug, Clone, Default)]
292struct ThreadExtraState {
293 vector_index: Option<VectorIdx>,
299
300 termination_vector_clock: Option<VClock>,
305}
306
307#[derive(Debug, Clone)]
312pub struct GlobalState {
313 multi_threaded: Cell<bool>,
320
321 ongoing_action_data_race_free: Cell<bool>,
325
326 vector_clocks: RefCell<IndexVec<VectorIdx, ThreadClockSet>>,
330
331 vector_info: RefCell<IndexVec<VectorIdx, ThreadId>>,
335
336 thread_info: RefCell<IndexVec<ThreadId, ThreadExtraState>>,
338
339 reuse_candidates: RefCell<FxHashSet<VectorIdx>>,
347
348 last_sc_fence: RefCell<VClock>,
351
352 last_sc_write_per_thread: RefCell<VClock>,
355
356 pub track_outdated_loads: bool,
358
359 pub weak_memory: bool,
361}
362
363impl VisitProvenance for GlobalState {
364 fn visit_provenance(&self, _visit: &mut VisitWith<'_>) {
365 }
367}
368
369impl AccessType {
370 fn description(self, ty: Option<Ty<'_>>, size: Option<Size>) -> String {
371 let mut msg = String::new();
372
373 if let Some(size) = size {
374 if size == Size::ZERO {
375 assert!(self == AccessType::AtomicLoad);
379 assert!(ty.is_none());
380 return format!("multiple differently-sized atomic loads, including one load");
381 }
382 msg.push_str(&format!("{}-byte {}", size.bytes(), msg))
383 }
384
385 msg.push_str(match self {
386 AccessType::NaRead(w) => w.description(),
387 AccessType::NaWrite(w) => w.description(),
388 AccessType::AtomicLoad => "atomic load",
389 AccessType::AtomicStore => "atomic store",
390 AccessType::AtomicRmw => "atomic read-modify-write",
391 });
392
393 if let Some(ty) = ty {
394 msg.push_str(&format!(" of type `{ty}`"));
395 }
396
397 msg
398 }
399
400 fn is_atomic(self) -> bool {
401 match self {
402 AccessType::AtomicLoad | AccessType::AtomicStore | AccessType::AtomicRmw => true,
403 AccessType::NaRead(_) | AccessType::NaWrite(_) => false,
404 }
405 }
406
407 fn is_read(self) -> bool {
408 match self {
409 AccessType::AtomicLoad | AccessType::NaRead(_) => true,
410 AccessType::NaWrite(_) | AccessType::AtomicStore | AccessType::AtomicRmw => false,
411 }
412 }
413
414 fn is_retag(self) -> bool {
415 matches!(
416 self,
417 AccessType::NaRead(NaReadType::Retag) | AccessType::NaWrite(NaWriteType::Retag)
418 )
419 }
420}
421
422impl AtomicMemoryCellClocks {
423 fn new(size: Size) -> Self {
424 AtomicMemoryCellClocks {
425 read_vector: Default::default(),
426 write_vector: Default::default(),
427 sync_vector: Default::default(),
428 size: Some(size),
429 }
430 }
431}
432
433impl MemoryCellClocks {
434 fn new(alloc: VTimestamp, alloc_index: VectorIdx) -> Self {
437 MemoryCellClocks {
438 read: VClock::default(),
439 write: (alloc_index, alloc),
440 write_type: NaWriteType::Allocate,
441 atomic_ops: None,
442 }
443 }
444
445 #[inline]
446 fn write_was_before(&self, other: &VClock) -> bool {
447 self.write.1 <= other[self.write.0]
450 }
451
452 #[inline]
453 fn write(&self) -> VClock {
454 VClock::new_with_index(self.write.0, self.write.1)
455 }
456
457 #[inline]
459 fn atomic(&self) -> Option<&AtomicMemoryCellClocks> {
460 self.atomic_ops.as_deref()
461 }
462
463 #[inline]
465 fn atomic_mut_unwrap(&mut self) -> &mut AtomicMemoryCellClocks {
466 self.atomic_ops.as_deref_mut().unwrap()
467 }
468
469 fn atomic_access(
472 &mut self,
473 thread_clocks: &ThreadClockSet,
474 size: Size,
475 write: bool,
476 ) -> Result<&mut AtomicMemoryCellClocks, DataRace> {
477 match self.atomic_ops {
478 Some(ref mut atomic) => {
479 if atomic.size == Some(size) {
481 Ok(atomic)
482 } else if atomic.read_vector <= thread_clocks.clock
483 && atomic.write_vector <= thread_clocks.clock
484 {
485 atomic.size = Some(size);
487 Ok(atomic)
488 } else if !write && atomic.write_vector <= thread_clocks.clock {
489 atomic.size = None;
492 Ok(atomic)
493 } else {
494 Err(DataRace)
495 }
496 }
497 None => {
498 self.atomic_ops = Some(Box::new(AtomicMemoryCellClocks::new(size)));
499 Ok(self.atomic_ops.as_mut().unwrap())
500 }
501 }
502 }
503
504 fn load_acquire(
508 &mut self,
509 thread_clocks: &mut ThreadClockSet,
510 index: VectorIdx,
511 access_size: Size,
512 sync_clock: Option<&VClock>,
513 ) -> Result<(), DataRace> {
514 self.atomic_read_detect(thread_clocks, index, access_size)?;
515 if let Some(sync_clock) = sync_clock.or_else(|| self.atomic().map(|a| &a.sync_vector)) {
516 thread_clocks.clock.join(sync_clock);
517 }
518 Ok(())
519 }
520
521 fn load_relaxed(
525 &mut self,
526 thread_clocks: &mut ThreadClockSet,
527 index: VectorIdx,
528 access_size: Size,
529 sync_clock: Option<&VClock>,
530 ) -> Result<(), DataRace> {
531 self.atomic_read_detect(thread_clocks, index, access_size)?;
532 if let Some(sync_clock) = sync_clock.or_else(|| self.atomic().map(|a| &a.sync_vector)) {
533 thread_clocks.fence_acquire.join(sync_clock);
534 }
535 Ok(())
536 }
537
538 fn store_release(
541 &mut self,
542 thread_clocks: &ThreadClockSet,
543 index: VectorIdx,
544 access_size: Size,
545 ) -> Result<(), DataRace> {
546 self.atomic_write_detect(thread_clocks, index, access_size)?;
547 let atomic = self.atomic_mut_unwrap(); atomic.sync_vector.clone_from(&thread_clocks.clock);
549 Ok(())
550 }
551
552 fn store_relaxed(
555 &mut self,
556 thread_clocks: &ThreadClockSet,
557 index: VectorIdx,
558 access_size: Size,
559 ) -> Result<(), DataRace> {
560 self.atomic_write_detect(thread_clocks, index, access_size)?;
561
562 let atomic = self.atomic_mut_unwrap();
568 atomic.sync_vector.clone_from(&thread_clocks.fence_release);
569 Ok(())
570 }
571
572 fn rmw_release(
575 &mut self,
576 thread_clocks: &ThreadClockSet,
577 index: VectorIdx,
578 access_size: Size,
579 ) -> Result<(), DataRace> {
580 self.atomic_write_detect(thread_clocks, index, access_size)?;
581 let atomic = self.atomic_mut_unwrap();
582 atomic.sync_vector.join(&thread_clocks.clock);
585 Ok(())
586 }
587
588 fn rmw_relaxed(
591 &mut self,
592 thread_clocks: &ThreadClockSet,
593 index: VectorIdx,
594 access_size: Size,
595 ) -> Result<(), DataRace> {
596 self.atomic_write_detect(thread_clocks, index, access_size)?;
597 let atomic = self.atomic_mut_unwrap();
598 atomic.sync_vector.join(&thread_clocks.fence_release);
601 Ok(())
602 }
603
604 fn atomic_read_detect(
607 &mut self,
608 thread_clocks: &ThreadClockSet,
609 index: VectorIdx,
610 access_size: Size,
611 ) -> Result<(), DataRace> {
612 trace!("Atomic read with vectors: {:#?} :: {:#?}", self, thread_clocks);
613 let atomic = self.atomic_access(thread_clocks, access_size, false)?;
614 atomic.read_vector.set_at_index(&thread_clocks.clock, index);
615 if self.write_was_before(&thread_clocks.clock) { Ok(()) } else { Err(DataRace) }
617 }
618
619 fn atomic_write_detect(
622 &mut self,
623 thread_clocks: &ThreadClockSet,
624 index: VectorIdx,
625 access_size: Size,
626 ) -> Result<(), DataRace> {
627 trace!("Atomic write with vectors: {:#?} :: {:#?}", self, thread_clocks);
628 let atomic = self.atomic_access(thread_clocks, access_size, true)?;
629 atomic.write_vector.set_at_index(&thread_clocks.clock, index);
630 if self.write_was_before(&thread_clocks.clock) && self.read <= thread_clocks.clock {
632 Ok(())
633 } else {
634 Err(DataRace)
635 }
636 }
637
638 fn read_race_detect(
641 &mut self,
642 thread_clocks: &mut ThreadClockSet,
643 index: VectorIdx,
644 read_type: NaReadType,
645 current_span: Span,
646 ) -> Result<(), DataRace> {
647 trace!("Unsynchronized read with vectors: {:#?} :: {:#?}", self, thread_clocks);
648 if !current_span.is_dummy() {
649 thread_clocks.clock.index_mut(index).span = current_span;
650 }
651 thread_clocks.clock.index_mut(index).set_read_type(read_type);
652 if !self.write_was_before(&thread_clocks.clock) {
654 return Err(DataRace);
655 }
656 if !self.atomic().is_none_or(|atomic| atomic.write_vector <= thread_clocks.clock) {
658 return Err(DataRace);
659 }
660 self.read.set_at_index(&thread_clocks.clock, index);
662 Ok(())
663 }
664
665 fn write_race_detect(
668 &mut self,
669 thread_clocks: &mut ThreadClockSet,
670 index: VectorIdx,
671 write_type: NaWriteType,
672 current_span: Span,
673 ) -> Result<(), DataRace> {
674 trace!("Unsynchronized write with vectors: {:#?} :: {:#?}", self, thread_clocks);
675 if !current_span.is_dummy() {
676 thread_clocks.clock.index_mut(index).span = current_span;
677 }
678 if !(self.write_was_before(&thread_clocks.clock) && self.read <= thread_clocks.clock) {
680 return Err(DataRace);
681 }
682 if !self.atomic().is_none_or(|atomic| {
684 atomic.write_vector <= thread_clocks.clock && atomic.read_vector <= thread_clocks.clock
685 }) {
686 return Err(DataRace);
687 }
688 self.write = (index, thread_clocks.clock[index]);
690 self.write_type = write_type;
691 self.read.set_zero_vector();
692 self.atomic_ops = None;
694 Ok(())
695 }
696}
697
698impl GlobalDataRaceHandler {
699 fn set_ongoing_action_data_race_free(&self, enable: bool) {
702 match self {
703 GlobalDataRaceHandler::None => {}
704 GlobalDataRaceHandler::Vclocks(data_race) => {
705 let old = data_race.ongoing_action_data_race_free.replace(enable);
706 assert_ne!(old, enable, "cannot nest allow_data_races");
707 }
708 GlobalDataRaceHandler::Genmc(genmc_ctx) => {
709 genmc_ctx.set_ongoing_action_data_race_free(enable);
710 }
711 }
712 }
713}
714
715impl<'tcx> EvalContextExt<'tcx> for MiriInterpCx<'tcx> {}
717pub trait EvalContextExt<'tcx>: MiriInterpCxExt<'tcx> {
718 fn read_scalar_atomic(
720 &self,
721 place: &MPlaceTy<'tcx>,
722 atomic: AtomicReadOrd,
723 ) -> InterpResult<'tcx, Scalar> {
724 let this = self.eval_context_ref();
725 this.atomic_access_check(place, AtomicAccessType::Load(atomic))?;
726 if let Some(genmc_ctx) = this.machine.data_race.as_genmc_ref() {
733 let old_val = this.run_for_validation_ref(|this| this.read_scalar(place)).discard_err();
734 return genmc_ctx.atomic_load(
735 this,
736 place.ptr().addr(),
737 place.layout.size,
738 atomic,
739 old_val,
740 );
741 }
742
743 let scalar = this.allow_data_races_ref(move |this| this.read_scalar(place))?;
744 let buffered_scalar = this.buffered_atomic_read(place, atomic, scalar, |sync_clock| {
745 this.validate_atomic_load(place, atomic, sync_clock)
746 })?;
747 interp_ok(buffered_scalar.ok_or_else(|| err_ub!(InvalidUninitBytes(None)))?)
748 }
749
750 fn write_scalar_atomic(
752 &mut self,
753 val: Scalar,
754 dest: &MPlaceTy<'tcx>,
755 atomic: AtomicWriteOrd,
756 ) -> InterpResult<'tcx> {
757 let this = self.eval_context_mut();
758 this.atomic_access_check(dest, AtomicAccessType::Store)?;
759
760 if let Some(genmc_ctx) = this.machine.data_race.as_genmc_ref() {
762 let old_val = this.run_for_validation_ref(|this| this.read_scalar(dest)).discard_err();
763 if genmc_ctx.atomic_store(
764 this,
765 dest.ptr().addr(),
766 dest.layout.size,
767 val,
768 old_val,
769 atomic,
770 )? {
771 this.allow_data_races_mut(|this| this.write_scalar(val, dest))?;
774 }
775 return interp_ok(());
776 }
777
778 let old_val = this.get_latest_nonatomic_val(dest);
780 this.allow_data_races_mut(move |this| this.write_scalar(val, dest))?;
781 this.validate_atomic_store(dest, atomic)?;
782 this.buffered_atomic_write(val, dest, atomic, old_val)
783 }
784
785 fn atomic_rmw_op_immediate(
787 &mut self,
788 place: &MPlaceTy<'tcx>,
789 rhs: &ImmTy<'tcx>,
790 atomic_op: AtomicRmwOp,
791 ord: AtomicRwOrd,
792 ) -> InterpResult<'tcx, ImmTy<'tcx>> {
793 let this = self.eval_context_mut();
794 this.atomic_access_check(place, AtomicAccessType::Rmw)?;
795
796 let old = this.allow_data_races_mut(|this| this.read_immediate(place))?;
797
798 if let Some(genmc_ctx) = this.machine.data_race.as_genmc_ref() {
800 let (old_val, new_val) = genmc_ctx.atomic_rmw_op(
801 this,
802 place.ptr().addr(),
803 place.layout.size,
804 atomic_op,
805 place.layout.backend_repr.is_signed(),
806 ord,
807 rhs.to_scalar(),
808 old.to_scalar(),
809 )?;
810 if let Some(new_val) = new_val {
811 this.allow_data_races_mut(|this| this.write_scalar(new_val, place))?;
812 }
813 return interp_ok(ImmTy::from_scalar(old_val, old.layout));
814 }
815
816 let val = match atomic_op {
817 AtomicRmwOp::MirOp { op, neg } => {
818 let val = this.binary_op(op, &old, rhs)?;
819 if neg { this.unary_op(mir::UnOp::Not, &val)? } else { val }
820 }
821 AtomicRmwOp::Max => {
822 let lt = this.binary_op(mir::BinOp::Lt, &old, rhs)?.to_scalar().to_bool()?;
823 if lt { rhs } else { &old }.clone()
824 }
825 AtomicRmwOp::Min => {
826 let lt = this.binary_op(mir::BinOp::Lt, &old, rhs)?.to_scalar().to_bool()?;
827 if lt { &old } else { rhs }.clone()
828 }
829 };
830
831 this.allow_data_races_mut(|this| this.write_immediate(*val, place))?;
832
833 this.validate_atomic_rmw(place, ord)?;
834
835 this.buffered_atomic_rmw(val.to_scalar(), place, ord, old.to_scalar())?;
836 interp_ok(old)
837 }
838
839 fn atomic_exchange_scalar(
842 &mut self,
843 place: &MPlaceTy<'tcx>,
844 new: Scalar,
845 atomic: AtomicRwOrd,
846 ) -> InterpResult<'tcx, Scalar> {
847 let this = self.eval_context_mut();
848 this.atomic_access_check(place, AtomicAccessType::Rmw)?;
849
850 let old = this.allow_data_races_mut(|this| this.read_scalar(place))?;
851 this.allow_data_races_mut(|this| this.write_scalar(new, place))?;
852
853 if let Some(genmc_ctx) = this.machine.data_race.as_genmc_ref() {
855 let (old_val, new_val) = genmc_ctx.atomic_exchange(
856 this,
857 place.ptr().addr(),
858 place.layout.size,
859 new,
860 atomic,
861 old,
862 )?;
863 if let Some(new_val) = new_val {
866 this.allow_data_races_mut(|this| this.write_scalar(new_val, place))?;
867 }
868 return interp_ok(old_val);
869 }
870
871 this.validate_atomic_rmw(place, atomic)?;
872
873 this.buffered_atomic_rmw(new, place, atomic, old)?;
874 interp_ok(old)
875 }
876
877 fn atomic_compare_exchange_scalar(
884 &mut self,
885 place: &MPlaceTy<'tcx>,
886 expect_old: &ImmTy<'tcx>,
887 new: Scalar,
888 success: AtomicRwOrd,
889 fail: AtomicReadOrd,
890 can_fail_spuriously: bool,
891 ) -> InterpResult<'tcx, Immediate<Provenance>> {
892 let this = self.eval_context_mut();
893 this.atomic_access_check(place, AtomicAccessType::Rmw)?;
894
895 let old = this.allow_data_races_mut(|this| this.read_immediate(place))?;
897
898 if let Some(genmc_ctx) = this.machine.data_race.as_genmc_ref() {
900 let (old_value, new_value, cmpxchg_success) = genmc_ctx.atomic_compare_exchange(
901 this,
902 place.ptr().addr(),
903 place.layout.size,
904 this.read_scalar(expect_old)?,
905 new,
906 success,
907 fail,
908 can_fail_spuriously,
909 old.to_scalar(),
910 )?;
911 if let Some(new_value) = new_value {
914 this.allow_data_races_mut(|this| this.write_scalar(new_value, place))?;
915 }
916 return interp_ok(Immediate::ScalarPair(old_value, Scalar::from_bool(cmpxchg_success)));
917 }
918
919 let eq = this.binary_op(mir::BinOp::Eq, &old, expect_old)?;
921 let success_rate = 1.0 - this.machine.cmpxchg_weak_failure_rate;
924 let cmpxchg_success = eq.to_scalar().to_bool()?
925 && if can_fail_spuriously {
926 this.machine.rng.get_mut().random_bool(success_rate)
927 } else {
928 true
929 };
930 let res = Immediate::ScalarPair(old.to_scalar(), Scalar::from_bool(cmpxchg_success));
931
932 if cmpxchg_success {
936 this.allow_data_races_mut(|this| this.write_scalar(new, place))?;
937 this.validate_atomic_rmw(place, success)?;
938 this.buffered_atomic_rmw(new, place, success, old.to_scalar())?;
939 } else {
940 this.validate_atomic_load(place, fail, None)?;
941 this.perform_read_on_buffered_latest(place, fail)?;
946 }
947
948 interp_ok(res)
950 }
951
952 fn atomic_fence(&mut self, atomic: AtomicFenceOrd) -> InterpResult<'tcx> {
954 let this = self.eval_context_mut();
955 let machine = &this.machine;
956 match &this.machine.data_race {
957 GlobalDataRaceHandler::None => interp_ok(()),
958 GlobalDataRaceHandler::Vclocks(data_race) => data_race.atomic_fence(machine, atomic),
959 GlobalDataRaceHandler::Genmc(genmc_ctx) => genmc_ctx.atomic_fence(machine, atomic),
960 }
961 }
962
963 fn release_clock<R>(
969 &self,
970 callback: impl FnOnce(&VClock) -> R,
971 ) -> InterpResult<'tcx, Option<R>> {
972 let this = self.eval_context_ref();
973 interp_ok(match &this.machine.data_race {
974 GlobalDataRaceHandler::None => None,
975 GlobalDataRaceHandler::Genmc(_genmc_ctx) =>
976 throw_unsup_format!(
977 "this operation performs synchronization that is not supported in GenMC mode"
978 ),
979 GlobalDataRaceHandler::Vclocks(data_race) =>
980 Some(data_race.release_clock(&this.machine.threads, callback)),
981 })
982 }
983
984 fn acquire_clock(&self, clock: &VClock) -> InterpResult<'tcx> {
987 let this = self.eval_context_ref();
988 match &this.machine.data_race {
989 GlobalDataRaceHandler::None => {}
990 GlobalDataRaceHandler::Genmc(_genmc_ctx) =>
991 throw_unsup_format!(
992 "this operation performs synchronization that is not supported in GenMC mode"
993 ),
994 GlobalDataRaceHandler::Vclocks(data_race) =>
995 data_race.acquire_clock(clock, &this.machine.threads),
996 }
997 interp_ok(())
998 }
999}
1000
1001#[derive(Debug, Clone)]
1003pub struct VClockAlloc {
1004 alloc_ranges: RefCell<DedupRangeMap<MemoryCellClocks>>,
1006}
1007
1008impl VisitProvenance for VClockAlloc {
1009 fn visit_provenance(&self, _visit: &mut VisitWith<'_>) {
1010 }
1012}
1013
1014impl VClockAlloc {
1015 pub fn new_allocation(
1017 global: &GlobalState,
1018 thread_mgr: &ThreadManager<'_>,
1019 len: Size,
1020 kind: MemoryKind,
1021 current_span: Span,
1022 ) -> VClockAlloc {
1023 let (alloc_timestamp, alloc_index) = match kind {
1025 MemoryKind::Machine(
1027 MiriMemoryKind::Rust
1028 | MiriMemoryKind::Miri
1029 | MiriMemoryKind::C
1030 | MiriMemoryKind::WinHeap
1031 | MiriMemoryKind::WinLocal
1032 | MiriMemoryKind::Mmap
1033 | MiriMemoryKind::SocketAddress,
1034 )
1035 | MemoryKind::Stack => {
1036 let (alloc_index, clocks) = global.active_thread_state(thread_mgr);
1037 let mut alloc_timestamp = clocks.clock[alloc_index];
1038 alloc_timestamp.span = current_span;
1039 (alloc_timestamp, alloc_index)
1040 }
1041 MemoryKind::Machine(
1044 MiriMemoryKind::Global
1045 | MiriMemoryKind::Machine
1046 | MiriMemoryKind::Runtime
1047 | MiriMemoryKind::ExternStatic
1048 | MiriMemoryKind::Tls,
1049 )
1050 | MemoryKind::CallerLocation =>
1051 (VTimestamp::ZERO, global.thread_index(ThreadId::MAIN_THREAD)),
1052 };
1053 VClockAlloc {
1054 alloc_ranges: RefCell::new(DedupRangeMap::new(
1055 len,
1056 MemoryCellClocks::new(alloc_timestamp, alloc_index),
1057 )),
1058 }
1059 }
1060
1061 fn find_gt_index(l: &VClock, r: &VClock) -> Option<VectorIdx> {
1064 trace!("Find index where not {:?} <= {:?}", l, r);
1065 let l_slice = l.as_slice();
1066 let r_slice = r.as_slice();
1067 l_slice
1068 .iter()
1069 .zip(r_slice.iter())
1070 .enumerate()
1071 .find_map(|(idx, (&l, &r))| if l > r { Some(idx) } else { None })
1072 .or_else(|| {
1073 if l_slice.len() > r_slice.len() {
1074 let l_remainder_slice = &l_slice[r_slice.len()..];
1079 let idx = l_remainder_slice
1080 .iter()
1081 .enumerate()
1082 .find_map(|(idx, &r)| if r == VTimestamp::ZERO { None } else { Some(idx) })
1083 .expect("Invalid VClock Invariant");
1084 Some(idx + r_slice.len())
1085 } else {
1086 None
1087 }
1088 })
1089 .map(VectorIdx::new)
1090 }
1091
1092 #[cold]
1099 #[inline(never)]
1100 fn report_data_race<'tcx>(
1101 global: &GlobalState,
1102 thread_mgr: &ThreadManager<'_>,
1103 mem_clocks: &MemoryCellClocks,
1104 access: AccessType,
1105 access_size: Size,
1106 ptr_dbg: interpret::Pointer<AllocId>,
1107 ty: Option<Ty<'_>>,
1108 ) -> InterpResult<'tcx> {
1109 let (active_index, active_clocks) = global.active_thread_state(thread_mgr);
1110 let mut other_size = None; let write_clock;
1112 let (other_access, other_thread, other_clock) =
1113 if !access.is_atomic() &&
1115 let Some(atomic) = mem_clocks.atomic() &&
1116 let Some(idx) = Self::find_gt_index(&atomic.write_vector, &active_clocks.clock)
1117 {
1118 (AccessType::AtomicStore, idx, &atomic.write_vector)
1119 } else if !access.is_atomic() &&
1120 !access.is_read() &&
1121 let Some(atomic) = mem_clocks.atomic() &&
1122 let Some(idx) = Self::find_gt_index(&atomic.read_vector, &active_clocks.clock)
1123 {
1124 (AccessType::AtomicLoad, idx, &atomic.read_vector)
1125 } else if mem_clocks.write.1 > active_clocks.clock[mem_clocks.write.0] {
1127 write_clock = mem_clocks.write();
1128 (AccessType::NaWrite(mem_clocks.write_type), mem_clocks.write.0, &write_clock)
1129 } else if !access.is_read() && let Some(idx) = Self::find_gt_index(&mem_clocks.read, &active_clocks.clock) {
1130 (AccessType::NaRead(mem_clocks.read[idx].read_type()), idx, &mem_clocks.read)
1131 } else if access.is_atomic() && let Some(atomic) = mem_clocks.atomic() && atomic.size != Some(access_size) {
1133 other_size = Some(atomic.size.unwrap_or(Size::ZERO));
1136 if let Some(idx) = Self::find_gt_index(&atomic.write_vector, &active_clocks.clock)
1137 {
1138 (AccessType::AtomicStore, idx, &atomic.write_vector)
1139 } else if let Some(idx) =
1140 Self::find_gt_index(&atomic.read_vector, &active_clocks.clock)
1141 {
1142 (AccessType::AtomicLoad, idx, &atomic.read_vector)
1143 } else {
1144 unreachable!(
1145 "Failed to report data-race for mixed-size access: no race found"
1146 )
1147 }
1148 } else {
1149 unreachable!("Failed to report data-race")
1150 };
1151
1152 let active_thread_info = global.print_thread_metadata(thread_mgr, active_index);
1154 let other_thread_info = global.print_thread_metadata(thread_mgr, other_thread);
1155 let involves_non_atomic = !access.is_atomic() || !other_access.is_atomic();
1156
1157 let extra = if other_size.is_some() {
1159 assert!(!involves_non_atomic);
1160 Some("overlapping unsynchronized atomic accesses must use the same access size")
1161 } else if access.is_read() && other_access.is_read() {
1162 panic!(
1163 "there should be no same-size read-read races\naccess: {access:?}\nother_access: {other_access:?}"
1164 )
1165 } else {
1166 None
1167 };
1168 Err(err_machine_stop!(TerminationInfo::DataRace {
1169 involves_non_atomic,
1170 extra,
1171 retag_explain: access.is_retag() || other_access.is_retag(),
1172 ptr: ptr_dbg,
1173 op1: RacingOp {
1174 action: other_access.description(None, other_size),
1175 thread_info: other_thread_info,
1176 span: other_clock.as_slice()[other_thread.index()].span_data(),
1177 },
1178 op2: RacingOp {
1179 action: access.description(ty, other_size.map(|_| access_size)),
1180 thread_info: active_thread_info,
1181 span: active_clocks.clock.as_slice()[active_index.index()].span_data(),
1182 },
1183 }))?
1184 }
1185
1186 pub(super) fn sync_clock(&self, access_range: AllocRange) -> VClock {
1188 let alloc_ranges = self.alloc_ranges.borrow();
1189 let mut clock = VClock::default();
1190 for (_, mem_clocks) in alloc_ranges.iter(access_range.start, access_range.size) {
1191 if let Some(atomic) = mem_clocks.atomic() {
1192 clock.join(&atomic.sync_vector);
1193 }
1194 }
1195 clock
1196 }
1197
1198 pub fn read_non_atomic<'tcx>(
1205 &self,
1206 alloc_id: AllocId,
1207 access_range: AllocRange,
1208 read_type: NaReadType,
1209 ty: Option<Ty<'_>>,
1210 machine: &MiriMachine<'_>,
1211 ) -> InterpResult<'tcx> {
1212 let current_span = machine.current_user_relevant_span();
1213 let global = machine.data_race.as_vclocks_ref().unwrap();
1214 if !global.race_detecting() {
1215 return interp_ok(());
1216 }
1217 let (index, mut thread_clocks) = global.active_thread_state_mut(&machine.threads);
1218 let mut alloc_ranges = self.alloc_ranges.borrow_mut();
1219 for (mem_clocks_range, mem_clocks) in
1220 alloc_ranges.iter_mut(access_range.start, access_range.size)
1221 {
1222 if let Err(DataRace) =
1223 mem_clocks.read_race_detect(&mut thread_clocks, index, read_type, current_span)
1224 {
1225 drop(thread_clocks);
1226 return Self::report_data_race(
1228 global,
1229 &machine.threads,
1230 mem_clocks,
1231 AccessType::NaRead(read_type),
1232 access_range.size,
1233 interpret::Pointer::new(alloc_id, Size::from_bytes(mem_clocks_range.start)),
1234 ty,
1235 );
1236 }
1237 }
1238 interp_ok(())
1239 }
1240
1241 pub fn write_non_atomic<'tcx>(
1247 &self,
1248 alloc_id: AllocId,
1249 access_range: AllocRange,
1250 write_type: NaWriteType,
1251 ty: Option<Ty<'_>>,
1252 machine: &MiriMachine<'_>,
1253 ) -> InterpResult<'tcx> {
1254 let current_span = machine.current_user_relevant_span();
1255 let global = machine.data_race.as_vclocks_ref().unwrap();
1256 if !global.race_detecting() {
1257 return interp_ok(());
1258 }
1259 let (index, mut thread_clocks) = global.active_thread_state_mut(&machine.threads);
1260 for (mem_clocks_range, mem_clocks) in
1261 self.alloc_ranges.borrow_mut().iter_mut(access_range.start, access_range.size)
1262 {
1263 if let Err(DataRace) =
1264 mem_clocks.write_race_detect(&mut thread_clocks, index, write_type, current_span)
1265 {
1266 drop(thread_clocks);
1267 return Self::report_data_race(
1269 global,
1270 &machine.threads,
1271 mem_clocks,
1272 AccessType::NaWrite(write_type),
1273 access_range.size,
1274 interpret::Pointer::new(alloc_id, Size::from_bytes(mem_clocks_range.start)),
1275 ty,
1276 );
1277 }
1278 }
1279 interp_ok(())
1280 }
1281}
1282
1283#[derive(Debug, Default)]
1286pub struct FrameState {
1287 local_clocks: RefCell<FxHashMap<mir::Local, LocalClocks>>,
1288}
1289
1290#[derive(Debug)]
1294struct LocalClocks {
1295 write: VTimestamp,
1296 write_type: NaWriteType,
1297 read: VTimestamp,
1298}
1299
1300impl Default for LocalClocks {
1301 fn default() -> Self {
1302 Self { write: VTimestamp::ZERO, write_type: NaWriteType::Allocate, read: VTimestamp::ZERO }
1303 }
1304}
1305
1306impl FrameState {
1307 pub fn local_write(&self, local: mir::Local, storage_live: bool, machine: &MiriMachine<'_>) {
1308 let current_span = machine.current_user_relevant_span();
1309 let global = machine.data_race.as_vclocks_ref().unwrap();
1310 if !global.race_detecting() {
1311 return;
1312 }
1313 let (index, mut thread_clocks) = global.active_thread_state_mut(&machine.threads);
1314 if !current_span.is_dummy() {
1316 thread_clocks.clock.index_mut(index).span = current_span;
1317 }
1318 let mut clocks = self.local_clocks.borrow_mut();
1319 if storage_live {
1320 let new_clocks = LocalClocks {
1321 write: thread_clocks.clock[index],
1322 write_type: NaWriteType::Allocate,
1323 read: VTimestamp::ZERO,
1324 };
1325 clocks.insert(local, new_clocks);
1328 } else {
1329 let clocks = clocks.entry(local).or_default();
1332 clocks.write = thread_clocks.clock[index];
1333 clocks.write_type = NaWriteType::Write;
1334 }
1335 }
1336
1337 pub fn local_read(&self, local: mir::Local, machine: &MiriMachine<'_>) {
1338 let current_span = machine.current_user_relevant_span();
1339 let global = machine.data_race.as_vclocks_ref().unwrap();
1340 if !global.race_detecting() {
1341 return;
1342 }
1343 let (index, mut thread_clocks) = global.active_thread_state_mut(&machine.threads);
1344 if !current_span.is_dummy() {
1346 thread_clocks.clock.index_mut(index).span = current_span;
1347 }
1348 thread_clocks.clock.index_mut(index).set_read_type(NaReadType::Read);
1349 let mut clocks = self.local_clocks.borrow_mut();
1352 let clocks = clocks.entry(local).or_default();
1353 clocks.read = thread_clocks.clock[index];
1354 }
1355
1356 pub fn local_moved_to_memory(
1357 &self,
1358 local: mir::Local,
1359 alloc: &mut VClockAlloc,
1360 machine: &MiriMachine<'_>,
1361 ) {
1362 let global = machine.data_race.as_vclocks_ref().unwrap();
1363 if !global.race_detecting() {
1364 return;
1365 }
1366 let (index, _thread_clocks) = global.active_thread_state_mut(&machine.threads);
1367 let local_clocks = self.local_clocks.borrow_mut().remove(&local).unwrap_or_default();
1371 for (_mem_clocks_range, mem_clocks) in alloc.alloc_ranges.get_mut().iter_mut_all() {
1372 assert_eq!(mem_clocks.write.0, index);
1375 mem_clocks.write = (index, local_clocks.write);
1377 mem_clocks.write_type = local_clocks.write_type;
1378 mem_clocks.read = VClock::new_with_index(index, local_clocks.read);
1379 }
1380 }
1381}
1382
1383impl<'tcx> EvalContextPrivExt<'tcx> for MiriInterpCx<'tcx> {}
1384trait EvalContextPrivExt<'tcx>: MiriInterpCxExt<'tcx> {
1385 #[inline]
1393 fn allow_data_races_ref<R>(&self, op: impl FnOnce(&MiriInterpCx<'tcx>) -> R) -> R {
1394 let this = self.eval_context_ref();
1395 this.machine.data_race.set_ongoing_action_data_race_free(true);
1396 let result = op(this);
1397 this.machine.data_race.set_ongoing_action_data_race_free(false);
1398 result
1399 }
1400
1401 #[inline]
1405 fn allow_data_races_mut<R>(&mut self, op: impl FnOnce(&mut MiriInterpCx<'tcx>) -> R) -> R {
1406 let this = self.eval_context_mut();
1407 this.machine.data_race.set_ongoing_action_data_race_free(true);
1408 let result = op(this);
1409 this.machine.data_race.set_ongoing_action_data_race_free(false);
1410 result
1411 }
1412
1413 fn atomic_access_check(
1415 &self,
1416 place: &MPlaceTy<'tcx>,
1417 access_type: AtomicAccessType,
1418 ) -> InterpResult<'tcx> {
1419 let this = self.eval_context_ref();
1420 let align = Align::from_bytes(place.layout.size.bytes()).unwrap();
1424 this.check_ptr_align(place.ptr(), align)?;
1425 let (alloc_id, _offset, _prov) = this
1433 .ptr_try_get_alloc_id(place.ptr(), 0)
1434 .expect("there are no zero-sized atomic accesses");
1435 if this.get_alloc_mutability(alloc_id)? == Mutability::Not {
1436 match access_type {
1438 AtomicAccessType::Rmw | AtomicAccessType::Store => {
1439 throw_ub_format!(
1440 "atomic store and read-modify-write operations cannot be performed on read-only memory\n\
1441 see <https://doc.rust-lang.org/nightly/std/sync/atomic/index.html#atomic-accesses-to-read-only-memory> for more information"
1442 );
1443 }
1444 AtomicAccessType::Load(_)
1445 if place.layout.size > this.tcx.data_layout().pointer_size() =>
1446 {
1447 throw_ub_format!(
1448 "large atomic load operations cannot be performed on read-only memory\n\
1449 these operations often have to be implemented using read-modify-write operations, which require writeable memory\n\
1450 see <https://doc.rust-lang.org/nightly/std/sync/atomic/index.html#atomic-accesses-to-read-only-memory> for more information"
1451 );
1452 }
1453 AtomicAccessType::Load(o) if o != AtomicReadOrd::Relaxed => {
1454 throw_ub_format!(
1455 "non-relaxed atomic load operations cannot be performed on read-only memory\n\
1456 these operations sometimes have to be implemented using read-modify-write operations, which require writeable memory\n\
1457 see <https://doc.rust-lang.org/nightly/std/sync/atomic/index.html#atomic-accesses-to-read-only-memory> for more information"
1458 );
1459 }
1460 _ => {
1461 }
1463 }
1464 }
1465 interp_ok(())
1466 }
1467
1468 fn validate_atomic_load(
1471 &self,
1472 place: &MPlaceTy<'tcx>,
1473 atomic: AtomicReadOrd,
1474 sync_clock: Option<&VClock>,
1475 ) -> InterpResult<'tcx> {
1476 let this = self.eval_context_ref();
1477 this.validate_atomic_op(
1478 place,
1479 atomic,
1480 AccessType::AtomicLoad,
1481 move |memory, clocks, index, atomic| {
1482 if atomic == AtomicReadOrd::Relaxed {
1483 memory.load_relaxed(&mut *clocks, index, place.layout.size, sync_clock)
1484 } else {
1485 memory.load_acquire(&mut *clocks, index, place.layout.size, sync_clock)
1486 }
1487 },
1488 )
1489 }
1490
1491 fn validate_atomic_store(
1494 &mut self,
1495 place: &MPlaceTy<'tcx>,
1496 atomic: AtomicWriteOrd,
1497 ) -> InterpResult<'tcx> {
1498 let this = self.eval_context_mut();
1499 this.validate_atomic_op(
1500 place,
1501 atomic,
1502 AccessType::AtomicStore,
1503 move |memory, clocks, index, atomic| {
1504 if atomic == AtomicWriteOrd::Relaxed {
1505 memory.store_relaxed(clocks, index, place.layout.size)
1506 } else {
1507 memory.store_release(clocks, index, place.layout.size)
1508 }
1509 },
1510 )
1511 }
1512
1513 fn validate_atomic_rmw(
1516 &mut self,
1517 place: &MPlaceTy<'tcx>,
1518 atomic: AtomicRwOrd,
1519 ) -> InterpResult<'tcx> {
1520 use AtomicRwOrd::*;
1521 let acquire = matches!(atomic, Acquire | AcqRel | SeqCst);
1522 let release = matches!(atomic, Release | AcqRel | SeqCst);
1523 let this = self.eval_context_mut();
1524 this.validate_atomic_op(
1525 place,
1526 atomic,
1527 AccessType::AtomicRmw,
1528 move |memory, clocks, index, _| {
1529 if acquire {
1530 memory.load_acquire(clocks, index, place.layout.size, None)?;
1531 } else {
1532 memory.load_relaxed(clocks, index, place.layout.size, None)?;
1533 }
1534 if release {
1535 memory.rmw_release(clocks, index, place.layout.size)
1536 } else {
1537 memory.rmw_relaxed(clocks, index, place.layout.size)
1538 }
1539 },
1540 )
1541 }
1542
1543 fn get_latest_nonatomic_val(&self, place: &MPlaceTy<'tcx>) -> Result<Option<Scalar>, ()> {
1547 let this = self.eval_context_ref();
1548 let (alloc_id, offset, _prov) = this.ptr_get_alloc_id(place.ptr(), 0).unwrap();
1550 let alloc_meta = &this.get_alloc_extra(alloc_id).unwrap().data_race;
1551 if alloc_meta.as_weak_memory_ref().is_none() {
1552 return Err(());
1554 }
1555 let data_race = alloc_meta.as_vclocks_ref().unwrap();
1556 for (_range, clocks) in data_race.alloc_ranges.borrow_mut().iter(offset, place.layout.size)
1558 {
1559 if clocks.atomic().is_some_and(|atomic| !(atomic.write_vector <= clocks.write())) {
1563 return Err(());
1564 }
1565 }
1566 Ok(this.run_for_validation_ref(|this| this.read_scalar(place)).discard_err())
1570 }
1571
1572 fn validate_atomic_op<A: Debug + Copy>(
1574 &self,
1575 place: &MPlaceTy<'tcx>,
1576 atomic: A,
1577 access: AccessType,
1578 mut op: impl FnMut(
1579 &mut MemoryCellClocks,
1580 &mut ThreadClockSet,
1581 VectorIdx,
1582 A,
1583 ) -> Result<(), DataRace>,
1584 ) -> InterpResult<'tcx> {
1585 let this = self.eval_context_ref();
1586 assert!(access.is_atomic());
1587 let Some(data_race) = this.machine.data_race.as_vclocks_ref() else {
1588 return interp_ok(());
1589 };
1590 if !data_race.race_detecting() {
1591 return interp_ok(());
1592 }
1593 let size = place.layout.size;
1594 let (alloc_id, base_offset, _prov) = this.ptr_get_alloc_id(place.ptr(), 0)?;
1595 let alloc_meta = this.get_alloc_extra(alloc_id)?.data_race.as_vclocks_ref().unwrap();
1598 trace!(
1599 "Atomic op({}) with ordering {:?} on {:?} (size={})",
1600 access.description(None, None),
1601 &atomic,
1602 place.ptr(),
1603 size.bytes()
1604 );
1605
1606 let current_span = this.machine.current_user_relevant_span();
1607 data_race.maybe_perform_sync_operation(
1609 &this.machine.threads,
1610 current_span,
1611 |index, mut thread_clocks| {
1612 for (mem_clocks_range, mem_clocks) in
1613 alloc_meta.alloc_ranges.borrow_mut().iter_mut(base_offset, size)
1614 {
1615 if let Err(DataRace) = op(mem_clocks, &mut thread_clocks, index, atomic) {
1616 mem::drop(thread_clocks);
1617 return VClockAlloc::report_data_race(
1618 data_race,
1619 &this.machine.threads,
1620 mem_clocks,
1621 access,
1622 place.layout.size,
1623 interpret::Pointer::new(
1624 alloc_id,
1625 Size::from_bytes(mem_clocks_range.start),
1626 ),
1627 None,
1628 )
1629 .map(|_| true);
1630 }
1631 }
1632
1633 interp_ok(true)
1635 },
1636 )?;
1637
1638 if tracing::enabled!(tracing::Level::TRACE) {
1640 for (_offset, mem_clocks) in alloc_meta.alloc_ranges.borrow().iter(base_offset, size) {
1641 trace!(
1642 "Updated atomic memory({:?}, size={}) to {:#?}",
1643 place.ptr(),
1644 size.bytes(),
1645 mem_clocks.atomic_ops
1646 );
1647 }
1648 }
1649
1650 interp_ok(())
1651 }
1652}
1653
1654impl GlobalState {
1655 pub fn new(config: &MiriConfig) -> Self {
1658 let mut global_state = GlobalState {
1659 multi_threaded: Cell::new(false),
1660 ongoing_action_data_race_free: Cell::new(false),
1661 vector_clocks: RefCell::new(IndexVec::new()),
1662 vector_info: RefCell::new(IndexVec::new()),
1663 thread_info: RefCell::new(IndexVec::new()),
1664 reuse_candidates: RefCell::new(FxHashSet::default()),
1665 last_sc_fence: RefCell::new(VClock::default()),
1666 last_sc_write_per_thread: RefCell::new(VClock::default()),
1667 track_outdated_loads: config.track_outdated_loads,
1668 weak_memory: config.weak_memory_emulation,
1669 };
1670
1671 let index = global_state.vector_clocks.get_mut().push(ThreadClockSet::default());
1674 global_state.vector_info.get_mut().push(ThreadId::MAIN_THREAD);
1675 global_state
1676 .thread_info
1677 .get_mut()
1678 .push(ThreadExtraState { vector_index: Some(index), termination_vector_clock: None });
1679
1680 global_state
1681 }
1682
1683 fn race_detecting(&self) -> bool {
1687 self.multi_threaded.get() && !self.ongoing_action_data_race_free.get()
1688 }
1689
1690 pub fn ongoing_action_data_race_free(&self) -> bool {
1691 self.ongoing_action_data_race_free.get()
1692 }
1693
1694 fn find_vector_index_reuse_candidate(&self) -> Option<VectorIdx> {
1697 let mut reuse = self.reuse_candidates.borrow_mut();
1698 let vector_clocks = self.vector_clocks.borrow();
1699 for &candidate in reuse.iter() {
1700 let target_timestamp = vector_clocks[candidate].clock[candidate];
1701 if vector_clocks.iter_enumerated().all(|(clock_idx, clock)| {
1702 let no_data_race = clock.clock[candidate] >= target_timestamp;
1705
1706 let vector_terminated = reuse.contains(&clock_idx);
1709
1710 no_data_race || vector_terminated
1713 }) {
1714 assert!(reuse.remove(&candidate));
1719 return Some(candidate);
1720 }
1721 }
1722 None
1723 }
1724
1725 #[inline]
1728 pub fn thread_created(
1729 &mut self,
1730 thread_mgr: &ThreadManager<'_>,
1731 thread: ThreadId,
1732 current_span: Span,
1733 ) {
1734 let current_index = self.active_thread_index(thread_mgr);
1735
1736 self.multi_threaded.set(true);
1739
1740 let mut thread_info = self.thread_info.borrow_mut();
1742 thread_info.ensure_contains_elem(thread, Default::default);
1743
1744 let created_index = if let Some(reuse_index) = self.find_vector_index_reuse_candidate() {
1747 let vector_clocks = self.vector_clocks.get_mut();
1750 vector_clocks[reuse_index].increment_clock(reuse_index, current_span);
1751
1752 let vector_info = self.vector_info.get_mut();
1755 let old_thread = vector_info[reuse_index];
1756 vector_info[reuse_index] = thread;
1757
1758 thread_info[old_thread].vector_index = None;
1761
1762 reuse_index
1763 } else {
1764 let vector_info = self.vector_info.get_mut();
1767 vector_info.push(thread)
1768 };
1769
1770 trace!("Creating thread = {:?} with vector index = {:?}", thread, created_index);
1771
1772 thread_info[thread].vector_index = Some(created_index);
1774
1775 let vector_clocks = self.vector_clocks.get_mut();
1777 if created_index == vector_clocks.next_index() {
1778 vector_clocks.push(ThreadClockSet::default());
1779 }
1780
1781 let (current, created) = vector_clocks.pick2_mut(current_index, created_index);
1783
1784 created.join_with(current);
1787
1788 current.increment_clock(current_index, current_span);
1791 created.increment_clock(created_index, current_span);
1792 }
1793
1794 #[inline]
1798 pub fn thread_joined(&mut self, threads: &ThreadManager<'_>, joinee: ThreadId) {
1799 let thread_info = self.thread_info.borrow();
1800 let thread_info = &thread_info[joinee];
1801
1802 let join_clock = thread_info
1804 .termination_vector_clock
1805 .as_ref()
1806 .expect("joined with thread but thread has not terminated");
1807 self.acquire_clock(join_clock, threads);
1809
1810 if let Some(current_index) = thread_info.vector_index {
1815 if threads.get_live_thread_count() == 1 {
1816 let vector_clocks = self.vector_clocks.get_mut();
1817 let current_clock = &vector_clocks[current_index];
1819 if vector_clocks
1820 .iter_enumerated()
1821 .all(|(idx, clocks)| clocks.clock[idx] <= current_clock.clock[idx])
1822 {
1823 self.multi_threaded.set(false);
1827 }
1828 }
1829 }
1830 }
1831
1832 #[inline]
1840 pub fn thread_terminated(&mut self, thread_mgr: &ThreadManager<'_>) {
1841 let current_thread = thread_mgr.active_thread();
1842 let current_index = self.active_thread_index(thread_mgr);
1843
1844 let terminaion_clock = self.release_clock(thread_mgr, |clock| clock.clone());
1846 self.thread_info.get_mut()[current_thread].termination_vector_clock =
1847 Some(terminaion_clock);
1848
1849 let reuse = self.reuse_candidates.get_mut();
1851 reuse.insert(current_index);
1852 }
1853
1854 fn atomic_fence<'tcx>(
1856 &self,
1857 machine: &MiriMachine<'tcx>,
1858 atomic: AtomicFenceOrd,
1859 ) -> InterpResult<'tcx> {
1860 let current_span = machine.current_user_relevant_span();
1861 self.maybe_perform_sync_operation(&machine.threads, current_span, |index, mut clocks| {
1862 trace!("Atomic fence on {:?} with ordering {:?}", index, atomic);
1863
1864 if atomic != AtomicFenceOrd::Release {
1868 clocks.apply_acquire_fence();
1870 }
1871 if atomic == AtomicFenceOrd::SeqCst {
1872 let mut sc_fence_clock = self.last_sc_fence.borrow_mut();
1880 sc_fence_clock.join(&clocks.clock);
1881 clocks.clock.join(&sc_fence_clock);
1882 clocks.write_seqcst.join(&self.last_sc_write_per_thread.borrow());
1885 }
1886 if atomic != AtomicFenceOrd::Acquire {
1889 clocks.apply_release_fence();
1891 }
1892
1893 interp_ok(atomic != AtomicFenceOrd::Acquire)
1895 })
1896 }
1897
1898 fn maybe_perform_sync_operation<'tcx>(
1906 &self,
1907 thread_mgr: &ThreadManager<'_>,
1908 current_span: Span,
1909 op: impl FnOnce(VectorIdx, RefMut<'_, ThreadClockSet>) -> InterpResult<'tcx, bool>,
1910 ) -> InterpResult<'tcx> {
1911 if self.multi_threaded.get() {
1912 let (index, clocks) = self.active_thread_state_mut(thread_mgr);
1913 if op(index, clocks)? {
1914 let (_, mut clocks) = self.active_thread_state_mut(thread_mgr);
1915 clocks.increment_clock(index, current_span);
1916 }
1917 }
1918 interp_ok(())
1919 }
1920
1921 fn print_thread_metadata(&self, thread_mgr: &ThreadManager<'_>, vector: VectorIdx) -> String {
1924 let thread = self.vector_info.borrow()[vector];
1925 let thread_name = thread_mgr.get_thread_display_name(thread);
1926 format!("thread `{thread_name}`")
1927 }
1928
1929 pub fn acquire_clock<'tcx>(&self, clock: &VClock, threads: &ThreadManager<'tcx>) {
1934 let thread = threads.active_thread();
1935 let (_, mut clocks) = self.thread_state_mut(thread);
1936 clocks.clock.join(clock);
1937 }
1938
1939 pub fn release_clock<'tcx, R>(
1943 &self,
1944 threads: &ThreadManager<'tcx>,
1945 callback: impl FnOnce(&VClock) -> R,
1946 ) -> R {
1947 let thread = threads.active_thread();
1948 let span = threads.active_thread_ref().current_user_relevant_span();
1949 let (index, mut clocks) = self.thread_state_mut(thread);
1950 let r = callback(&clocks.clock);
1951 clocks.increment_clock(index, span);
1954
1955 r
1956 }
1957
1958 fn thread_index(&self, thread: ThreadId) -> VectorIdx {
1959 self.thread_info.borrow()[thread].vector_index.expect("thread has no assigned vector")
1960 }
1961
1962 #[inline]
1965 fn thread_state_mut(&self, thread: ThreadId) -> (VectorIdx, RefMut<'_, ThreadClockSet>) {
1966 let index = self.thread_index(thread);
1967 let ref_vector = self.vector_clocks.borrow_mut();
1968 let clocks = RefMut::map(ref_vector, |vec| &mut vec[index]);
1969 (index, clocks)
1970 }
1971
1972 #[inline]
1975 fn thread_state(&self, thread: ThreadId) -> (VectorIdx, Ref<'_, ThreadClockSet>) {
1976 let index = self.thread_index(thread);
1977 let ref_vector = self.vector_clocks.borrow();
1978 let clocks = Ref::map(ref_vector, |vec| &vec[index]);
1979 (index, clocks)
1980 }
1981
1982 #[inline]
1985 pub(super) fn active_thread_state(
1986 &self,
1987 thread_mgr: &ThreadManager<'_>,
1988 ) -> (VectorIdx, Ref<'_, ThreadClockSet>) {
1989 self.thread_state(thread_mgr.active_thread())
1990 }
1991
1992 #[inline]
1995 pub(super) fn active_thread_state_mut(
1996 &self,
1997 thread_mgr: &ThreadManager<'_>,
1998 ) -> (VectorIdx, RefMut<'_, ThreadClockSet>) {
1999 self.thread_state_mut(thread_mgr.active_thread())
2000 }
2001
2002 #[inline]
2005 fn active_thread_index(&self, thread_mgr: &ThreadManager<'_>) -> VectorIdx {
2006 let active_thread_id = thread_mgr.active_thread();
2007 self.thread_index(active_thread_id)
2008 }
2009
2010 pub(super) fn sc_write(&self, thread_mgr: &ThreadManager<'_>) {
2012 let (index, clocks) = self.active_thread_state(thread_mgr);
2013 self.last_sc_write_per_thread.borrow_mut().set_at_index(&clocks.clock, index);
2014 }
2015
2016 pub(super) fn sc_read(&self, thread_mgr: &ThreadManager<'_>) {
2018 let (.., mut clocks) = self.active_thread_state_mut(thread_mgr);
2019 clocks.read_seqcst.join(&self.last_sc_fence.borrow());
2020 }
2021}