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::{AtomicOrdering, 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::*;
62
63pub type AllocState = VClockAlloc;
64
65#[derive(Copy, Clone, PartialEq, Eq, Debug)]
67pub enum AtomicRwOrd {
68 Relaxed,
69 Acquire,
70 Release,
71 AcqRel,
72 SeqCst,
73}
74
75impl AtomicRwOrd {
76 pub fn from(ordering: AtomicOrdering) -> Self {
77 use AtomicRwOrd::*;
78 match ordering {
79 AtomicOrdering::Relaxed => Relaxed,
80 AtomicOrdering::Release => Release,
81 AtomicOrdering::Acquire => Acquire,
82 AtomicOrdering::AcqRel => AcqRel,
83 AtomicOrdering::SeqCst => SeqCst,
84 }
85 }
86}
87
88#[derive(Copy, Clone, PartialEq, Eq, Debug)]
90pub enum AtomicReadOrd {
91 Relaxed,
92 Acquire,
93 SeqCst,
94}
95
96impl AtomicReadOrd {
97 pub fn from(ordering: AtomicOrdering) -> Self {
98 use AtomicReadOrd::*;
99 match ordering {
100 AtomicOrdering::Relaxed => Relaxed,
101 AtomicOrdering::Acquire => Acquire,
102 AtomicOrdering::SeqCst => SeqCst,
103 _ => panic!("invalid atomic read ordering: {ordering:?}"),
104 }
105 }
106}
107
108#[derive(Copy, Clone, PartialEq, Eq, Debug)]
110pub enum AtomicWriteOrd {
111 Relaxed,
112 Release,
113 SeqCst,
114}
115
116impl AtomicWriteOrd {
117 pub fn from(ordering: AtomicOrdering) -> Self {
118 use AtomicWriteOrd::*;
119 match ordering {
120 AtomicOrdering::Relaxed => Relaxed,
121 AtomicOrdering::Release => Release,
122 AtomicOrdering::SeqCst => SeqCst,
123 _ => panic!("invalid atomic write ordering: {ordering:?}"),
124 }
125 }
126}
127
128#[derive(Copy, Clone, PartialEq, Eq, Debug)]
130pub enum AtomicFenceOrd {
131 Acquire,
132 Release,
133 AcqRel,
134 SeqCst,
135}
136
137impl AtomicFenceOrd {
138 pub fn from(ordering: AtomicOrdering) -> Self {
139 use AtomicFenceOrd::*;
140 match ordering {
141 AtomicOrdering::Acquire => Acquire,
142 AtomicOrdering::Release => Release,
143 AtomicOrdering::SeqCst => SeqCst,
144 AtomicOrdering::AcqRel => AcqRel,
145 _ => panic!("invalid atomic fence ordering: {ordering:?}"),
146 }
147 }
148}
149
150#[derive(Clone, Default, Debug)]
154pub(super) struct ThreadClockSet {
155 pub(super) clock: VClock,
158
159 fence_acquire: VClock,
162
163 fence_release: VClock,
166
167 pub(super) write_seqcst: VClock,
172
173 pub(super) read_seqcst: VClock,
178}
179
180impl ThreadClockSet {
181 #[inline]
184 fn apply_release_fence(&mut self) {
185 self.fence_release.clone_from(&self.clock);
186 }
187
188 #[inline]
191 fn apply_acquire_fence(&mut self) {
192 self.clock.join(&self.fence_acquire);
193 }
194
195 #[inline]
198 fn increment_clock(&mut self, index: VectorIdx, current_span: Span) {
199 self.clock.increment_index(index, current_span);
200 }
201
202 fn join_with(&mut self, other: &ThreadClockSet) {
206 self.clock.join(&other.clock);
207 }
208}
209
210#[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
213pub struct DataRace;
214
215#[derive(Clone, PartialEq, Eq, Debug)]
220struct AtomicMemoryCellClocks {
221 read_vector: VClock,
226
227 write_vector: VClock,
232
233 sync_vector: VClock,
241
242 size: Option<Size>,
247}
248
249#[derive(Copy, Clone, PartialEq, Eq, Debug)]
250enum AtomicAccessType {
251 Load(AtomicReadOrd),
252 Store,
253 Rmw,
254}
255
256#[derive(Copy, Clone, PartialEq, Eq, Debug)]
258pub enum NaReadType {
259 Read,
261
262 Retag,
264}
265
266impl NaReadType {
267 fn description(self) -> &'static str {
268 match self {
269 NaReadType::Read => "non-atomic read",
270 NaReadType::Retag => "retag read",
271 }
272 }
273}
274
275#[derive(Copy, Clone, PartialEq, Eq, Debug)]
278pub enum NaWriteType {
279 Allocate,
281
282 Write,
284
285 Retag,
287
288 Deallocate,
293}
294
295impl NaWriteType {
296 fn description(self) -> &'static str {
297 match self {
298 NaWriteType::Allocate => "creating a new allocation",
299 NaWriteType::Write => "non-atomic write",
300 NaWriteType::Retag => "retag write",
301 NaWriteType::Deallocate => "deallocation",
302 }
303 }
304}
305
306#[derive(Copy, Clone, PartialEq, Eq, Debug)]
307enum AccessType {
308 NaRead(NaReadType),
309 NaWrite(NaWriteType),
310 AtomicLoad,
311 AtomicStore,
312 AtomicRmw,
313}
314
315#[derive(Clone, PartialEq, Eq, Debug)]
317struct MemoryCellClocks {
318 write: (VectorIdx, VTimestamp),
322
323 write_type: NaWriteType,
327
328 read: VClock,
332
333 atomic_ops: Option<Box<AtomicMemoryCellClocks>>,
337}
338
339#[derive(Debug, Clone, Default)]
341struct ThreadExtraState {
342 vector_index: Option<VectorIdx>,
348
349 termination_vector_clock: Option<VClock>,
354}
355
356#[derive(Debug, Clone)]
361pub struct GlobalState {
362 multi_threaded: Cell<bool>,
369
370 ongoing_action_data_race_free: Cell<bool>,
374
375 vector_clocks: RefCell<IndexVec<VectorIdx, ThreadClockSet>>,
379
380 vector_info: RefCell<IndexVec<VectorIdx, ThreadId>>,
384
385 thread_info: RefCell<IndexVec<ThreadId, ThreadExtraState>>,
387
388 reuse_candidates: RefCell<FxHashSet<VectorIdx>>,
396
397 last_sc_fence: RefCell<VClock>,
400
401 last_sc_write_per_thread: RefCell<VClock>,
404
405 pub track_outdated_loads: bool,
407
408 pub weak_memory: bool,
410}
411
412impl VisitProvenance for GlobalState {
413 fn visit_provenance(&self, _visit: &mut VisitWith<'_>) {
414 }
416}
417
418impl AccessType {
419 fn description(self, ty: Option<Ty<'_>>, size: Option<Size>) -> String {
420 let mut msg = String::new();
421
422 if let Some(size) = size {
423 if size == Size::ZERO {
424 assert!(self == AccessType::AtomicLoad);
428 assert!(ty.is_none());
429 return format!("multiple differently-sized atomic loads, including one load");
430 }
431 msg.push_str(&format!("{}-byte {}", size.bytes(), msg))
432 }
433
434 msg.push_str(match self {
435 AccessType::NaRead(w) => w.description(),
436 AccessType::NaWrite(w) => w.description(),
437 AccessType::AtomicLoad => "atomic load",
438 AccessType::AtomicStore => "atomic store",
439 AccessType::AtomicRmw => "atomic read-modify-write",
440 });
441
442 if let Some(ty) = ty {
443 msg.push_str(&format!(" of type `{ty}`"));
444 }
445
446 msg
447 }
448
449 fn is_atomic(self) -> bool {
450 match self {
451 AccessType::AtomicLoad | AccessType::AtomicStore | AccessType::AtomicRmw => true,
452 AccessType::NaRead(_) | AccessType::NaWrite(_) => false,
453 }
454 }
455
456 fn is_read(self) -> bool {
457 match self {
458 AccessType::AtomicLoad | AccessType::NaRead(_) => true,
459 AccessType::NaWrite(_) | AccessType::AtomicStore | AccessType::AtomicRmw => false,
460 }
461 }
462
463 fn is_retag(self) -> bool {
464 matches!(
465 self,
466 AccessType::NaRead(NaReadType::Retag) | AccessType::NaWrite(NaWriteType::Retag)
467 )
468 }
469}
470
471impl AtomicMemoryCellClocks {
472 fn new(size: Size) -> Self {
473 AtomicMemoryCellClocks {
474 read_vector: Default::default(),
475 write_vector: Default::default(),
476 sync_vector: Default::default(),
477 size: Some(size),
478 }
479 }
480}
481
482impl MemoryCellClocks {
483 fn new(alloc: VTimestamp, alloc_index: VectorIdx) -> Self {
486 MemoryCellClocks {
487 read: VClock::default(),
488 write: (alloc_index, alloc),
489 write_type: NaWriteType::Allocate,
490 atomic_ops: None,
491 }
492 }
493
494 #[inline]
495 fn write_was_before(&self, other: &VClock) -> bool {
496 self.write.1 <= other[self.write.0]
499 }
500
501 #[inline]
502 fn write(&self) -> VClock {
503 VClock::new_with_index(self.write.0, self.write.1)
504 }
505
506 #[inline]
508 fn atomic(&self) -> Option<&AtomicMemoryCellClocks> {
509 self.atomic_ops.as_deref()
510 }
511
512 #[inline]
514 fn atomic_mut_unwrap(&mut self) -> &mut AtomicMemoryCellClocks {
515 self.atomic_ops.as_deref_mut().unwrap()
516 }
517
518 fn atomic_access(
521 &mut self,
522 thread_clocks: &ThreadClockSet,
523 size: Size,
524 write: bool,
525 ) -> Result<&mut AtomicMemoryCellClocks, DataRace> {
526 match self.atomic_ops {
527 Some(ref mut atomic) => {
528 if atomic.size == Some(size) {
530 Ok(atomic)
531 } else if atomic.read_vector <= thread_clocks.clock
532 && atomic.write_vector <= thread_clocks.clock
533 {
534 atomic.size = Some(size);
536 Ok(atomic)
537 } else if !write && atomic.write_vector <= thread_clocks.clock {
538 atomic.size = None;
541 Ok(atomic)
542 } else {
543 Err(DataRace)
544 }
545 }
546 None => {
547 self.atomic_ops = Some(Box::new(AtomicMemoryCellClocks::new(size)));
548 Ok(self.atomic_ops.as_mut().unwrap())
549 }
550 }
551 }
552
553 fn load_acquire(
557 &mut self,
558 thread_clocks: &mut ThreadClockSet,
559 index: VectorIdx,
560 access_size: Size,
561 sync_clock: Option<&VClock>,
562 ) -> Result<(), DataRace> {
563 self.atomic_read_detect(thread_clocks, index, access_size)?;
564 if let Some(sync_clock) = sync_clock.or_else(|| self.atomic().map(|a| &a.sync_vector)) {
565 thread_clocks.clock.join(sync_clock);
566 }
567 Ok(())
568 }
569
570 fn load_relaxed(
574 &mut self,
575 thread_clocks: &mut ThreadClockSet,
576 index: VectorIdx,
577 access_size: Size,
578 sync_clock: Option<&VClock>,
579 ) -> Result<(), DataRace> {
580 self.atomic_read_detect(thread_clocks, index, access_size)?;
581 if let Some(sync_clock) = sync_clock.or_else(|| self.atomic().map(|a| &a.sync_vector)) {
582 thread_clocks.fence_acquire.join(sync_clock);
583 }
584 Ok(())
585 }
586
587 fn store_release(
590 &mut self,
591 thread_clocks: &ThreadClockSet,
592 index: VectorIdx,
593 access_size: Size,
594 ) -> Result<(), DataRace> {
595 self.atomic_write_detect(thread_clocks, index, access_size)?;
596 let atomic = self.atomic_mut_unwrap(); atomic.sync_vector.clone_from(&thread_clocks.clock);
598 Ok(())
599 }
600
601 fn store_relaxed(
604 &mut self,
605 thread_clocks: &ThreadClockSet,
606 index: VectorIdx,
607 access_size: Size,
608 ) -> Result<(), DataRace> {
609 self.atomic_write_detect(thread_clocks, index, access_size)?;
610
611 let atomic = self.atomic_mut_unwrap();
617 atomic.sync_vector.clone_from(&thread_clocks.fence_release);
618 Ok(())
619 }
620
621 fn rmw_release(
624 &mut self,
625 thread_clocks: &ThreadClockSet,
626 index: VectorIdx,
627 access_size: Size,
628 ) -> Result<(), DataRace> {
629 self.atomic_write_detect(thread_clocks, index, access_size)?;
630 let atomic = self.atomic_mut_unwrap();
631 atomic.sync_vector.join(&thread_clocks.clock);
634 Ok(())
635 }
636
637 fn rmw_relaxed(
640 &mut self,
641 thread_clocks: &ThreadClockSet,
642 index: VectorIdx,
643 access_size: Size,
644 ) -> Result<(), DataRace> {
645 self.atomic_write_detect(thread_clocks, index, access_size)?;
646 let atomic = self.atomic_mut_unwrap();
647 atomic.sync_vector.join(&thread_clocks.fence_release);
650 Ok(())
651 }
652
653 fn atomic_read_detect(
656 &mut self,
657 thread_clocks: &ThreadClockSet,
658 index: VectorIdx,
659 access_size: Size,
660 ) -> Result<(), DataRace> {
661 trace!("Atomic read with vectors: {:#?} :: {:#?}", self, thread_clocks);
662 let atomic = self.atomic_access(thread_clocks, access_size, false)?;
663 atomic.read_vector.set_at_index(&thread_clocks.clock, index);
664 if self.write_was_before(&thread_clocks.clock) { Ok(()) } else { Err(DataRace) }
666 }
667
668 fn atomic_write_detect(
671 &mut self,
672 thread_clocks: &ThreadClockSet,
673 index: VectorIdx,
674 access_size: Size,
675 ) -> Result<(), DataRace> {
676 trace!("Atomic write with vectors: {:#?} :: {:#?}", self, thread_clocks);
677 let atomic = self.atomic_access(thread_clocks, access_size, true)?;
678 atomic.write_vector.set_at_index(&thread_clocks.clock, index);
679 if self.write_was_before(&thread_clocks.clock) && self.read <= thread_clocks.clock {
681 Ok(())
682 } else {
683 Err(DataRace)
684 }
685 }
686
687 fn non_atomic_read_detect(
690 &mut self,
691 thread_clocks: &mut ThreadClockSet,
692 index: VectorIdx,
693 read_type: NaReadType,
694 current_span: Span,
695 ) -> Result<(), DataRace> {
696 trace!("Unsynchronized read with vectors: {:#?} :: {:#?}", self, thread_clocks);
697 if !current_span.is_dummy() {
698 thread_clocks.clock.index_mut(index).span = current_span;
699 }
700 thread_clocks.clock.index_mut(index).set_read_type(read_type);
701 if !self.write_was_before(&thread_clocks.clock) {
703 return Err(DataRace);
704 }
705 if !self.atomic().is_none_or(|atomic| atomic.write_vector <= thread_clocks.clock) {
707 return Err(DataRace);
708 }
709 self.read.set_at_index(&thread_clocks.clock, index);
711 Ok(())
712 }
713
714 fn non_atomic_write_detect(
717 &mut self,
718 thread_clocks: &mut ThreadClockSet,
719 index: VectorIdx,
720 write_type: NaWriteType,
721 current_span: Span,
722 ) -> Result<(), DataRace> {
723 trace!("Unsynchronized write with vectors: {:#?} :: {:#?}", self, thread_clocks);
724 if !current_span.is_dummy() {
725 thread_clocks.clock.index_mut(index).span = current_span;
726 }
727 if !(self.write_was_before(&thread_clocks.clock) && self.read <= thread_clocks.clock) {
729 return Err(DataRace);
730 }
731 if !self.atomic().is_none_or(|atomic| {
733 atomic.write_vector <= thread_clocks.clock && atomic.read_vector <= thread_clocks.clock
734 }) {
735 return Err(DataRace);
736 }
737 self.write = (index, thread_clocks.clock[index]);
739 self.write_type = write_type;
740 self.read.set_zero_vector();
741 self.atomic_ops = None;
743 Ok(())
744 }
745}
746
747impl GlobalDataRaceHandler {
748 fn set_ongoing_action_data_race_free(&self, enable: bool) {
751 match self {
752 GlobalDataRaceHandler::None => {}
753 GlobalDataRaceHandler::Vclocks(data_race) => {
754 let old = data_race.ongoing_action_data_race_free.replace(enable);
755 assert_ne!(old, enable, "cannot nest allow_data_races");
756 }
757 GlobalDataRaceHandler::Genmc(genmc_ctx) => {
758 genmc_ctx.set_ongoing_action_data_race_free(enable);
759 }
760 }
761 }
762}
763
764impl<'tcx> EvalContextExt<'tcx> for MiriInterpCx<'tcx> {}
766pub trait EvalContextExt<'tcx>: MiriInterpCxExt<'tcx> {
767 fn read_scalar_atomic(
769 &self,
770 place: &MPlaceTy<'tcx>,
771 atomic: AtomicReadOrd,
772 ) -> InterpResult<'tcx, Scalar> {
773 let this = self.eval_context_ref();
774 this.atomic_access_check(place, AtomicAccessType::Load(atomic))?;
775 if let Some(genmc_ctx) = this.machine.data_race.as_genmc_ref() {
782 let old_val = this.run_for_validation_ref(|this| this.read_scalar(place)).discard_err();
783 return genmc_ctx.atomic_load(
784 this,
785 place.ptr().addr(),
786 place.layout.size,
787 atomic,
788 old_val,
789 );
790 }
791
792 trace!("read_scalar_atomic({:?}, {} bytes)", place.ptr(), place.layout.size.bytes());
793
794 let scalar = this.allow_data_races_ref(move |this| this.read_scalar(place))?;
795 let buffered_scalar = this.buffered_atomic_read(place, atomic, scalar, |sync_clock| {
796 this.validate_atomic_load(place, atomic, sync_clock)
797 })?;
798 interp_ok(buffered_scalar.ok_or_else(|| err_ub!(InvalidUninitBytes(None)))?)
799 }
800
801 fn write_scalar_atomic(
803 &mut self,
804 val: Scalar,
805 dest: &MPlaceTy<'tcx>,
806 atomic: AtomicWriteOrd,
807 ) -> InterpResult<'tcx> {
808 let this = self.eval_context_mut();
809 this.atomic_access_check(dest, AtomicAccessType::Store)?;
810
811 let old_val = this.run_for_validation_ref(|this| this.read_scalar(dest)).discard_err();
815
816 if let Some(genmc_ctx) = this.machine.data_race.as_genmc_ref() {
818 if genmc_ctx.atomic_store(
819 this,
820 dest.ptr().addr(),
821 dest.layout.size,
822 val,
823 old_val,
824 atomic,
825 )? {
826 this.allow_data_races_mut(|this| this.write_scalar(val, dest))?;
829 }
830 return interp_ok(());
831 }
832
833 trace!("write_scalar_atomic({:?}, {} bytes)", dest.ptr(), dest.layout.size.bytes());
834
835 this.allow_data_races_mut(move |this| this.write_scalar(val, dest))?;
836 this.validate_atomic_store(dest, atomic)?;
837 this.buffered_atomic_write(val, dest, atomic, old_val)
838 }
839
840 fn atomic_rmw(
842 &mut self,
843 place: &MPlaceTy<'tcx>,
844 rhs: &ImmTy<'tcx>,
845 atomic_op: AtomicRmwOp,
846 ord: AtomicRwOrd,
847 ) -> InterpResult<'tcx, Scalar> {
848 let this = self.eval_context_mut();
849 this.atomic_access_check(place, AtomicAccessType::Rmw)?;
850
851 let old = this.allow_data_races_mut(|this| this.read_immediate(place))?;
852
853 if let Some(genmc_ctx) = this.machine.data_race.as_genmc_ref() {
855 let (old_val, new_val) = genmc_ctx.atomic_rmw(
856 this,
857 place.ptr().addr(),
858 place.layout.size,
859 atomic_op,
860 place.layout.backend_repr.is_signed(),
861 ord,
862 rhs.to_scalar(),
863 old.to_scalar(),
864 )?;
865 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 trace!("atomic_rmw({:?}, {} bytes)", place.ptr(), place.layout.size.bytes());
872
873 let val = this.atomic_rmw_op(atomic_op, &old, rhs)?;
874
875 this.allow_data_races_mut(|this| this.write_immediate(*val, place))?;
876 this.validate_atomic_rmw(place, ord)?;
877 this.buffered_atomic_rmw(val.to_scalar(), place, ord, old.to_scalar())?;
878 interp_ok(old.to_scalar())
879 }
880
881 fn atomic_compare_exchange(
888 &mut self,
889 place: &MPlaceTy<'tcx>,
890 expect_old: &ImmTy<'tcx>,
891 new: Scalar,
892 success: AtomicRwOrd,
893 fail: AtomicReadOrd,
894 can_fail_spuriously: bool,
895 ) -> InterpResult<'tcx, (Scalar, bool)> {
896 let this = self.eval_context_mut();
897 this.atomic_access_check(place, AtomicAccessType::Rmw)?;
898
899 let old = this.allow_data_races_mut(|this| this.read_immediate(place))?;
901
902 if let Some(genmc_ctx) = this.machine.data_race.as_genmc_ref() {
904 let (old_value, new_value, cmpxchg_success) = genmc_ctx.atomic_compare_exchange(
905 this,
906 place.ptr().addr(),
907 place.layout.size,
908 this.read_scalar(expect_old)?,
909 new,
910 success,
911 fail,
912 can_fail_spuriously,
913 old.to_scalar(),
914 )?;
915 if let Some(new_value) = new_value {
918 this.allow_data_races_mut(|this| this.write_scalar(new_value, place))?;
919 }
920 return interp_ok((old_value, cmpxchg_success));
921 }
922
923 let eq = this.binary_op(mir::BinOp::Eq, &old, expect_old)?;
925 let success_rate = 1.0 - this.machine.cmpxchg_weak_failure_rate;
928 let cmpxchg_success = eq.to_scalar().to_bool()?
929 && if can_fail_spuriously {
930 this.machine.rng.get_mut().random_bool(success_rate)
931 } else {
932 true
933 };
934 let res = (old.to_scalar(), cmpxchg_success);
935
936 trace!(
937 "atomic_compare_exchange_scalar({:?}, {} bytes, success = {})",
938 place.ptr(),
939 place.layout.size.bytes(),
940 cmpxchg_success,
941 );
942
943 if cmpxchg_success {
947 this.allow_data_races_mut(|this| this.write_scalar(new, place))?;
948 this.validate_atomic_rmw(place, success)?;
949 this.buffered_atomic_rmw(new, place, success, old.to_scalar())?;
950 } else {
951 this.validate_atomic_load(place, fail, None)?;
952 this.perform_read_on_buffered_latest(place, fail)?;
957 }
958
959 interp_ok(res)
961 }
962
963 fn atomic_fence(&self, atomic: AtomicFenceOrd) -> InterpResult<'tcx> {
965 let this = self.eval_context_ref();
966 let machine = &this.machine;
967 match &machine.data_race {
968 GlobalDataRaceHandler::None => interp_ok(()),
969 GlobalDataRaceHandler::Vclocks(data_race) => data_race.atomic_fence(machine, atomic),
970 GlobalDataRaceHandler::Genmc(genmc_ctx) => genmc_ctx.atomic_fence(machine, atomic),
971 }
972 }
973
974 fn release_clock<R>(
980 &self,
981 callback: impl FnOnce(&VClock) -> R,
982 ) -> InterpResult<'tcx, Option<R>> {
983 let this = self.eval_context_ref();
984 interp_ok(match &this.machine.data_race {
985 GlobalDataRaceHandler::None => None,
986 GlobalDataRaceHandler::Genmc(_genmc_ctx) =>
987 throw_unsup_format!(
988 "this operation performs synchronization that is not supported in GenMC mode"
989 ),
990 GlobalDataRaceHandler::Vclocks(data_race) =>
991 Some(data_race.release_clock(&this.machine.threads, callback)),
992 })
993 }
994
995 fn acquire_clock(&self, clock: &VClock) -> InterpResult<'tcx> {
998 let this = self.eval_context_ref();
999 match &this.machine.data_race {
1000 GlobalDataRaceHandler::None => {}
1001 GlobalDataRaceHandler::Genmc(_genmc_ctx) =>
1002 throw_unsup_format!(
1003 "this operation performs synchronization that is not supported in GenMC mode"
1004 ),
1005 GlobalDataRaceHandler::Vclocks(data_race) =>
1006 data_race.acquire_clock(clock, &this.machine.threads),
1007 }
1008 interp_ok(())
1009 }
1010}
1011
1012#[derive(Debug, Clone)]
1014pub struct VClockAlloc {
1015 alloc_ranges: RefCell<DedupRangeMap<MemoryCellClocks>>,
1017}
1018
1019impl VisitProvenance for VClockAlloc {
1020 fn visit_provenance(&self, _visit: &mut VisitWith<'_>) {
1021 }
1023}
1024
1025impl VClockAlloc {
1026 pub fn new_allocation(
1028 global: &GlobalState,
1029 thread_mgr: &ThreadManager<'_>,
1030 len: Size,
1031 kind: MemoryKind,
1032 current_span: Span,
1033 ) -> VClockAlloc {
1034 let (alloc_timestamp, alloc_index) = match kind {
1036 MemoryKind::Machine(
1038 MiriMemoryKind::Rust
1039 | MiriMemoryKind::Miri
1040 | MiriMemoryKind::C
1041 | MiriMemoryKind::WinHeap
1042 | MiriMemoryKind::WinLocal
1043 | MiriMemoryKind::Mmap
1044 | MiriMemoryKind::SocketAddress,
1045 )
1046 | MemoryKind::Stack => {
1047 let (alloc_index, clocks) = global.active_thread_state(thread_mgr);
1048 let mut alloc_timestamp = clocks.clock[alloc_index];
1049 alloc_timestamp.span = current_span;
1050 (alloc_timestamp, alloc_index)
1051 }
1052 MemoryKind::Machine(
1055 MiriMemoryKind::Global
1056 | MiriMemoryKind::Machine
1057 | MiriMemoryKind::Runtime
1058 | MiriMemoryKind::ExternStatic
1059 | MiriMemoryKind::Tls,
1060 )
1061 | MemoryKind::CallerLocation =>
1062 (VTimestamp::ZERO, global.thread_index(ThreadId::MAIN_THREAD)),
1063 };
1064 VClockAlloc {
1065 alloc_ranges: RefCell::new(DedupRangeMap::new(
1066 len,
1067 MemoryCellClocks::new(alloc_timestamp, alloc_index),
1068 )),
1069 }
1070 }
1071
1072 fn find_gt_index(l: &VClock, r: &VClock) -> Option<VectorIdx> {
1075 trace!("Find index where not {:?} <= {:?}", l, r);
1076 let l_slice = l.as_slice();
1077 let r_slice = r.as_slice();
1078 l_slice
1079 .iter()
1080 .zip(r_slice.iter())
1081 .enumerate()
1082 .find_map(|(idx, (&l, &r))| if l > r { Some(idx) } else { None })
1083 .or_else(|| {
1084 if l_slice.len() > r_slice.len() {
1085 let l_remainder_slice = &l_slice[r_slice.len()..];
1090 let idx = l_remainder_slice
1091 .iter()
1092 .enumerate()
1093 .find_map(|(idx, &r)| if r == VTimestamp::ZERO { None } else { Some(idx) })
1094 .expect("Invalid VClock Invariant");
1095 Some(idx + r_slice.len())
1096 } else {
1097 None
1098 }
1099 })
1100 .map(VectorIdx::new)
1101 }
1102
1103 #[cold]
1110 #[inline(never)]
1111 fn report_data_race<'tcx>(
1112 global: &GlobalState,
1113 thread_mgr: &ThreadManager<'_>,
1114 mem_clocks: &MemoryCellClocks,
1115 access: AccessType,
1116 access_size: Size,
1117 ptr_dbg: interpret::Pointer<AllocId>,
1118 ty: Option<Ty<'_>>,
1119 ) -> InterpResult<'tcx> {
1120 let (active_index, active_clocks) = global.active_thread_state(thread_mgr);
1121 let mut other_size = None; let write_clock;
1123 let (other_access, other_thread, other_clock) =
1124 if !access.is_atomic() &&
1126 let Some(atomic) = mem_clocks.atomic() &&
1127 let Some(idx) = Self::find_gt_index(&atomic.write_vector, &active_clocks.clock)
1128 {
1129 (AccessType::AtomicStore, idx, &atomic.write_vector)
1130 } else if !access.is_atomic() &&
1131 !access.is_read() &&
1132 let Some(atomic) = mem_clocks.atomic() &&
1133 let Some(idx) = Self::find_gt_index(&atomic.read_vector, &active_clocks.clock)
1134 {
1135 (AccessType::AtomicLoad, idx, &atomic.read_vector)
1136 } else if mem_clocks.write.1 > active_clocks.clock[mem_clocks.write.0] {
1138 write_clock = mem_clocks.write();
1139 (AccessType::NaWrite(mem_clocks.write_type), mem_clocks.write.0, &write_clock)
1140 } else if !access.is_read() && let Some(idx) = Self::find_gt_index(&mem_clocks.read, &active_clocks.clock) {
1141 (AccessType::NaRead(mem_clocks.read[idx].read_type()), idx, &mem_clocks.read)
1142 } else if access.is_atomic() && let Some(atomic) = mem_clocks.atomic() && atomic.size != Some(access_size) {
1144 other_size = Some(atomic.size.unwrap_or(Size::ZERO));
1147 if let Some(idx) = Self::find_gt_index(&atomic.write_vector, &active_clocks.clock)
1148 {
1149 (AccessType::AtomicStore, idx, &atomic.write_vector)
1150 } else if let Some(idx) =
1151 Self::find_gt_index(&atomic.read_vector, &active_clocks.clock)
1152 {
1153 (AccessType::AtomicLoad, idx, &atomic.read_vector)
1154 } else {
1155 unreachable!(
1156 "Failed to report data-race for mixed-size access: no race found"
1157 )
1158 }
1159 } else {
1160 unreachable!("Failed to report data-race")
1161 };
1162
1163 let active_thread_info = global.print_thread_metadata(thread_mgr, active_index);
1165 let other_thread_info = global.print_thread_metadata(thread_mgr, other_thread);
1166 let involves_non_atomic = !access.is_atomic() || !other_access.is_atomic();
1167
1168 let extra = if other_size.is_some() {
1170 assert!(!involves_non_atomic);
1171 Some("overlapping unsynchronized atomic accesses must use the same access size")
1172 } else if access.is_read() && other_access.is_read() {
1173 panic!(
1174 "there should be no same-size read-read races\naccess: {access:?}\nother_access: {other_access:?}"
1175 )
1176 } else {
1177 None
1178 };
1179 Err(err_machine_stop!(TerminationInfo::DataRace {
1180 involves_non_atomic,
1181 extra,
1182 retag_explain: access.is_retag() || other_access.is_retag(),
1183 ptr: ptr_dbg,
1184 op1: RacingOp {
1185 action: other_access.description(None, other_size),
1186 thread_info: other_thread_info,
1187 span: other_clock.as_slice()[other_thread.index()].span_data(),
1188 },
1189 op2: RacingOp {
1190 action: access.description(ty, other_size.map(|_| access_size)),
1191 thread_info: active_thread_info,
1192 span: active_clocks.clock.as_slice()[active_index.index()].span_data(),
1193 },
1194 }))?
1195 }
1196
1197 pub(super) fn sync_clock(&self, access_range: AllocRange) -> VClock {
1199 let alloc_ranges = self.alloc_ranges.borrow();
1200 let mut clock = VClock::default();
1201 for (_, mem_clocks) in alloc_ranges.iter(access_range.start, access_range.size) {
1202 if let Some(atomic) = mem_clocks.atomic() {
1203 clock.join(&atomic.sync_vector);
1204 }
1205 }
1206 clock
1207 }
1208
1209 pub fn read_non_atomic<'tcx>(
1216 &self,
1217 alloc_id: AllocId,
1218 access_range: AllocRange,
1219 read_type: NaReadType,
1220 ty: Option<Ty<'_>>,
1221 machine: &MiriMachine<'_>,
1222 ) -> InterpResult<'tcx> {
1223 let current_span = machine.current_user_relevant_span();
1224 let global = machine.data_race.as_vclocks_ref().unwrap();
1225 if !global.race_detecting() {
1226 return interp_ok(());
1227 }
1228 let (index, mut thread_clocks) = global.active_thread_state_mut(&machine.threads);
1229 let mut alloc_ranges = self.alloc_ranges.borrow_mut();
1230 for (mem_clocks_range, mem_clocks) in
1231 alloc_ranges.iter_mut(access_range.start, access_range.size)
1232 {
1233 if let Err(DataRace) = mem_clocks.non_atomic_read_detect(
1234 &mut thread_clocks,
1235 index,
1236 read_type,
1237 current_span,
1238 ) {
1239 drop(thread_clocks);
1240 return Self::report_data_race(
1242 global,
1243 &machine.threads,
1244 mem_clocks,
1245 AccessType::NaRead(read_type),
1246 access_range.size,
1247 interpret::Pointer::new(alloc_id, Size::from_bytes(mem_clocks_range.start)),
1248 ty,
1249 );
1250 }
1251 }
1252 interp_ok(())
1253 }
1254
1255 pub fn write_non_atomic<'tcx>(
1261 &self,
1262 alloc_id: AllocId,
1263 access_range: AllocRange,
1264 write_type: NaWriteType,
1265 ty: Option<Ty<'_>>,
1266 machine: &MiriMachine<'_>,
1267 ) -> InterpResult<'tcx> {
1268 let current_span = machine.current_user_relevant_span();
1269 let global = machine.data_race.as_vclocks_ref().unwrap();
1270 if !global.race_detecting() {
1271 return interp_ok(());
1272 }
1273 let (index, mut thread_clocks) = global.active_thread_state_mut(&machine.threads);
1274 for (mem_clocks_range, mem_clocks) in
1275 self.alloc_ranges.borrow_mut().iter_mut(access_range.start, access_range.size)
1276 {
1277 if let Err(DataRace) = mem_clocks.non_atomic_write_detect(
1278 &mut thread_clocks,
1279 index,
1280 write_type,
1281 current_span,
1282 ) {
1283 drop(thread_clocks);
1284 return Self::report_data_race(
1286 global,
1287 &machine.threads,
1288 mem_clocks,
1289 AccessType::NaWrite(write_type),
1290 access_range.size,
1291 interpret::Pointer::new(alloc_id, Size::from_bytes(mem_clocks_range.start)),
1292 ty,
1293 );
1294 }
1295 }
1296 interp_ok(())
1297 }
1298}
1299
1300#[derive(Debug, Default)]
1303pub struct FrameState {
1304 local_clocks: RefCell<FxHashMap<mir::Local, LocalClocks>>,
1305}
1306
1307#[derive(Debug)]
1311struct LocalClocks {
1312 write: VTimestamp,
1313 write_type: NaWriteType,
1314 read: VTimestamp,
1315}
1316
1317impl Default for LocalClocks {
1318 fn default() -> Self {
1319 Self { write: VTimestamp::ZERO, write_type: NaWriteType::Allocate, read: VTimestamp::ZERO }
1320 }
1321}
1322
1323impl FrameState {
1324 pub fn local_write(&self, local: mir::Local, storage_live: bool, machine: &MiriMachine<'_>) {
1325 let current_span = machine.current_user_relevant_span();
1326 let global = machine.data_race.as_vclocks_ref().unwrap();
1327 if !global.race_detecting() {
1328 return;
1329 }
1330 let (index, mut thread_clocks) = global.active_thread_state_mut(&machine.threads);
1331 if !current_span.is_dummy() {
1333 thread_clocks.clock.index_mut(index).span = current_span;
1334 }
1335 let mut clocks = self.local_clocks.borrow_mut();
1336 if storage_live {
1337 let new_clocks = LocalClocks {
1338 write: thread_clocks.clock[index],
1339 write_type: NaWriteType::Allocate,
1340 read: VTimestamp::ZERO,
1341 };
1342 clocks.insert(local, new_clocks);
1345 } else {
1346 let clocks = clocks.entry(local).or_default();
1349 clocks.write = thread_clocks.clock[index];
1350 clocks.write_type = NaWriteType::Write;
1351 }
1352 }
1353
1354 pub fn local_read(&self, local: mir::Local, machine: &MiriMachine<'_>) {
1355 let current_span = machine.current_user_relevant_span();
1356 let global = machine.data_race.as_vclocks_ref().unwrap();
1357 if !global.race_detecting() {
1358 return;
1359 }
1360 let (index, mut thread_clocks) = global.active_thread_state_mut(&machine.threads);
1361 if !current_span.is_dummy() {
1363 thread_clocks.clock.index_mut(index).span = current_span;
1364 }
1365 thread_clocks.clock.index_mut(index).set_read_type(NaReadType::Read);
1366 let mut clocks = self.local_clocks.borrow_mut();
1369 let clocks = clocks.entry(local).or_default();
1370 clocks.read = thread_clocks.clock[index];
1371 }
1372
1373 pub fn local_moved_to_memory(
1374 &self,
1375 local: mir::Local,
1376 alloc: &mut VClockAlloc,
1377 machine: &MiriMachine<'_>,
1378 ) {
1379 let global = machine.data_race.as_vclocks_ref().unwrap();
1380 if !global.race_detecting() {
1381 return;
1382 }
1383 let (index, _thread_clocks) = global.active_thread_state_mut(&machine.threads);
1384 let local_clocks = self.local_clocks.borrow_mut().remove(&local).unwrap_or_default();
1388 for (_mem_clocks_range, mem_clocks) in alloc.alloc_ranges.get_mut().iter_mut_all() {
1389 assert_eq!(mem_clocks.write.0, index);
1392 mem_clocks.write = (index, local_clocks.write);
1394 mem_clocks.write_type = local_clocks.write_type;
1395 mem_clocks.read = VClock::new_with_index(index, local_clocks.read);
1396 }
1397 }
1398}
1399
1400impl<'tcx> EvalContextPrivExt<'tcx> for MiriInterpCx<'tcx> {}
1401trait EvalContextPrivExt<'tcx>: MiriInterpCxExt<'tcx> {
1402 #[inline]
1410 fn allow_data_races_ref<R>(&self, op: impl FnOnce(&MiriInterpCx<'tcx>) -> R) -> R {
1411 let this = self.eval_context_ref();
1412 this.machine.data_race.set_ongoing_action_data_race_free(true);
1413 let result = op(this);
1414 this.machine.data_race.set_ongoing_action_data_race_free(false);
1415 result
1416 }
1417
1418 #[inline]
1422 fn allow_data_races_mut<R>(&mut self, op: impl FnOnce(&mut MiriInterpCx<'tcx>) -> R) -> R {
1423 let this = self.eval_context_mut();
1424 this.machine.data_race.set_ongoing_action_data_race_free(true);
1425 let result = op(this);
1426 this.machine.data_race.set_ongoing_action_data_race_free(false);
1427 result
1428 }
1429
1430 fn atomic_access_check(
1432 &self,
1433 place: &MPlaceTy<'tcx>,
1434 access_type: AtomicAccessType,
1435 ) -> InterpResult<'tcx> {
1436 let this = self.eval_context_ref();
1437 let align = Align::from_bytes(place.layout.size.bytes()).unwrap();
1441 this.check_ptr_align(place.ptr(), align)?;
1442 let (alloc_id, _offset, _prov) = this
1450 .ptr_try_get_alloc_id(place.ptr(), 0)
1451 .expect("there are no zero-sized atomic accesses");
1452 if this.get_alloc_mutability(alloc_id)? == Mutability::Not {
1453 match access_type {
1455 AtomicAccessType::Rmw | AtomicAccessType::Store => {
1456 throw_ub_format!(
1457 "atomic store and read-modify-write operations cannot be performed on read-only memory\n\
1458 see <https://doc.rust-lang.org/nightly/std/sync/atomic/index.html#atomic-accesses-to-read-only-memory> for more information"
1459 );
1460 }
1461 AtomicAccessType::Load(_)
1462 if place.layout.size > this.tcx.data_layout().pointer_size() =>
1463 {
1464 throw_ub_format!(
1465 "large atomic load operations cannot be performed on read-only memory\n\
1466 these operations often have to be implemented using read-modify-write operations, which require writeable memory\n\
1467 see <https://doc.rust-lang.org/nightly/std/sync/atomic/index.html#atomic-accesses-to-read-only-memory> for more information"
1468 );
1469 }
1470 AtomicAccessType::Load(o) if o != AtomicReadOrd::Relaxed => {
1471 throw_ub_format!(
1472 "non-relaxed atomic load operations cannot be performed on read-only memory\n\
1473 these operations sometimes have to be implemented using read-modify-write operations, which require writeable memory\n\
1474 see <https://doc.rust-lang.org/nightly/std/sync/atomic/index.html#atomic-accesses-to-read-only-memory> for more information"
1475 );
1476 }
1477 _ => {
1478 }
1480 }
1481 }
1482 interp_ok(())
1483 }
1484
1485 fn validate_atomic_load(
1488 &self,
1489 place: &MPlaceTy<'tcx>,
1490 atomic: AtomicReadOrd,
1491 sync_clock: Option<&VClock>,
1492 ) -> InterpResult<'tcx> {
1493 let this = self.eval_context_ref();
1494 this.validate_atomic_op(
1495 place,
1496 atomic,
1497 AccessType::AtomicLoad,
1498 move |memory, clocks, index, atomic| {
1499 if atomic == AtomicReadOrd::Relaxed {
1500 memory.load_relaxed(&mut *clocks, index, place.layout.size, sync_clock)
1501 } else {
1502 memory.load_acquire(&mut *clocks, index, place.layout.size, sync_clock)
1503 }
1504 },
1505 )
1506 }
1507
1508 fn validate_atomic_store(
1511 &mut self,
1512 place: &MPlaceTy<'tcx>,
1513 atomic: AtomicWriteOrd,
1514 ) -> InterpResult<'tcx> {
1515 let this = self.eval_context_mut();
1516 this.validate_atomic_op(
1517 place,
1518 atomic,
1519 AccessType::AtomicStore,
1520 move |memory, clocks, index, atomic| {
1521 if atomic == AtomicWriteOrd::Relaxed {
1522 memory.store_relaxed(clocks, index, place.layout.size)
1523 } else {
1524 memory.store_release(clocks, index, place.layout.size)
1525 }
1526 },
1527 )
1528 }
1529
1530 fn validate_atomic_rmw(
1533 &mut self,
1534 place: &MPlaceTy<'tcx>,
1535 atomic: AtomicRwOrd,
1536 ) -> InterpResult<'tcx> {
1537 use AtomicRwOrd::*;
1538 let acquire = matches!(atomic, Acquire | AcqRel | SeqCst);
1539 let release = matches!(atomic, Release | AcqRel | SeqCst);
1540 let this = self.eval_context_mut();
1541 this.validate_atomic_op(
1542 place,
1543 atomic,
1544 AccessType::AtomicRmw,
1545 move |memory, clocks, index, _| {
1546 if acquire {
1547 memory.load_acquire(clocks, index, place.layout.size, None)?;
1548 } else {
1549 memory.load_relaxed(clocks, index, place.layout.size, None)?;
1550 }
1551 if release {
1552 memory.rmw_release(clocks, index, place.layout.size)
1553 } else {
1554 memory.rmw_relaxed(clocks, index, place.layout.size)
1555 }
1556 },
1557 )
1558 }
1559
1560 fn validate_atomic_op<A: Debug + Copy>(
1562 &self,
1563 place: &MPlaceTy<'tcx>,
1564 atomic: A,
1565 access: AccessType,
1566 mut op: impl FnMut(
1567 &mut MemoryCellClocks,
1568 &mut ThreadClockSet,
1569 VectorIdx,
1570 A,
1571 ) -> Result<(), DataRace>,
1572 ) -> InterpResult<'tcx> {
1573 let this = self.eval_context_ref();
1574 assert!(access.is_atomic());
1575 let Some(data_race) = this.machine.data_race.as_vclocks_ref() else {
1576 return interp_ok(());
1577 };
1578 if !data_race.race_detecting() {
1579 return interp_ok(());
1580 }
1581 let size = place.layout.size;
1582 let (alloc_id, base_offset, _prov) = this.ptr_get_alloc_id(place.ptr(), 0)?;
1583 let alloc_meta = this.get_alloc_extra(alloc_id)?.data_race.as_vclocks_ref().unwrap();
1586 trace!(
1587 "Atomic op({}) with ordering {:?} on {:?} (size={})",
1588 access.description(None, None),
1589 &atomic,
1590 place.ptr(),
1591 size.bytes()
1592 );
1593
1594 let current_span = this.machine.current_user_relevant_span();
1595 data_race.maybe_perform_sync_operation(
1597 &this.machine.threads,
1598 current_span,
1599 |index, mut thread_clocks| {
1600 for (mem_clocks_range, mem_clocks) in
1601 alloc_meta.alloc_ranges.borrow_mut().iter_mut(base_offset, size)
1602 {
1603 if let Err(DataRace) = op(mem_clocks, &mut thread_clocks, index, atomic) {
1604 mem::drop(thread_clocks);
1605 return VClockAlloc::report_data_race(
1606 data_race,
1607 &this.machine.threads,
1608 mem_clocks,
1609 access,
1610 place.layout.size,
1611 interpret::Pointer::new(
1612 alloc_id,
1613 Size::from_bytes(mem_clocks_range.start),
1614 ),
1615 None,
1616 )
1617 .map(|_| true);
1618 }
1619 }
1620
1621 interp_ok(true)
1623 },
1624 )?;
1625
1626 if tracing::enabled!(tracing::Level::TRACE) {
1628 for (_offset, mem_clocks) in alloc_meta.alloc_ranges.borrow().iter(base_offset, size) {
1629 trace!(
1630 "Updated atomic memory({:?}, size={}) to {:#?}",
1631 place.ptr(),
1632 size.bytes(),
1633 mem_clocks.atomic_ops
1634 );
1635 }
1636 }
1637
1638 interp_ok(())
1639 }
1640}
1641
1642impl GlobalState {
1643 pub fn new(config: &MiriConfig) -> Self {
1646 let mut global_state = GlobalState {
1647 multi_threaded: Cell::new(false),
1648 ongoing_action_data_race_free: Cell::new(false),
1649 vector_clocks: RefCell::new(IndexVec::new()),
1650 vector_info: RefCell::new(IndexVec::new()),
1651 thread_info: RefCell::new(IndexVec::new()),
1652 reuse_candidates: RefCell::new(FxHashSet::default()),
1653 last_sc_fence: RefCell::new(VClock::default()),
1654 last_sc_write_per_thread: RefCell::new(VClock::default()),
1655 track_outdated_loads: config.track_outdated_loads,
1656 weak_memory: config.weak_memory_emulation,
1657 };
1658
1659 let index = global_state.vector_clocks.get_mut().push(ThreadClockSet::default());
1662 global_state.vector_info.get_mut().push(ThreadId::MAIN_THREAD);
1663 global_state
1664 .thread_info
1665 .get_mut()
1666 .push(ThreadExtraState { vector_index: Some(index), termination_vector_clock: None });
1667
1668 global_state
1669 }
1670
1671 pub(super) fn race_detecting(&self) -> bool {
1675 self.multi_threaded.get() && !self.ongoing_action_data_race_free.get()
1676 }
1677
1678 pub(super) fn ongoing_action_data_race_free(&self) -> bool {
1679 self.ongoing_action_data_race_free.get()
1680 }
1681
1682 fn find_vector_index_reuse_candidate(&self) -> Option<VectorIdx> {
1685 let mut reuse = self.reuse_candidates.borrow_mut();
1686 let vector_clocks = self.vector_clocks.borrow();
1687 for &candidate in reuse.iter() {
1688 let target_timestamp = vector_clocks[candidate].clock[candidate];
1689 if vector_clocks.iter_enumerated().all(|(clock_idx, clock)| {
1690 let no_data_race = clock.clock[candidate] >= target_timestamp;
1693
1694 let vector_terminated = reuse.contains(&clock_idx);
1697
1698 no_data_race || vector_terminated
1701 }) {
1702 assert!(reuse.remove(&candidate));
1707 return Some(candidate);
1708 }
1709 }
1710 None
1711 }
1712
1713 #[inline]
1716 pub fn thread_created(
1717 &mut self,
1718 thread_mgr: &ThreadManager<'_>,
1719 thread: ThreadId,
1720 current_span: Span,
1721 ) {
1722 let current_index = self.active_thread_index(thread_mgr);
1723
1724 self.multi_threaded.set(true);
1727
1728 let mut thread_info = self.thread_info.borrow_mut();
1730 thread_info.ensure_contains_elem(thread, Default::default);
1731
1732 let created_index = if let Some(reuse_index) = self.find_vector_index_reuse_candidate() {
1735 let vector_clocks = self.vector_clocks.get_mut();
1738 vector_clocks[reuse_index].increment_clock(reuse_index, current_span);
1739
1740 let vector_info = self.vector_info.get_mut();
1743 let old_thread = vector_info[reuse_index];
1744 vector_info[reuse_index] = thread;
1745
1746 thread_info[old_thread].vector_index = None;
1749
1750 reuse_index
1751 } else {
1752 let vector_info = self.vector_info.get_mut();
1755 vector_info.push(thread)
1756 };
1757
1758 trace!("Creating thread = {:?} with vector index = {:?}", thread, created_index);
1759
1760 thread_info[thread].vector_index = Some(created_index);
1762
1763 let vector_clocks = self.vector_clocks.get_mut();
1765 if created_index == vector_clocks.next_index() {
1766 vector_clocks.push(ThreadClockSet::default());
1767 }
1768
1769 let (current, created) = vector_clocks.pick2_mut(current_index, created_index);
1771
1772 created.join_with(current);
1775
1776 current.increment_clock(current_index, current_span);
1779 created.increment_clock(created_index, current_span);
1780 }
1781
1782 #[inline]
1786 pub fn thread_joined(&mut self, threads: &ThreadManager<'_>, joinee: ThreadId) {
1787 let thread_info = self.thread_info.borrow();
1788 let thread_info = &thread_info[joinee];
1789
1790 let join_clock = thread_info
1792 .termination_vector_clock
1793 .as_ref()
1794 .expect("joined with thread but thread has not terminated");
1795 self.acquire_clock(join_clock, threads);
1797
1798 if let Some(current_index) = thread_info.vector_index {
1803 if threads.get_live_thread_count() == 1 {
1804 let vector_clocks = self.vector_clocks.get_mut();
1805 let current_clock = &vector_clocks[current_index];
1807 if vector_clocks
1808 .iter_enumerated()
1809 .all(|(idx, clocks)| clocks.clock[idx] <= current_clock.clock[idx])
1810 {
1811 self.multi_threaded.set(false);
1815 }
1816 }
1817 }
1818 }
1819
1820 #[inline]
1828 pub fn thread_terminated(&mut self, thread_mgr: &ThreadManager<'_>) {
1829 let current_thread = thread_mgr.active_thread();
1830 let current_index = self.active_thread_index(thread_mgr);
1831
1832 let terminaion_clock = self.release_clock(thread_mgr, |clock| clock.clone());
1834 self.thread_info.get_mut()[current_thread].termination_vector_clock =
1835 Some(terminaion_clock);
1836
1837 let reuse = self.reuse_candidates.get_mut();
1839 reuse.insert(current_index);
1840 }
1841
1842 fn atomic_fence<'tcx>(
1844 &self,
1845 machine: &MiriMachine<'tcx>,
1846 atomic: AtomicFenceOrd,
1847 ) -> InterpResult<'tcx> {
1848 let current_span = machine.current_user_relevant_span();
1849 self.maybe_perform_sync_operation(&machine.threads, current_span, |index, mut clocks| {
1850 trace!("Atomic fence on {:?} with ordering {:?}", index, atomic);
1851
1852 if atomic != AtomicFenceOrd::Release {
1856 clocks.apply_acquire_fence();
1858 }
1859 if atomic == AtomicFenceOrd::SeqCst {
1860 let mut sc_fence_clock = self.last_sc_fence.borrow_mut();
1868 sc_fence_clock.join(&clocks.clock);
1869 clocks.clock.join(&sc_fence_clock);
1870 clocks.write_seqcst.join(&self.last_sc_write_per_thread.borrow());
1873 }
1874 if atomic != AtomicFenceOrd::Acquire {
1877 clocks.apply_release_fence();
1879 }
1880
1881 interp_ok(atomic != AtomicFenceOrd::Acquire)
1883 })
1884 }
1885
1886 fn maybe_perform_sync_operation<'tcx>(
1894 &self,
1895 thread_mgr: &ThreadManager<'_>,
1896 current_span: Span,
1897 op: impl FnOnce(VectorIdx, RefMut<'_, ThreadClockSet>) -> InterpResult<'tcx, bool>,
1898 ) -> InterpResult<'tcx> {
1899 if self.multi_threaded.get() {
1900 let (index, clocks) = self.active_thread_state_mut(thread_mgr);
1901 if op(index, clocks)? {
1902 let (_, mut clocks) = self.active_thread_state_mut(thread_mgr);
1903 clocks.increment_clock(index, current_span);
1904 }
1905 }
1906 interp_ok(())
1907 }
1908
1909 fn print_thread_metadata(&self, thread_mgr: &ThreadManager<'_>, vector: VectorIdx) -> String {
1912 let thread = self.vector_info.borrow()[vector];
1913 let thread_name = thread_mgr.get_thread_display_name(thread);
1914 format!("thread `{thread_name}`")
1915 }
1916
1917 pub fn acquire_clock<'tcx>(&self, clock: &VClock, threads: &ThreadManager<'tcx>) {
1922 let thread = threads.active_thread();
1923 let (_, mut clocks) = self.thread_state_mut(thread);
1924 clocks.clock.join(clock);
1925 }
1926
1927 pub fn release_clock<'tcx, R>(
1931 &self,
1932 threads: &ThreadManager<'tcx>,
1933 callback: impl FnOnce(&VClock) -> R,
1934 ) -> R {
1935 let thread = threads.active_thread();
1936 let span = threads.active_thread_ref().current_user_relevant_span();
1937 let (index, mut clocks) = self.thread_state_mut(thread);
1938 let r = callback(&clocks.clock);
1939 clocks.increment_clock(index, span);
1942
1943 r
1944 }
1945
1946 fn thread_index(&self, thread: ThreadId) -> VectorIdx {
1947 self.thread_info.borrow()[thread].vector_index.expect("thread has no assigned vector")
1948 }
1949
1950 #[inline]
1953 fn thread_state_mut(&self, thread: ThreadId) -> (VectorIdx, RefMut<'_, ThreadClockSet>) {
1954 let index = self.thread_index(thread);
1955 let ref_vector = self.vector_clocks.borrow_mut();
1956 let clocks = RefMut::map(ref_vector, |vec| &mut vec[index]);
1957 (index, clocks)
1958 }
1959
1960 #[inline]
1963 fn thread_state(&self, thread: ThreadId) -> (VectorIdx, Ref<'_, ThreadClockSet>) {
1964 let index = self.thread_index(thread);
1965 let ref_vector = self.vector_clocks.borrow();
1966 let clocks = Ref::map(ref_vector, |vec| &vec[index]);
1967 (index, clocks)
1968 }
1969
1970 #[inline]
1973 pub(super) fn active_thread_state(
1974 &self,
1975 thread_mgr: &ThreadManager<'_>,
1976 ) -> (VectorIdx, Ref<'_, ThreadClockSet>) {
1977 self.thread_state(thread_mgr.active_thread())
1978 }
1979
1980 #[inline]
1983 pub(super) fn active_thread_state_mut(
1984 &self,
1985 thread_mgr: &ThreadManager<'_>,
1986 ) -> (VectorIdx, RefMut<'_, ThreadClockSet>) {
1987 self.thread_state_mut(thread_mgr.active_thread())
1988 }
1989
1990 #[inline]
1993 fn active_thread_index(&self, thread_mgr: &ThreadManager<'_>) -> VectorIdx {
1994 let active_thread_id = thread_mgr.active_thread();
1995 self.thread_index(active_thread_id)
1996 }
1997
1998 pub(super) fn sc_write(&self, thread_mgr: &ThreadManager<'_>) {
2000 let (index, clocks) = self.active_thread_state(thread_mgr);
2001 self.last_sc_write_per_thread.borrow_mut().set_at_index(&clocks.clock, index);
2002 }
2003
2004 pub(super) fn sc_read(&self, thread_mgr: &ThreadManager<'_>) {
2006 let (.., mut clocks) = self.active_thread_state_mut(thread_mgr);
2007 clocks.read_seqcst.join(&self.last_sc_fence.borrow());
2008 }
2009}