]> git.lizzy.rs Git - rust.git/blob - src/data_race.rs
rustup
[rust.git] / src / data_race.rs
1 //! Implementation of a data-race detector using Lamport Timestamps / Vector-clocks
2 //! based on the Dynamic Race Detection for C++:
3 //! https://www.doc.ic.ac.uk/~afd/homepages/papers/pdfs/2017/POPL.pdf
4 //! which does not report false-positives when fences are used, and gives better
5 //! accuracy in presence of read-modify-write operations.
6 //!
7 //! The implementation contains modifications to correctly model the changes to the memory model in C++20
8 //! regarding the weakening of release sequences: http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2018/p0982r1.html.
9 //! Relaxed stores now unconditionally block all currently active release sequences and so per-thread tracking of release
10 //! sequences is not needed.
11 //!
12 //! The implementation also models races with memory allocation and deallocation via treating allocation and
13 //! deallocation as a type of write internally for detecting data-races.
14 //!
15 //! This does not explore weak memory orders and so can still miss data-races
16 //! but should not report false-positives
17 //!
18 //! Data-race definition from(https://en.cppreference.com/w/cpp/language/memory_model#Threads_and_data_races):
19 //! a data race occurs between two memory accesses if they are on different threads, at least one operation
20 //! is non-atomic, at least one operation is a write and neither access happens-before the other. Read the link
21 //! for full definition.
22 //!
23 //! This re-uses vector indexes for threads that are known to be unable to report data-races, this is valid
24 //! because it only re-uses vector indexes once all currently-active (not-terminated) threads have an internal
25 //! vector clock that happens-after the join operation of the candidate thread. Threads that have not been joined
26 //! on are not considered. Since the thread's vector clock will only increase and a data-race implies that
27 //! there is some index x where clock[x] > thread_clock, when this is true clock[candidate-idx] > thread_clock
28 //! can never hold and hence a data-race can never be reported in that vector index again.
29 //! This means that the thread-index can be safely re-used, starting on the next timestamp for the newly created
30 //! thread.
31 //!
32 //! The sequentially consistent ordering corresponds to the ordering that the threads
33 //! are currently scheduled, this means that the data-race detector has no additional
34 //! logic for sequentially consistent accesses at the moment since they are indistinguishable
35 //! from acquire/release operations. If weak memory orderings are explored then this
36 //! may need to change or be updated accordingly.
37 //!
38 //! Per the C++ spec for the memory model a sequentially consistent operation:
39 //!   "A load operation with this memory order performs an acquire operation,
40 //!    a store performs a release operation, and read-modify-write performs
41 //!    both an acquire operation and a release operation, plus a single total
42 //!    order exists in which all threads observe all modifications in the same
43 //!    order (see Sequentially-consistent ordering below) "
44 //! So in the absence of weak memory effects a seq-cst load & a seq-cst store is identical
45 //! to a acquire load and a release store given the global sequentially consistent order
46 //! of the schedule.
47 //!
48 //! The timestamps used in the data-race detector assign each sequence of non-atomic operations
49 //! followed by a single atomic or concurrent operation a single timestamp.
50 //! Write, Read, Write, ThreadJoin will be represented by a single timestamp value on a thread.
51 //! This is because extra increment operations between the operations in the sequence are not
52 //! required for accurate reporting of data-race values.
53 //!
54 //! As per the paper a threads timestamp is only incremented after a release operation is performed
55 //! so some atomic operations that only perform acquires do not increment the timestamp. Due to shared
56 //! code some atomic operations may increment the timestamp when not necessary but this has no effect
57 //! on the data-race detection code.
58 //!
59 //! FIXME:
60 //! currently we have our own local copy of the currently active thread index and names, this is due
61 //! in part to the inability to access the current location of threads.active_thread inside the AllocExtra
62 //! read, write and deallocate functions and should be cleaned up in the future.
63
64 use std::{
65     cell::{Cell, Ref, RefCell, RefMut},
66     fmt::Debug,
67     mem,
68 };
69
70 use rustc_data_structures::fx::{FxHashMap, FxHashSet};
71 use rustc_index::vec::{Idx, IndexVec};
72 use rustc_middle::{mir, ty::layout::TyAndLayout};
73 use rustc_target::abi::Size;
74
75 use crate::{
76     ImmTy, Immediate, InterpResult, MPlaceTy, MemPlaceMeta, MemoryKind, MiriEvalContext,
77     MiriEvalContextExt, MiriMemoryKind, OpTy, Pointer, RangeMap, Scalar, ScalarMaybeUninit, Tag,
78     ThreadId, VClock, VTimestamp, VectorIdx,
79 };
80
81 pub type AllocExtra = VClockAlloc;
82 pub type MemoryExtra = GlobalState;
83
84 /// Valid atomic read-write operations, alias of atomic::Ordering (not non-exhaustive).
85 #[derive(Copy, Clone, PartialEq, Eq, Debug)]
86 pub enum AtomicRwOp {
87     Relaxed,
88     Acquire,
89     Release,
90     AcqRel,
91     SeqCst,
92 }
93
94 /// Valid atomic read operations, subset of atomic::Ordering.
95 #[derive(Copy, Clone, PartialEq, Eq, Debug)]
96 pub enum AtomicReadOp {
97     Relaxed,
98     Acquire,
99     SeqCst,
100 }
101
102 /// Valid atomic write operations, subset of atomic::Ordering.
103 #[derive(Copy, Clone, PartialEq, Eq, Debug)]
104 pub enum AtomicWriteOp {
105     Relaxed,
106     Release,
107     SeqCst,
108 }
109
110 /// Valid atomic fence operations, subset of atomic::Ordering.
111 #[derive(Copy, Clone, PartialEq, Eq, Debug)]
112 pub enum AtomicFenceOp {
113     Acquire,
114     Release,
115     AcqRel,
116     SeqCst,
117 }
118
119 /// The current set of vector clocks describing the state
120 /// of a thread, contains the happens-before clock and
121 /// additional metadata to model atomic fence operations.
122 #[derive(Clone, Default, Debug)]
123 struct ThreadClockSet {
124     /// The increasing clock representing timestamps
125     /// that happen-before this thread.
126     clock: VClock,
127
128     /// The set of timestamps that will happen-before this
129     /// thread once it performs an acquire fence.
130     fence_acquire: VClock,
131
132     /// The last timestamp of happens-before relations that
133     /// have been released by this thread by a fence.
134     fence_release: VClock,
135 }
136
137 impl ThreadClockSet {
138     /// Apply the effects of a release fence to this
139     /// set of thread vector clocks.
140     #[inline]
141     fn apply_release_fence(&mut self) {
142         self.fence_release.clone_from(&self.clock);
143     }
144
145     /// Apply the effects of a acquire fence to this
146     /// set of thread vector clocks.
147     #[inline]
148     fn apply_acquire_fence(&mut self) {
149         self.clock.join(&self.fence_acquire);
150     }
151
152     /// Increment the happens-before clock at a
153     /// known index.
154     #[inline]
155     fn increment_clock(&mut self, index: VectorIdx) {
156         self.clock.increment_index(index);
157     }
158
159     /// Join the happens-before clock with that of
160     /// another thread, used to model thread join
161     /// operations.
162     fn join_with(&mut self, other: &ThreadClockSet) {
163         self.clock.join(&other.clock);
164     }
165 }
166
167 /// Error returned by finding a data race
168 /// should be elaborated upon.
169 #[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]
170 pub struct DataRace;
171
172 /// Externally stored memory cell clocks
173 /// explicitly to reduce memory usage for the
174 /// common case where no atomic operations
175 /// exists on the memory cell.
176 #[derive(Clone, PartialEq, Eq, Default, Debug)]
177 struct AtomicMemoryCellClocks {
178     /// The clock-vector of the timestamp of the last atomic
179     /// read operation performed by each thread.
180     /// This detects potential data-races between atomic read
181     /// and non-atomic write operations.
182     read_vector: VClock,
183
184     /// The clock-vector of the timestamp of the last atomic
185     /// write operation performed by each thread.
186     /// This detects potential data-races between atomic write
187     /// and non-atomic read or write operations.
188     write_vector: VClock,
189
190     /// Synchronization vector for acquire-release semantics
191     /// contains the vector of timestamps that will
192     /// happen-before a thread if an acquire-load is
193     /// performed on the data.
194     sync_vector: VClock,
195 }
196
197 /// Type of write operation: allocating memory
198 /// non-atomic writes and deallocating memory
199 /// are all treated as writes for the purpose
200 /// of the data-race detector.
201 #[derive(Copy, Clone, PartialEq, Eq, Debug)]
202 enum WriteType {
203     /// Allocate memory.
204     Allocate,
205
206     /// Standard unsynchronized write.
207     Write,
208
209     /// Deallocate memory.
210     /// Note that when memory is deallocated first, later non-atomic accesses
211     /// will be reported as use-after-free, not as data races.
212     /// (Same for `Allocate` above.)
213     Deallocate,
214 }
215 impl WriteType {
216     fn get_descriptor(self) -> &'static str {
217         match self {
218             WriteType::Allocate => "Allocate",
219             WriteType::Write => "Write",
220             WriteType::Deallocate => "Deallocate",
221         }
222     }
223 }
224
225 /// Memory Cell vector clock metadata
226 /// for data-race detection.
227 #[derive(Clone, PartialEq, Eq, Debug)]
228 struct MemoryCellClocks {
229     /// The vector-clock timestamp of the last write
230     /// corresponding to the writing threads timestamp.
231     write: VTimestamp,
232
233     /// The identifier of the vector index, corresponding to a thread
234     /// that performed the last write operation.
235     write_index: VectorIdx,
236
237     /// The type of operation that the write index represents,
238     /// either newly allocated memory, a non-atomic write or
239     /// a deallocation of memory.
240     write_type: WriteType,
241
242     /// The vector-clock of the timestamp of the last read operation
243     /// performed by a thread since the last write operation occurred.
244     /// It is reset to zero on each write operation.
245     read: VClock,
246
247     /// Atomic acquire & release sequence tracking clocks.
248     /// For non-atomic memory in the common case this
249     /// value is set to None.
250     atomic_ops: Option<Box<AtomicMemoryCellClocks>>,
251 }
252
253 impl MemoryCellClocks {
254     /// Create a new set of clocks representing memory allocated
255     ///  at a given vector timestamp and index.
256     fn new(alloc: VTimestamp, alloc_index: VectorIdx) -> Self {
257         MemoryCellClocks {
258             read: VClock::default(),
259             write: alloc,
260             write_index: alloc_index,
261             write_type: WriteType::Allocate,
262             atomic_ops: None,
263         }
264     }
265
266     /// Load the internal atomic memory cells if they exist.
267     #[inline]
268     fn atomic(&self) -> Option<&AtomicMemoryCellClocks> {
269         match &self.atomic_ops {
270             Some(op) => Some(&*op),
271             None => None,
272         }
273     }
274
275     /// Load or create the internal atomic memory metadata
276     /// if it does not exist.
277     #[inline]
278     fn atomic_mut(&mut self) -> &mut AtomicMemoryCellClocks {
279         self.atomic_ops.get_or_insert_with(Default::default)
280     }
281
282     /// Update memory cell data-race tracking for atomic
283     /// load acquire semantics, is a no-op if this memory was
284     /// not used previously as atomic memory.
285     fn load_acquire(
286         &mut self,
287         clocks: &mut ThreadClockSet,
288         index: VectorIdx,
289     ) -> Result<(), DataRace> {
290         self.atomic_read_detect(clocks, index)?;
291         if let Some(atomic) = self.atomic() {
292             clocks.clock.join(&atomic.sync_vector);
293         }
294         Ok(())
295     }
296
297     /// Update memory cell data-race tracking for atomic
298     /// load relaxed semantics, is a no-op if this memory was
299     /// not used previously as atomic memory.
300     fn load_relaxed(
301         &mut self,
302         clocks: &mut ThreadClockSet,
303         index: VectorIdx,
304     ) -> Result<(), DataRace> {
305         self.atomic_read_detect(clocks, index)?;
306         if let Some(atomic) = self.atomic() {
307             clocks.fence_acquire.join(&atomic.sync_vector);
308         }
309         Ok(())
310     }
311
312     /// Update the memory cell data-race tracking for atomic
313     /// store release semantics.
314     fn store_release(&mut self, clocks: &ThreadClockSet, index: VectorIdx) -> Result<(), DataRace> {
315         self.atomic_write_detect(clocks, index)?;
316         let atomic = self.atomic_mut();
317         atomic.sync_vector.clone_from(&clocks.clock);
318         Ok(())
319     }
320
321     /// Update the memory cell data-race tracking for atomic
322     /// store relaxed semantics.
323     fn store_relaxed(&mut self, clocks: &ThreadClockSet, index: VectorIdx) -> Result<(), DataRace> {
324         self.atomic_write_detect(clocks, index)?;
325
326         // The handling of release sequences was changed in C++20 and so
327         // the code here is different to the paper since now all relaxed
328         // stores block release sequences. The exception for same-thread
329         // relaxed stores has been removed.
330         let atomic = self.atomic_mut();
331         atomic.sync_vector.clone_from(&clocks.fence_release);
332         Ok(())
333     }
334
335     /// Update the memory cell data-race tracking for atomic
336     /// store release semantics for RMW operations.
337     fn rmw_release(&mut self, clocks: &ThreadClockSet, index: VectorIdx) -> Result<(), DataRace> {
338         self.atomic_write_detect(clocks, index)?;
339         let atomic = self.atomic_mut();
340         atomic.sync_vector.join(&clocks.clock);
341         Ok(())
342     }
343
344     /// Update the memory cell data-race tracking for atomic
345     /// store relaxed semantics for RMW operations.
346     fn rmw_relaxed(&mut self, clocks: &ThreadClockSet, index: VectorIdx) -> Result<(), DataRace> {
347         self.atomic_write_detect(clocks, index)?;
348         let atomic = self.atomic_mut();
349         atomic.sync_vector.join(&clocks.fence_release);
350         Ok(())
351     }
352
353     /// Detect data-races with an atomic read, caused by a non-atomic write that does
354     /// not happen-before the atomic-read.
355     fn atomic_read_detect(
356         &mut self,
357         clocks: &ThreadClockSet,
358         index: VectorIdx,
359     ) -> Result<(), DataRace> {
360         log::trace!("Atomic read with vectors: {:#?} :: {:#?}", self, clocks);
361         if self.write <= clocks.clock[self.write_index] {
362             let atomic = self.atomic_mut();
363             atomic.read_vector.set_at_index(&clocks.clock, index);
364             Ok(())
365         } else {
366             Err(DataRace)
367         }
368     }
369
370     /// Detect data-races with an atomic write, either with a non-atomic read or with
371     /// a non-atomic write.
372     fn atomic_write_detect(
373         &mut self,
374         clocks: &ThreadClockSet,
375         index: VectorIdx,
376     ) -> Result<(), DataRace> {
377         log::trace!("Atomic write with vectors: {:#?} :: {:#?}", self, clocks);
378         if self.write <= clocks.clock[self.write_index] && self.read <= clocks.clock {
379             let atomic = self.atomic_mut();
380             atomic.write_vector.set_at_index(&clocks.clock, index);
381             Ok(())
382         } else {
383             Err(DataRace)
384         }
385     }
386
387     /// Detect races for non-atomic read operations at the current memory cell
388     /// returns true if a data-race is detected.
389     fn read_race_detect(
390         &mut self,
391         clocks: &ThreadClockSet,
392         index: VectorIdx,
393     ) -> Result<(), DataRace> {
394         log::trace!("Unsynchronized read with vectors: {:#?} :: {:#?}", self, clocks);
395         if self.write <= clocks.clock[self.write_index] {
396             let race_free = if let Some(atomic) = self.atomic() {
397                 atomic.write_vector <= clocks.clock
398             } else {
399                 true
400             };
401             if race_free {
402                 self.read.set_at_index(&clocks.clock, index);
403                 Ok(())
404             } else {
405                 Err(DataRace)
406             }
407         } else {
408             Err(DataRace)
409         }
410     }
411
412     /// Detect races for non-atomic write operations at the current memory cell
413     /// returns true if a data-race is detected.
414     fn write_race_detect(
415         &mut self,
416         clocks: &ThreadClockSet,
417         index: VectorIdx,
418         write_type: WriteType,
419     ) -> Result<(), DataRace> {
420         log::trace!("Unsynchronized write with vectors: {:#?} :: {:#?}", self, clocks);
421         if self.write <= clocks.clock[self.write_index] && self.read <= clocks.clock {
422             let race_free = if let Some(atomic) = self.atomic() {
423                 atomic.write_vector <= clocks.clock && atomic.read_vector <= clocks.clock
424             } else {
425                 true
426             };
427             if race_free {
428                 self.write = clocks.clock[index];
429                 self.write_index = index;
430                 self.write_type = write_type;
431                 self.read.set_zero_vector();
432                 Ok(())
433             } else {
434                 Err(DataRace)
435             }
436         } else {
437             Err(DataRace)
438         }
439     }
440 }
441
442 /// Evaluation context extensions.
443 impl<'mir, 'tcx: 'mir> EvalContextExt<'mir, 'tcx> for MiriEvalContext<'mir, 'tcx> {}
444 pub trait EvalContextExt<'mir, 'tcx: 'mir>: MiriEvalContextExt<'mir, 'tcx> {
445     /// Atomic variant of read_scalar_at_offset.
446     fn read_scalar_at_offset_atomic(
447         &self,
448         op: &OpTy<'tcx, Tag>,
449         offset: u64,
450         layout: TyAndLayout<'tcx>,
451         atomic: AtomicReadOp,
452     ) -> InterpResult<'tcx, ScalarMaybeUninit<Tag>> {
453         let this = self.eval_context_ref();
454         let op_place = this.deref_operand(op)?;
455         let offset = Size::from_bytes(offset);
456
457         // Ensure that the following read at an offset is within bounds.
458         assert!(op_place.layout.size >= offset + layout.size);
459         let value_place = op_place.offset(offset, MemPlaceMeta::None, layout, this)?;
460         this.read_scalar_atomic(&value_place, atomic)
461     }
462
463     /// Atomic variant of write_scalar_at_offset.
464     fn write_scalar_at_offset_atomic(
465         &mut self,
466         op: &OpTy<'tcx, Tag>,
467         offset: u64,
468         value: impl Into<ScalarMaybeUninit<Tag>>,
469         layout: TyAndLayout<'tcx>,
470         atomic: AtomicWriteOp,
471     ) -> InterpResult<'tcx> {
472         let this = self.eval_context_mut();
473         let op_place = this.deref_operand(op)?;
474         let offset = Size::from_bytes(offset);
475
476         // Ensure that the following read at an offset is within bounds.
477         assert!(op_place.layout.size >= offset + layout.size);
478         let value_place = op_place.offset(offset, MemPlaceMeta::None, layout, this)?;
479         this.write_scalar_atomic(value.into(), &value_place, atomic)
480     }
481
482     /// Perform an atomic read operation at the memory location.
483     fn read_scalar_atomic(
484         &self,
485         place: &MPlaceTy<'tcx, Tag>,
486         atomic: AtomicReadOp,
487     ) -> InterpResult<'tcx, ScalarMaybeUninit<Tag>> {
488         let this = self.eval_context_ref();
489         let scalar = this.allow_data_races_ref(move |this| this.read_scalar(&place.into()))?;
490         this.validate_atomic_load(place, atomic)?;
491         Ok(scalar)
492     }
493
494     /// Perform an atomic write operation at the memory location.
495     fn write_scalar_atomic(
496         &mut self,
497         val: ScalarMaybeUninit<Tag>,
498         dest: &MPlaceTy<'tcx, Tag>,
499         atomic: AtomicWriteOp,
500     ) -> InterpResult<'tcx> {
501         let this = self.eval_context_mut();
502         this.allow_data_races_mut(move |this| this.write_scalar(val, &(*dest).into()))?;
503         this.validate_atomic_store(dest, atomic)
504     }
505
506     /// Perform a atomic operation on a memory location.
507     fn atomic_op_immediate(
508         &mut self,
509         place: &MPlaceTy<'tcx, Tag>,
510         rhs: &ImmTy<'tcx, Tag>,
511         op: mir::BinOp,
512         neg: bool,
513         atomic: AtomicRwOp,
514     ) -> InterpResult<'tcx, ImmTy<'tcx, Tag>> {
515         let this = self.eval_context_mut();
516
517         let old = this.allow_data_races_mut(|this| this.read_immediate(&place.into()))?;
518
519         // Atomics wrap around on overflow.
520         let val = this.binary_op(op, &old, rhs)?;
521         let val = if neg { this.unary_op(mir::UnOp::Not, &val)? } else { val };
522         this.allow_data_races_mut(|this| this.write_immediate(*val, &(*place).into()))?;
523
524         this.validate_atomic_rmw(place, atomic)?;
525         Ok(old)
526     }
527
528     /// Perform an atomic exchange with a memory place and a new
529     /// scalar value, the old value is returned.
530     fn atomic_exchange_scalar(
531         &mut self,
532         place: &MPlaceTy<'tcx, Tag>,
533         new: ScalarMaybeUninit<Tag>,
534         atomic: AtomicRwOp,
535     ) -> InterpResult<'tcx, ScalarMaybeUninit<Tag>> {
536         let this = self.eval_context_mut();
537
538         let old = this.allow_data_races_mut(|this| this.read_scalar(&place.into()))?;
539         this.allow_data_races_mut(|this| this.write_scalar(new, &(*place).into()))?;
540         this.validate_atomic_rmw(place, atomic)?;
541         Ok(old)
542     }
543
544     /// Perform an conditional atomic exchange with a memory place and a new
545     /// scalar value, the old value is returned.
546     fn atomic_min_max_scalar(
547         &mut self,
548         place: &MPlaceTy<'tcx, Tag>,
549         rhs: ImmTy<'tcx, Tag>,
550         min: bool,
551         atomic: AtomicRwOp,
552     ) -> InterpResult<'tcx, ImmTy<'tcx, Tag>> {
553         let this = self.eval_context_mut();
554
555         let old = this.allow_data_races_mut(|this| this.read_immediate(&place.into()))?;
556         let lt = this.overflowing_binary_op(mir::BinOp::Lt, &old, &rhs)?.0.to_bool()?;
557
558         let new_val = if min {
559             if lt { &old } else { &rhs }
560         } else {
561             if lt { &rhs } else { &old }
562         };
563
564         this.allow_data_races_mut(|this| this.write_immediate_to_mplace(**new_val, place))?;
565
566         this.validate_atomic_rmw(&place, atomic)?;
567
568         // Return the old value.
569         Ok(old)
570     }
571
572     /// Perform an atomic compare and exchange at a given memory location.
573     /// On success an atomic RMW operation is performed and on failure
574     /// only an atomic read occurs. If `can_fail_spuriously` is true,
575     /// then we treat it as a "compare_exchange_weak" operation, and
576     /// some portion of the time fail even when the values are actually
577     /// identical.
578     fn atomic_compare_exchange_scalar(
579         &mut self,
580         place: &MPlaceTy<'tcx, Tag>,
581         expect_old: &ImmTy<'tcx, Tag>,
582         new: ScalarMaybeUninit<Tag>,
583         success: AtomicRwOp,
584         fail: AtomicReadOp,
585         can_fail_spuriously: bool,
586     ) -> InterpResult<'tcx, Immediate<Tag>> {
587         use rand::Rng as _;
588         let this = self.eval_context_mut();
589
590         // Failure ordering cannot be stronger than success ordering, therefore first attempt
591         // to read with the failure ordering and if successful then try again with the success
592         // read ordering and write in the success case.
593         // Read as immediate for the sake of `binary_op()`
594         let old = this.allow_data_races_mut(|this| this.read_immediate(&(place.into())))?;
595         // `binary_op` will bail if either of them is not a scalar.
596         let eq = this.overflowing_binary_op(mir::BinOp::Eq, &old, expect_old)?.0;
597         // If the operation would succeed, but is "weak", fail some portion
598         // of the time, based on `rate`.
599         let rate = this.memory.extra.cmpxchg_weak_failure_rate;
600         let cmpxchg_success = eq.to_bool()?
601             && (!can_fail_spuriously || this.memory.extra.rng.get_mut().gen::<f64>() < rate);
602         let res = Immediate::ScalarPair(
603             old.to_scalar_or_uninit(),
604             Scalar::from_bool(cmpxchg_success).into(),
605         );
606
607         // Update ptr depending on comparison.
608         // if successful, perform a full rw-atomic validation
609         // otherwise treat this as an atomic load with the fail ordering.
610         if cmpxchg_success {
611             this.allow_data_races_mut(|this| this.write_scalar(new, &(*place).into()))?;
612             this.validate_atomic_rmw(place, success)?;
613         } else {
614             this.validate_atomic_load(place, fail)?;
615         }
616
617         // Return the old value.
618         Ok(res)
619     }
620
621     /// Update the data-race detector for an atomic read occurring at the
622     /// associated memory-place and on the current thread.
623     fn validate_atomic_load(
624         &self,
625         place: &MPlaceTy<'tcx, Tag>,
626         atomic: AtomicReadOp,
627     ) -> InterpResult<'tcx> {
628         let this = self.eval_context_ref();
629         this.validate_atomic_op(
630             place,
631             atomic,
632             "Atomic Load",
633             move |memory, clocks, index, atomic| {
634                 if atomic == AtomicReadOp::Relaxed {
635                     memory.load_relaxed(&mut *clocks, index)
636                 } else {
637                     memory.load_acquire(&mut *clocks, index)
638                 }
639             },
640         )
641     }
642
643     /// Update the data-race detector for an atomic write occurring at the
644     /// associated memory-place and on the current thread.
645     fn validate_atomic_store(
646         &mut self,
647         place: &MPlaceTy<'tcx, Tag>,
648         atomic: AtomicWriteOp,
649     ) -> InterpResult<'tcx> {
650         let this = self.eval_context_mut();
651         this.validate_atomic_op(
652             place,
653             atomic,
654             "Atomic Store",
655             move |memory, clocks, index, atomic| {
656                 if atomic == AtomicWriteOp::Relaxed {
657                     memory.store_relaxed(clocks, index)
658                 } else {
659                     memory.store_release(clocks, index)
660                 }
661             },
662         )
663     }
664
665     /// Update the data-race detector for an atomic read-modify-write occurring
666     /// at the associated memory place and on the current thread.
667     fn validate_atomic_rmw(
668         &mut self,
669         place: &MPlaceTy<'tcx, Tag>,
670         atomic: AtomicRwOp,
671     ) -> InterpResult<'tcx> {
672         use AtomicRwOp::*;
673         let acquire = matches!(atomic, Acquire | AcqRel | SeqCst);
674         let release = matches!(atomic, Release | AcqRel | SeqCst);
675         let this = self.eval_context_mut();
676         this.validate_atomic_op(place, atomic, "Atomic RMW", move |memory, clocks, index, _| {
677             if acquire {
678                 memory.load_acquire(clocks, index)?;
679             } else {
680                 memory.load_relaxed(clocks, index)?;
681             }
682             if release {
683                 memory.rmw_release(clocks, index)
684             } else {
685                 memory.rmw_relaxed(clocks, index)
686             }
687         })
688     }
689
690     /// Update the data-race detector for an atomic fence on the current thread.
691     fn validate_atomic_fence(&mut self, atomic: AtomicFenceOp) -> InterpResult<'tcx> {
692         let this = self.eval_context_mut();
693         if let Some(data_race) = &mut this.memory.extra.data_race {
694             data_race.maybe_perform_sync_operation(move |index, mut clocks| {
695                 log::trace!("Atomic fence on {:?} with ordering {:?}", index, atomic);
696
697                 // Apply data-race detection for the current fences
698                 // this treats AcqRel and SeqCst as the same as a acquire
699                 // and release fence applied in the same timestamp.
700                 if atomic != AtomicFenceOp::Release {
701                     // Either Acquire | AcqRel | SeqCst
702                     clocks.apply_acquire_fence();
703                 }
704                 if atomic != AtomicFenceOp::Acquire {
705                     // Either Release | AcqRel | SeqCst
706                     clocks.apply_release_fence();
707                 }
708
709                 // Increment timestamp in case of release semantics.
710                 Ok(atomic != AtomicFenceOp::Acquire)
711             })
712         } else {
713             Ok(())
714         }
715     }
716
717     fn reset_vector_clocks(&mut self, ptr: Pointer<Tag>, size: Size) -> InterpResult<'tcx> {
718         let this = self.eval_context_mut();
719         if let Some(data_race) = &mut this.memory.extra.data_race {
720             if data_race.multi_threaded.get() {
721                 let alloc_meta =
722                     this.memory.get_alloc_extra_mut(ptr.alloc_id)?.0.data_race.as_mut().unwrap();
723                 alloc_meta.reset_clocks(ptr.offset, size);
724             }
725         }
726         Ok(())
727     }
728 }
729
730 /// Vector clock metadata for a logical memory allocation.
731 #[derive(Debug, Clone)]
732 pub struct VClockAlloc {
733     /// Assigning each byte a MemoryCellClocks.
734     alloc_ranges: RefCell<RangeMap<MemoryCellClocks>>,
735 }
736
737 impl VClockAlloc {
738     /// Create a new data-race detector for newly allocated memory.
739     pub fn new_allocation(
740         global: &MemoryExtra,
741         len: Size,
742         kind: MemoryKind<MiriMemoryKind>,
743     ) -> VClockAlloc {
744         let (alloc_timestamp, alloc_index) = match kind {
745             // User allocated and stack memory should track allocation.
746             MemoryKind::Machine(
747                 MiriMemoryKind::Rust | MiriMemoryKind::C | MiriMemoryKind::WinHeap,
748             )
749             | MemoryKind::Stack => {
750                 let (alloc_index, clocks) = global.current_thread_state();
751                 let alloc_timestamp = clocks.clock[alloc_index];
752                 (alloc_timestamp, alloc_index)
753             }
754             // Other global memory should trace races but be allocated at the 0 timestamp.
755             MemoryKind::Machine(
756                 MiriMemoryKind::Global
757                 | MiriMemoryKind::Machine
758                 | MiriMemoryKind::Env
759                 | MiriMemoryKind::ExternStatic
760                 | MiriMemoryKind::Tls,
761             )
762             | MemoryKind::CallerLocation => (0, VectorIdx::MAX_INDEX),
763         };
764         VClockAlloc {
765             alloc_ranges: RefCell::new(RangeMap::new(
766                 len,
767                 MemoryCellClocks::new(alloc_timestamp, alloc_index),
768             )),
769         }
770     }
771
772     fn reset_clocks(&mut self, offset: Size, len: Size) {
773         let alloc_ranges = self.alloc_ranges.get_mut();
774         for (_, range) in alloc_ranges.iter_mut(offset, len) {
775             // Reset the portion of the range
776             *range = MemoryCellClocks::new(0, VectorIdx::MAX_INDEX);
777         }
778     }
779
780     // Find an index, if one exists where the value
781     // in `l` is greater than the value in `r`.
782     fn find_gt_index(l: &VClock, r: &VClock) -> Option<VectorIdx> {
783         log::trace!("Find index where not {:?} <= {:?}", l, r);
784         let l_slice = l.as_slice();
785         let r_slice = r.as_slice();
786         l_slice
787             .iter()
788             .zip(r_slice.iter())
789             .enumerate()
790             .find_map(|(idx, (&l, &r))| if l > r { Some(idx) } else { None })
791             .or_else(|| {
792                 if l_slice.len() > r_slice.len() {
793                     // By invariant, if l_slice is longer
794                     // then one element must be larger.
795                     // This just validates that this is true
796                     // and reports earlier elements first.
797                     let l_remainder_slice = &l_slice[r_slice.len()..];
798                     let idx = l_remainder_slice
799                         .iter()
800                         .enumerate()
801                         .find_map(|(idx, &r)| if r == 0 { None } else { Some(idx) })
802                         .expect("Invalid VClock Invariant");
803                     Some(idx + r_slice.len())
804                 } else {
805                     None
806                 }
807             })
808             .map(|idx| VectorIdx::new(idx))
809     }
810
811     /// Report a data-race found in the program.
812     /// This finds the two racing threads and the type
813     /// of data-race that occurred. This will also
814     /// return info about the memory location the data-race
815     /// occurred in.
816     #[cold]
817     #[inline(never)]
818     fn report_data_race<'tcx>(
819         global: &MemoryExtra,
820         range: &MemoryCellClocks,
821         action: &str,
822         is_atomic: bool,
823         pointer: Pointer<Tag>,
824         len: Size,
825     ) -> InterpResult<'tcx> {
826         let (current_index, current_clocks) = global.current_thread_state();
827         let write_clock;
828         let (other_action, other_thread, other_clock) = if range.write
829             > current_clocks.clock[range.write_index]
830         {
831             // Convert the write action into the vector clock it
832             // represents for diagnostic purposes.
833             write_clock = VClock::new_with_index(range.write_index, range.write);
834             (range.write_type.get_descriptor(), range.write_index, &write_clock)
835         } else if let Some(idx) = Self::find_gt_index(&range.read, &current_clocks.clock) {
836             ("Read", idx, &range.read)
837         } else if !is_atomic {
838             if let Some(atomic) = range.atomic() {
839                 if let Some(idx) = Self::find_gt_index(&atomic.write_vector, &current_clocks.clock)
840                 {
841                     ("Atomic Store", idx, &atomic.write_vector)
842                 } else if let Some(idx) =
843                     Self::find_gt_index(&atomic.read_vector, &current_clocks.clock)
844                 {
845                     ("Atomic Load", idx, &atomic.read_vector)
846                 } else {
847                     unreachable!(
848                         "Failed to report data-race for non-atomic operation: no race found"
849                     )
850                 }
851             } else {
852                 unreachable!(
853                     "Failed to report data-race for non-atomic operation: no atomic component"
854                 )
855             }
856         } else {
857             unreachable!("Failed to report data-race for atomic operation")
858         };
859
860         // Load elaborated thread information about the racing thread actions.
861         let current_thread_info = global.print_thread_metadata(current_index);
862         let other_thread_info = global.print_thread_metadata(other_thread);
863
864         // Throw the data-race detection.
865         throw_ub_format!(
866             "Data race detected between {} on {} and {} on {}, memory({:?},offset={},size={})\
867             \n(current vector clock = {:?}, conflicting timestamp = {:?})",
868             action,
869             current_thread_info,
870             other_action,
871             other_thread_info,
872             pointer.alloc_id,
873             pointer.offset.bytes(),
874             len.bytes(),
875             current_clocks.clock,
876             other_clock
877         )
878     }
879
880     /// Detect data-races for an unsynchronized read operation, will not perform
881     /// data-race detection if `multi-threaded` is false, either due to no threads
882     /// being created or if it is temporarily disabled during a racy read or write
883     /// operation for which data-race detection is handled separately, for example
884     /// atomic read operations.
885     pub fn read<'tcx>(
886         &self,
887         pointer: Pointer<Tag>,
888         len: Size,
889         global: &GlobalState,
890     ) -> InterpResult<'tcx> {
891         if global.multi_threaded.get() {
892             let (index, clocks) = global.current_thread_state();
893             let mut alloc_ranges = self.alloc_ranges.borrow_mut();
894             for (_, range) in alloc_ranges.iter_mut(pointer.offset, len) {
895                 if let Err(DataRace) = range.read_race_detect(&*clocks, index) {
896                     // Report data-race.
897                     return Self::report_data_race(global, range, "Read", false, pointer, len);
898                 }
899             }
900             Ok(())
901         } else {
902             Ok(())
903         }
904     }
905
906     // Shared code for detecting data-races on unique access to a section of memory
907     fn unique_access<'tcx>(
908         &mut self,
909         pointer: Pointer<Tag>,
910         len: Size,
911         write_type: WriteType,
912         global: &mut GlobalState,
913     ) -> InterpResult<'tcx> {
914         if global.multi_threaded.get() {
915             let (index, clocks) = global.current_thread_state();
916             for (_, range) in self.alloc_ranges.get_mut().iter_mut(pointer.offset, len) {
917                 if let Err(DataRace) = range.write_race_detect(&*clocks, index, write_type) {
918                     // Report data-race
919                     return Self::report_data_race(
920                         global,
921                         range,
922                         write_type.get_descriptor(),
923                         false,
924                         pointer,
925                         len,
926                     );
927                 }
928             }
929             Ok(())
930         } else {
931             Ok(())
932         }
933     }
934
935     /// Detect data-races for an unsynchronized write operation, will not perform
936     /// data-race threads if `multi-threaded` is false, either due to no threads
937     /// being created or if it is temporarily disabled during a racy read or write
938     /// operation
939     pub fn write<'tcx>(
940         &mut self,
941         pointer: Pointer<Tag>,
942         len: Size,
943         global: &mut GlobalState,
944     ) -> InterpResult<'tcx> {
945         self.unique_access(pointer, len, WriteType::Write, global)
946     }
947
948     /// Detect data-races for an unsynchronized deallocate operation, will not perform
949     /// data-race threads if `multi-threaded` is false, either due to no threads
950     /// being created or if it is temporarily disabled during a racy read or write
951     /// operation
952     pub fn deallocate<'tcx>(
953         &mut self,
954         pointer: Pointer<Tag>,
955         len: Size,
956         global: &mut GlobalState,
957     ) -> InterpResult<'tcx> {
958         self.unique_access(pointer, len, WriteType::Deallocate, global)
959     }
960 }
961
962 impl<'mir, 'tcx: 'mir> EvalContextPrivExt<'mir, 'tcx> for MiriEvalContext<'mir, 'tcx> {}
963 trait EvalContextPrivExt<'mir, 'tcx: 'mir>: MiriEvalContextExt<'mir, 'tcx> {
964     // Temporarily allow data-races to occur, this should only be
965     // used if either one of the appropriate `validate_atomic` functions
966     // will be called to treat a memory access as atomic or if the memory
967     // being accessed should be treated as internal state, that cannot be
968     // accessed by the interpreted program.
969     #[inline]
970     fn allow_data_races_ref<R>(&self, op: impl FnOnce(&MiriEvalContext<'mir, 'tcx>) -> R) -> R {
971         let this = self.eval_context_ref();
972         let old = if let Some(data_race) = &this.memory.extra.data_race {
973             data_race.multi_threaded.replace(false)
974         } else {
975             false
976         };
977         let result = op(this);
978         if let Some(data_race) = &this.memory.extra.data_race {
979             data_race.multi_threaded.set(old);
980         }
981         result
982     }
983
984     /// Same as `allow_data_races_ref`, this temporarily disables any data-race detection and
985     /// so should only be used for atomic operations or internal state that the program cannot
986     /// access.
987     #[inline]
988     fn allow_data_races_mut<R>(
989         &mut self,
990         op: impl FnOnce(&mut MiriEvalContext<'mir, 'tcx>) -> R,
991     ) -> R {
992         let this = self.eval_context_mut();
993         let old = if let Some(data_race) = &this.memory.extra.data_race {
994             data_race.multi_threaded.replace(false)
995         } else {
996             false
997         };
998         let result = op(this);
999         if let Some(data_race) = &this.memory.extra.data_race {
1000             data_race.multi_threaded.set(old);
1001         }
1002         result
1003     }
1004
1005     /// Generic atomic operation implementation,
1006     /// this accesses memory via get_raw instead of
1007     /// get_raw_mut, due to issues calling get_raw_mut
1008     /// for atomic loads from read-only memory.
1009     /// FIXME: is this valid, or should get_raw_mut be used for
1010     /// atomic-stores/atomic-rmw?
1011     fn validate_atomic_op<A: Debug + Copy>(
1012         &self,
1013         place: &MPlaceTy<'tcx, Tag>,
1014         atomic: A,
1015         description: &str,
1016         mut op: impl FnMut(
1017             &mut MemoryCellClocks,
1018             &mut ThreadClockSet,
1019             VectorIdx,
1020             A,
1021         ) -> Result<(), DataRace>,
1022     ) -> InterpResult<'tcx> {
1023         let this = self.eval_context_ref();
1024         if let Some(data_race) = &this.memory.extra.data_race {
1025             if data_race.multi_threaded.get() {
1026                 // Load and log the atomic operation.
1027                 // Note that atomic loads are possible even from read-only allocations, so `get_alloc_extra_mut` is not an option.
1028                 let place_ptr = place.ptr.assert_ptr();
1029                 let size = place.layout.size;
1030                 let alloc_meta =
1031                     &this.memory.get_alloc_extra(place_ptr.alloc_id)?.data_race.as_ref().unwrap();
1032                 log::trace!(
1033                     "Atomic op({}) with ordering {:?} on memory({:?}, offset={}, size={})",
1034                     description,
1035                     &atomic,
1036                     place_ptr.alloc_id,
1037                     place_ptr.offset.bytes(),
1038                     size.bytes()
1039                 );
1040
1041                 // Perform the atomic operation.
1042                 data_race.maybe_perform_sync_operation(|index, mut clocks| {
1043                     for (_, range) in
1044                         alloc_meta.alloc_ranges.borrow_mut().iter_mut(place_ptr.offset, size)
1045                     {
1046                         if let Err(DataRace) = op(range, &mut *clocks, index, atomic) {
1047                             mem::drop(clocks);
1048                             return VClockAlloc::report_data_race(
1049                                 data_race,
1050                                 range,
1051                                 description,
1052                                 true,
1053                                 place_ptr,
1054                                 size,
1055                             )
1056                             .map(|_| true);
1057                         }
1058                     }
1059
1060                     // This conservatively assumes all operations have release semantics
1061                     Ok(true)
1062                 })?;
1063
1064                 // Log changes to atomic memory.
1065                 if log::log_enabled!(log::Level::Trace) {
1066                     for (_, range) in alloc_meta.alloc_ranges.borrow().iter(place_ptr.offset, size)
1067                     {
1068                         log::trace!(
1069                             "Updated atomic memory({:?}, offset={}, size={}) to {:#?}",
1070                             place.ptr.assert_ptr().alloc_id,
1071                             place_ptr.offset.bytes(),
1072                             size.bytes(),
1073                             range.atomic_ops
1074                         );
1075                     }
1076                 }
1077             }
1078         }
1079         Ok(())
1080     }
1081 }
1082
1083 /// Extra metadata associated with a thread.
1084 #[derive(Debug, Clone, Default)]
1085 struct ThreadExtraState {
1086     /// The current vector index in use by the
1087     /// thread currently, this is set to None
1088     /// after the vector index has been re-used
1089     /// and hence the value will never need to be
1090     /// read during data-race reporting.
1091     vector_index: Option<VectorIdx>,
1092
1093     /// The name of the thread, updated for better
1094     /// diagnostics when reporting detected data
1095     /// races.
1096     thread_name: Option<Box<str>>,
1097
1098     /// Thread termination vector clock, this
1099     /// is set on thread termination and is used
1100     /// for joining on threads since the vector_index
1101     /// may be re-used when the join operation occurs.
1102     termination_vector_clock: Option<VClock>,
1103 }
1104
1105 /// Global data-race detection state, contains the currently
1106 /// executing thread as well as the vector-clocks associated
1107 /// with each of the threads.
1108 // FIXME: it is probably better to have one large RefCell, than to have so many small ones.
1109 #[derive(Debug, Clone)]
1110 pub struct GlobalState {
1111     /// Set to true once the first additional
1112     /// thread has launched, due to the dependency
1113     /// between before and after a thread launch.
1114     /// Any data-races must be recorded after this
1115     /// so concurrent execution can ignore recording
1116     /// any data-races.
1117     multi_threaded: Cell<bool>,
1118
1119     /// Mapping of a vector index to a known set of thread
1120     /// clocks, this is not directly mapping from a thread id
1121     /// since it may refer to multiple threads.
1122     vector_clocks: RefCell<IndexVec<VectorIdx, ThreadClockSet>>,
1123
1124     /// Mapping of a given vector index to the current thread
1125     /// that the execution is representing, this may change
1126     /// if a vector index is re-assigned to a new thread.
1127     vector_info: RefCell<IndexVec<VectorIdx, ThreadId>>,
1128
1129     /// The mapping of a given thread to associated thread metadata.
1130     thread_info: RefCell<IndexVec<ThreadId, ThreadExtraState>>,
1131
1132     /// The current vector index being executed.
1133     current_index: Cell<VectorIdx>,
1134
1135     /// Potential vector indices that could be re-used on thread creation
1136     /// values are inserted here on after the thread has terminated and
1137     /// been joined with, and hence may potentially become free
1138     /// for use as the index for a new thread.
1139     /// Elements in this set may still require the vector index to
1140     /// report data-races, and can only be re-used after all
1141     /// active vector-clocks catch up with the threads timestamp.
1142     reuse_candidates: RefCell<FxHashSet<VectorIdx>>,
1143
1144     /// Counts the number of threads that are currently active
1145     /// if the number of active threads reduces to 1 and then
1146     /// a join operation occurs with the remaining main thread
1147     /// then multi-threaded execution may be disabled.
1148     active_thread_count: Cell<usize>,
1149
1150     /// This contains threads that have terminated, but not yet joined
1151     /// and so cannot become re-use candidates until a join operation
1152     /// occurs.
1153     /// The associated vector index will be moved into re-use candidates
1154     /// after the join operation occurs.
1155     terminated_threads: RefCell<FxHashMap<ThreadId, VectorIdx>>,
1156 }
1157
1158 impl GlobalState {
1159     /// Create a new global state, setup with just thread-id=0
1160     /// advanced to timestamp = 1.
1161     pub fn new() -> Self {
1162         let mut global_state = GlobalState {
1163             multi_threaded: Cell::new(false),
1164             vector_clocks: RefCell::new(IndexVec::new()),
1165             vector_info: RefCell::new(IndexVec::new()),
1166             thread_info: RefCell::new(IndexVec::new()),
1167             current_index: Cell::new(VectorIdx::new(0)),
1168             active_thread_count: Cell::new(1),
1169             reuse_candidates: RefCell::new(FxHashSet::default()),
1170             terminated_threads: RefCell::new(FxHashMap::default()),
1171         };
1172
1173         // Setup the main-thread since it is not explicitly created:
1174         // uses vector index and thread-id 0, also the rust runtime gives
1175         // the main-thread a name of "main".
1176         let index = global_state.vector_clocks.get_mut().push(ThreadClockSet::default());
1177         global_state.vector_info.get_mut().push(ThreadId::new(0));
1178         global_state.thread_info.get_mut().push(ThreadExtraState {
1179             vector_index: Some(index),
1180             thread_name: Some("main".to_string().into_boxed_str()),
1181             termination_vector_clock: None,
1182         });
1183
1184         global_state
1185     }
1186
1187     // Try to find vector index values that can potentially be re-used
1188     // by a new thread instead of a new vector index being created.
1189     fn find_vector_index_reuse_candidate(&self) -> Option<VectorIdx> {
1190         let mut reuse = self.reuse_candidates.borrow_mut();
1191         let vector_clocks = self.vector_clocks.borrow();
1192         let vector_info = self.vector_info.borrow();
1193         let terminated_threads = self.terminated_threads.borrow();
1194         for &candidate in reuse.iter() {
1195             let target_timestamp = vector_clocks[candidate].clock[candidate];
1196             if vector_clocks.iter_enumerated().all(|(clock_idx, clock)| {
1197                 // The thread happens before the clock, and hence cannot report
1198                 // a data-race with this the candidate index.
1199                 let no_data_race = clock.clock[candidate] >= target_timestamp;
1200
1201                 // The vector represents a thread that has terminated and hence cannot
1202                 // report a data-race with the candidate index.
1203                 let thread_id = vector_info[clock_idx];
1204                 let vector_terminated =
1205                     reuse.contains(&clock_idx) || terminated_threads.contains_key(&thread_id);
1206
1207                 // The vector index cannot report a race with the candidate index
1208                 // and hence allows the candidate index to be re-used.
1209                 no_data_race || vector_terminated
1210             }) {
1211                 // All vector clocks for each vector index are equal to
1212                 // the target timestamp, and the thread is known to have
1213                 // terminated, therefore this vector clock index cannot
1214                 // report any more data-races.
1215                 assert!(reuse.remove(&candidate));
1216                 return Some(candidate);
1217             }
1218         }
1219         None
1220     }
1221
1222     // Hook for thread creation, enabled multi-threaded execution and marks
1223     // the current thread timestamp as happening-before the current thread.
1224     #[inline]
1225     pub fn thread_created(&mut self, thread: ThreadId) {
1226         let current_index = self.current_index();
1227
1228         // Increment the number of active threads.
1229         let active_threads = self.active_thread_count.get();
1230         self.active_thread_count.set(active_threads + 1);
1231
1232         // Enable multi-threaded execution, there are now two threads
1233         // so data-races are now possible.
1234         self.multi_threaded.set(true);
1235
1236         // Load and setup the associated thread metadata
1237         let mut thread_info = self.thread_info.borrow_mut();
1238         thread_info.ensure_contains_elem(thread, Default::default);
1239
1240         // Assign a vector index for the thread, attempting to re-use an old
1241         // vector index that can no longer report any data-races if possible.
1242         let created_index = if let Some(reuse_index) = self.find_vector_index_reuse_candidate() {
1243             // Now re-configure the re-use candidate, increment the clock
1244             // for the new sync use of the vector.
1245             let vector_clocks = self.vector_clocks.get_mut();
1246             vector_clocks[reuse_index].increment_clock(reuse_index);
1247
1248             // Locate the old thread the vector was associated with and update
1249             // it to represent the new thread instead.
1250             let vector_info = self.vector_info.get_mut();
1251             let old_thread = vector_info[reuse_index];
1252             vector_info[reuse_index] = thread;
1253
1254             // Mark the thread the vector index was associated with as no longer
1255             // representing a thread index.
1256             thread_info[old_thread].vector_index = None;
1257
1258             reuse_index
1259         } else {
1260             // No vector re-use candidates available, instead create
1261             // a new vector index.
1262             let vector_info = self.vector_info.get_mut();
1263             vector_info.push(thread)
1264         };
1265
1266         log::trace!("Creating thread = {:?} with vector index = {:?}", thread, created_index);
1267
1268         // Mark the chosen vector index as in use by the thread.
1269         thread_info[thread].vector_index = Some(created_index);
1270
1271         // Create a thread clock set if applicable.
1272         let vector_clocks = self.vector_clocks.get_mut();
1273         if created_index == vector_clocks.next_index() {
1274             vector_clocks.push(ThreadClockSet::default());
1275         }
1276
1277         // Now load the two clocks and configure the initial state.
1278         let (current, created) = vector_clocks.pick2_mut(current_index, created_index);
1279
1280         // Join the created with current, since the current threads
1281         // previous actions happen-before the created thread.
1282         created.join_with(current);
1283
1284         // Advance both threads after the synchronized operation.
1285         // Both operations are considered to have release semantics.
1286         current.increment_clock(current_index);
1287         created.increment_clock(created_index);
1288     }
1289
1290     /// Hook on a thread join to update the implicit happens-before relation
1291     /// between the joined thread and the current thread.
1292     #[inline]
1293     pub fn thread_joined(&mut self, current_thread: ThreadId, join_thread: ThreadId) {
1294         let clocks_vec = self.vector_clocks.get_mut();
1295         let thread_info = self.thread_info.get_mut();
1296
1297         // Load the vector clock of the current thread.
1298         let current_index = thread_info[current_thread]
1299             .vector_index
1300             .expect("Performed thread join on thread with no assigned vector");
1301         let current = &mut clocks_vec[current_index];
1302
1303         // Load the associated vector clock for the terminated thread.
1304         let join_clock = thread_info[join_thread]
1305             .termination_vector_clock
1306             .as_ref()
1307             .expect("Joined with thread but thread has not terminated");
1308
1309         // The join thread happens-before the current thread
1310         // so update the current vector clock.
1311         // Is not a release operation so the clock is not incremented.
1312         current.clock.join(join_clock);
1313
1314         // Check the number of active threads, if the value is 1
1315         // then test for potentially disabling multi-threaded execution.
1316         let active_threads = self.active_thread_count.get();
1317         if active_threads == 1 {
1318             // May potentially be able to disable multi-threaded execution.
1319             let current_clock = &clocks_vec[current_index];
1320             if clocks_vec
1321                 .iter_enumerated()
1322                 .all(|(idx, clocks)| clocks.clock[idx] <= current_clock.clock[idx])
1323             {
1324                 // All thread terminations happen-before the current clock
1325                 // therefore no data-races can be reported until a new thread
1326                 // is created, so disable multi-threaded execution.
1327                 self.multi_threaded.set(false);
1328             }
1329         }
1330
1331         // If the thread is marked as terminated but not joined
1332         // then move the thread to the re-use set.
1333         let termination = self.terminated_threads.get_mut();
1334         if let Some(index) = termination.remove(&join_thread) {
1335             let reuse = self.reuse_candidates.get_mut();
1336             reuse.insert(index);
1337         }
1338     }
1339
1340     /// On thread termination, the vector-clock may re-used
1341     /// in the future once all remaining thread-clocks catch
1342     /// up with the time index of the terminated thread.
1343     /// This assigns thread termination with a unique index
1344     /// which will be used to join the thread
1345     /// This should be called strictly before any calls to
1346     /// `thread_joined`.
1347     #[inline]
1348     pub fn thread_terminated(&mut self) {
1349         let current_index = self.current_index();
1350
1351         // Increment the clock to a unique termination timestamp.
1352         let vector_clocks = self.vector_clocks.get_mut();
1353         let current_clocks = &mut vector_clocks[current_index];
1354         current_clocks.increment_clock(current_index);
1355
1356         // Load the current thread id for the executing vector.
1357         let vector_info = self.vector_info.get_mut();
1358         let current_thread = vector_info[current_index];
1359
1360         // Load the current thread metadata, and move to a terminated
1361         // vector state. Setting up the vector clock all join operations
1362         // will use.
1363         let thread_info = self.thread_info.get_mut();
1364         let current = &mut thread_info[current_thread];
1365         current.termination_vector_clock = Some(current_clocks.clock.clone());
1366
1367         // Add this thread as a candidate for re-use after a thread join
1368         // occurs.
1369         let termination = self.terminated_threads.get_mut();
1370         termination.insert(current_thread, current_index);
1371
1372         // Reduce the number of active threads, now that a thread has
1373         // terminated.
1374         let mut active_threads = self.active_thread_count.get();
1375         active_threads -= 1;
1376         self.active_thread_count.set(active_threads);
1377     }
1378
1379     /// Hook for updating the local tracker of the currently
1380     /// enabled thread, should always be updated whenever
1381     /// `active_thread` in thread.rs is updated.
1382     #[inline]
1383     pub fn thread_set_active(&self, thread: ThreadId) {
1384         let thread_info = self.thread_info.borrow();
1385         let vector_idx = thread_info[thread]
1386             .vector_index
1387             .expect("Setting thread active with no assigned vector");
1388         self.current_index.set(vector_idx);
1389     }
1390
1391     /// Hook for updating the local tracker of the threads name
1392     /// this should always mirror the local value in thread.rs
1393     /// the thread name is used for improved diagnostics
1394     /// during a data-race.
1395     #[inline]
1396     pub fn thread_set_name(&mut self, thread: ThreadId, name: String) {
1397         let name = name.into_boxed_str();
1398         let thread_info = self.thread_info.get_mut();
1399         thread_info[thread].thread_name = Some(name);
1400     }
1401
1402     /// Attempt to perform a synchronized operation, this
1403     /// will perform no operation if multi-threading is
1404     /// not currently enabled.
1405     /// Otherwise it will increment the clock for the current
1406     /// vector before and after the operation for data-race
1407     /// detection between any happens-before edges the
1408     /// operation may create.
1409     fn maybe_perform_sync_operation<'tcx>(
1410         &self,
1411         op: impl FnOnce(VectorIdx, RefMut<'_, ThreadClockSet>) -> InterpResult<'tcx, bool>,
1412     ) -> InterpResult<'tcx> {
1413         if self.multi_threaded.get() {
1414             let (index, clocks) = self.current_thread_state_mut();
1415             if op(index, clocks)? {
1416                 let (_, mut clocks) = self.current_thread_state_mut();
1417                 clocks.increment_clock(index);
1418             }
1419         }
1420         Ok(())
1421     }
1422
1423     /// Internal utility to identify a thread stored internally
1424     /// returns the id and the name for better diagnostics.
1425     fn print_thread_metadata(&self, vector: VectorIdx) -> String {
1426         let thread = self.vector_info.borrow()[vector];
1427         let thread_name = &self.thread_info.borrow()[thread].thread_name;
1428         if let Some(name) = thread_name {
1429             let name: &str = name;
1430             format!("Thread(id = {:?}, name = {:?})", thread.to_u32(), &*name)
1431         } else {
1432             format!("Thread(id = {:?})", thread.to_u32())
1433         }
1434     }
1435
1436     /// Acquire a lock, express that the previous call of
1437     /// `validate_lock_release` must happen before this.
1438     /// As this is an acquire operation, the thread timestamp is not
1439     /// incremented.
1440     pub fn validate_lock_acquire(&self, lock: &VClock, thread: ThreadId) {
1441         let (_, mut clocks) = self.load_thread_state_mut(thread);
1442         clocks.clock.join(&lock);
1443     }
1444
1445     /// Release a lock handle, express that this happens-before
1446     /// any subsequent calls to `validate_lock_acquire`.
1447     /// For normal locks this should be equivalent to `validate_lock_release_shared`
1448     /// since an acquire operation should have occurred before, however
1449     /// for futex & condvar operations this is not the case and this
1450     /// operation must be used.
1451     pub fn validate_lock_release(&self, lock: &mut VClock, thread: ThreadId) {
1452         let (index, mut clocks) = self.load_thread_state_mut(thread);
1453         lock.clone_from(&clocks.clock);
1454         clocks.increment_clock(index);
1455     }
1456
1457     /// Release a lock handle, express that this happens-before
1458     /// any subsequent calls to `validate_lock_acquire` as well
1459     /// as any previous calls to this function after any
1460     /// `validate_lock_release` calls.
1461     /// For normal locks this should be equivalent to `validate_lock_release`.
1462     /// This function only exists for joining over the set of concurrent readers
1463     /// in a read-write lock and should not be used for anything else.
1464     pub fn validate_lock_release_shared(&self, lock: &mut VClock, thread: ThreadId) {
1465         let (index, mut clocks) = self.load_thread_state_mut(thread);
1466         lock.join(&clocks.clock);
1467         clocks.increment_clock(index);
1468     }
1469
1470     /// Load the vector index used by the given thread as well as the set of vector clocks
1471     /// used by the thread.
1472     #[inline]
1473     fn load_thread_state_mut(&self, thread: ThreadId) -> (VectorIdx, RefMut<'_, ThreadClockSet>) {
1474         let index = self.thread_info.borrow()[thread]
1475             .vector_index
1476             .expect("Loading thread state for thread with no assigned vector");
1477         let ref_vector = self.vector_clocks.borrow_mut();
1478         let clocks = RefMut::map(ref_vector, |vec| &mut vec[index]);
1479         (index, clocks)
1480     }
1481
1482     /// Load the current vector clock in use and the current set of thread clocks
1483     /// in use for the vector.
1484     #[inline]
1485     fn current_thread_state(&self) -> (VectorIdx, Ref<'_, ThreadClockSet>) {
1486         let index = self.current_index();
1487         let ref_vector = self.vector_clocks.borrow();
1488         let clocks = Ref::map(ref_vector, |vec| &vec[index]);
1489         (index, clocks)
1490     }
1491
1492     /// Load the current vector clock in use and the current set of thread clocks
1493     /// in use for the vector mutably for modification.
1494     #[inline]
1495     fn current_thread_state_mut(&self) -> (VectorIdx, RefMut<'_, ThreadClockSet>) {
1496         let index = self.current_index();
1497         let ref_vector = self.vector_clocks.borrow_mut();
1498         let clocks = RefMut::map(ref_vector, |vec| &mut vec[index]);
1499         (index, clocks)
1500     }
1501
1502     /// Return the current thread, should be the same
1503     /// as the data-race active thread.
1504     #[inline]
1505     fn current_index(&self) -> VectorIdx {
1506         self.current_index.get()
1507     }
1508 }