@@ -108,19 +108,19 @@ pub(super) struct ThreadClockSet {
108
108
fence_acquire : VClock ,
109
109
110
110
/// The last timestamp of happens-before relations that
111
- /// have been released by this thread by a fence.
111
+ /// have been released by this thread by a release fence.
112
112
fence_release : VClock ,
113
113
114
- /// Timestamps of the last SC fence performed by each
115
- /// thread, updated when this thread performs an SC fence
116
- pub ( super ) fence_seqcst : VClock ,
117
-
118
114
/// Timestamps of the last SC write performed by each
119
- /// thread, updated when this thread performs an SC fence
115
+ /// thread, updated when this thread performs an SC fence.
116
+ /// This is never acquired into the thread's clock, it
117
+ /// just limits which old writes can be seen in weak memory emulation.
120
118
pub ( super ) write_seqcst : VClock ,
121
119
122
120
/// Timestamps of the last SC fence performed by each
123
- /// thread, updated when this thread performs an SC read
121
+ /// thread, updated when this thread performs an SC read.
122
+ /// This is never acquired into the thread's clock, it
123
+ /// just limits which old writes can be seen in weak memory emulation.
124
124
pub ( super ) read_seqcst : VClock ,
125
125
}
126
126
@@ -256,6 +256,106 @@ enum AccessType {
256
256
AtomicRmw ,
257
257
}
258
258
259
+ /// Per-byte vector clock metadata for data-race detection.
260
+ #[ derive( Clone , PartialEq , Eq , Debug ) ]
261
+ struct MemoryCellClocks {
262
+ /// The vector-clock timestamp and the thread that did the last non-atomic write. We don't need
263
+ /// a full `VClock` here, it's always a single thread and nothing synchronizes, so the effective
264
+ /// clock is all-0 except for the thread that did the write.
265
+ write : ( VectorIdx , VTimestamp ) ,
266
+
267
+ /// The type of operation that the write index represents,
268
+ /// either newly allocated memory, a non-atomic write or
269
+ /// a deallocation of memory.
270
+ write_type : NaWriteType ,
271
+
272
+ /// The vector-clock of all non-atomic reads that happened since the last non-atomic write
273
+ /// (i.e., we join together the "singleton" clocks corresponding to each read). It is reset to
274
+ /// zero on each write operation.
275
+ read : VClock ,
276
+
277
+ /// Atomic access, acquire, release sequence tracking clocks.
278
+ /// For non-atomic memory this value is set to None.
279
+ /// For atomic memory, each byte carries this information.
280
+ atomic_ops : Option < Box < AtomicMemoryCellClocks > > ,
281
+ }
282
+
283
+ /// Extra metadata associated with a thread.
284
+ #[ derive( Debug , Clone , Default ) ]
285
+ struct ThreadExtraState {
286
+ /// The current vector index in use by the
287
+ /// thread currently, this is set to None
288
+ /// after the vector index has been re-used
289
+ /// and hence the value will never need to be
290
+ /// read during data-race reporting.
291
+ vector_index : Option < VectorIdx > ,
292
+
293
+ /// Thread termination vector clock, this
294
+ /// is set on thread termination and is used
295
+ /// for joining on threads since the vector_index
296
+ /// may be re-used when the join operation occurs.
297
+ termination_vector_clock : Option < VClock > ,
298
+ }
299
+
300
+ /// Global data-race detection state, contains the currently
301
+ /// executing thread as well as the vector-clocks associated
302
+ /// with each of the threads.
303
+ // FIXME: it is probably better to have one large RefCell, than to have so many small ones.
304
+ #[ derive( Debug , Clone ) ]
305
+ pub struct GlobalState {
306
+ /// Set to true once the first additional
307
+ /// thread has launched, due to the dependency
308
+ /// between before and after a thread launch.
309
+ /// Any data-races must be recorded after this
310
+ /// so concurrent execution can ignore recording
311
+ /// any data-races.
312
+ multi_threaded : Cell < bool > ,
313
+
314
+ /// A flag to mark we are currently performing
315
+ /// a data race free action (such as atomic access)
316
+ /// to suppress the race detector
317
+ ongoing_action_data_race_free : Cell < bool > ,
318
+
319
+ /// Mapping of a vector index to a known set of thread
320
+ /// clocks, this is not directly mapping from a thread id
321
+ /// since it may refer to multiple threads.
322
+ vector_clocks : RefCell < IndexVec < VectorIdx , ThreadClockSet > > ,
323
+
324
+ /// Mapping of a given vector index to the current thread
325
+ /// that the execution is representing, this may change
326
+ /// if a vector index is re-assigned to a new thread.
327
+ vector_info : RefCell < IndexVec < VectorIdx , ThreadId > > ,
328
+
329
+ /// The mapping of a given thread to associated thread metadata.
330
+ thread_info : RefCell < IndexVec < ThreadId , ThreadExtraState > > ,
331
+
332
+ /// Potential vector indices that could be re-used on thread creation
333
+ /// values are inserted here on after the thread has terminated and
334
+ /// been joined with, and hence may potentially become free
335
+ /// for use as the index for a new thread.
336
+ /// Elements in this set may still require the vector index to
337
+ /// report data-races, and can only be re-used after all
338
+ /// active vector-clocks catch up with the threads timestamp.
339
+ reuse_candidates : RefCell < FxHashSet < VectorIdx > > ,
340
+
341
+ /// We make SC fences act like RMWs on a global location.
342
+ /// To implement that, they all release and acquire into this clock.
343
+ last_sc_fence : RefCell < VClock > ,
344
+
345
+ /// The timestamp of last SC write performed by each thread.
346
+ /// Threads only update their own index here!
347
+ last_sc_write_per_thread : RefCell < VClock > ,
348
+
349
+ /// Track when an outdated (weak memory) load happens.
350
+ pub track_outdated_loads : bool ,
351
+ }
352
+
353
+ impl VisitProvenance for GlobalState {
354
+ fn visit_provenance ( & self , _visit : & mut VisitWith < ' _ > ) {
355
+ // We don't have any tags.
356
+ }
357
+ }
358
+
259
359
impl AccessType {
260
360
fn description ( self , ty : Option < Ty < ' _ > > , size : Option < Size > ) -> String {
261
361
let mut msg = String :: new ( ) ;
@@ -309,30 +409,6 @@ impl AccessType {
309
409
}
310
410
}
311
411
312
- /// Per-byte vector clock metadata for data-race detection.
313
- #[ derive( Clone , PartialEq , Eq , Debug ) ]
314
- struct MemoryCellClocks {
315
- /// The vector-clock timestamp and the thread that did the last non-atomic write. We don't need
316
- /// a full `VClock` here, it's always a single thread and nothing synchronizes, so the effective
317
- /// clock is all-0 except for the thread that did the write.
318
- write : ( VectorIdx , VTimestamp ) ,
319
-
320
- /// The type of operation that the write index represents,
321
- /// either newly allocated memory, a non-atomic write or
322
- /// a deallocation of memory.
323
- write_type : NaWriteType ,
324
-
325
- /// The vector-clock of all non-atomic reads that happened since the last non-atomic write
326
- /// (i.e., we join together the "singleton" clocks corresponding to each read). It is reset to
327
- /// zero on each write operation.
328
- read : VClock ,
329
-
330
- /// Atomic access, acquire, release sequence tracking clocks.
331
- /// For non-atomic memory this value is set to None.
332
- /// For atomic memory, each byte carries this information.
333
- atomic_ops : Option < Box < AtomicMemoryCellClocks > > ,
334
- }
335
-
336
412
impl AtomicMemoryCellClocks {
337
413
fn new ( size : Size ) -> Self {
338
414
AtomicMemoryCellClocks {
@@ -803,9 +879,17 @@ pub trait EvalContextExt<'tcx>: MiriInterpCxExt<'tcx> {
803
879
clocks. apply_release_fence ( ) ;
804
880
}
805
881
if atomic == AtomicFenceOrd :: SeqCst {
806
- data_race. last_sc_fence . borrow_mut ( ) . set_at_index ( & clocks. clock , index) ;
807
- clocks. fence_seqcst . join ( & data_race. last_sc_fence . borrow ( ) ) ;
808
- clocks. write_seqcst . join ( & data_race. last_sc_write . borrow ( ) ) ;
882
+ // Behave like an RMW on the global fence location. This takes full care of
883
+ // all the SC fence requirements, including C++17 §32.4 [atomics.order]
884
+ // paragraph 6 (which would limit what future reads can see). It also rules
885
+ // out many legal behaviors, but we don't currently have a model that would
886
+ // be more precise.
887
+ let mut sc_fence_clock = data_race. last_sc_fence . borrow_mut ( ) ;
888
+ sc_fence_clock. join ( & clocks. clock ) ;
889
+ clocks. clock . join ( & sc_fence_clock) ;
890
+ // Also establish some sort of order with the last SC write that happened, globally
891
+ // (but this is only respected by future reads).
892
+ clocks. write_seqcst . join ( & data_race. last_sc_write_per_thread . borrow ( ) ) ;
809
893
}
810
894
811
895
// Increment timestamp in case of release semantics.
@@ -1463,80 +1547,6 @@ trait EvalContextPrivExt<'tcx>: MiriInterpCxExt<'tcx> {
1463
1547
}
1464
1548
}
1465
1549
1466
- /// Extra metadata associated with a thread.
1467
- #[ derive( Debug , Clone , Default ) ]
1468
- struct ThreadExtraState {
1469
- /// The current vector index in use by the
1470
- /// thread currently, this is set to None
1471
- /// after the vector index has been re-used
1472
- /// and hence the value will never need to be
1473
- /// read during data-race reporting.
1474
- vector_index : Option < VectorIdx > ,
1475
-
1476
- /// Thread termination vector clock, this
1477
- /// is set on thread termination and is used
1478
- /// for joining on threads since the vector_index
1479
- /// may be re-used when the join operation occurs.
1480
- termination_vector_clock : Option < VClock > ,
1481
- }
1482
-
1483
- /// Global data-race detection state, contains the currently
1484
- /// executing thread as well as the vector-clocks associated
1485
- /// with each of the threads.
1486
- // FIXME: it is probably better to have one large RefCell, than to have so many small ones.
1487
- #[ derive( Debug , Clone ) ]
1488
- pub struct GlobalState {
1489
- /// Set to true once the first additional
1490
- /// thread has launched, due to the dependency
1491
- /// between before and after a thread launch.
1492
- /// Any data-races must be recorded after this
1493
- /// so concurrent execution can ignore recording
1494
- /// any data-races.
1495
- multi_threaded : Cell < bool > ,
1496
-
1497
- /// A flag to mark we are currently performing
1498
- /// a data race free action (such as atomic access)
1499
- /// to suppress the race detector
1500
- ongoing_action_data_race_free : Cell < bool > ,
1501
-
1502
- /// Mapping of a vector index to a known set of thread
1503
- /// clocks, this is not directly mapping from a thread id
1504
- /// since it may refer to multiple threads.
1505
- vector_clocks : RefCell < IndexVec < VectorIdx , ThreadClockSet > > ,
1506
-
1507
- /// Mapping of a given vector index to the current thread
1508
- /// that the execution is representing, this may change
1509
- /// if a vector index is re-assigned to a new thread.
1510
- vector_info : RefCell < IndexVec < VectorIdx , ThreadId > > ,
1511
-
1512
- /// The mapping of a given thread to associated thread metadata.
1513
- thread_info : RefCell < IndexVec < ThreadId , ThreadExtraState > > ,
1514
-
1515
- /// Potential vector indices that could be re-used on thread creation
1516
- /// values are inserted here on after the thread has terminated and
1517
- /// been joined with, and hence may potentially become free
1518
- /// for use as the index for a new thread.
1519
- /// Elements in this set may still require the vector index to
1520
- /// report data-races, and can only be re-used after all
1521
- /// active vector-clocks catch up with the threads timestamp.
1522
- reuse_candidates : RefCell < FxHashSet < VectorIdx > > ,
1523
-
1524
- /// The timestamp of last SC fence performed by each thread
1525
- last_sc_fence : RefCell < VClock > ,
1526
-
1527
- /// The timestamp of last SC write performed by each thread
1528
- last_sc_write : RefCell < VClock > ,
1529
-
1530
- /// Track when an outdated (weak memory) load happens.
1531
- pub track_outdated_loads : bool ,
1532
- }
1533
-
1534
- impl VisitProvenance for GlobalState {
1535
- fn visit_provenance ( & self , _visit : & mut VisitWith < ' _ > ) {
1536
- // We don't have any tags.
1537
- }
1538
- }
1539
-
1540
1550
impl GlobalState {
1541
1551
/// Create a new global state, setup with just thread-id=0
1542
1552
/// advanced to timestamp = 1.
@@ -1549,7 +1559,7 @@ impl GlobalState {
1549
1559
thread_info : RefCell :: new ( IndexVec :: new ( ) ) ,
1550
1560
reuse_candidates : RefCell :: new ( FxHashSet :: default ( ) ) ,
1551
1561
last_sc_fence : RefCell :: new ( VClock :: default ( ) ) ,
1552
- last_sc_write : RefCell :: new ( VClock :: default ( ) ) ,
1562
+ last_sc_write_per_thread : RefCell :: new ( VClock :: default ( ) ) ,
1553
1563
track_outdated_loads : config. track_outdated_loads ,
1554
1564
} ;
1555
1565
@@ -1851,7 +1861,7 @@ impl GlobalState {
1851
1861
// SC ATOMIC STORE rule in the paper.
1852
1862
pub ( super ) fn sc_write ( & self , thread_mgr : & ThreadManager < ' _ > ) {
1853
1863
let ( index, clocks) = self . active_thread_state ( thread_mgr) ;
1854
- self . last_sc_write . borrow_mut ( ) . set_at_index ( & clocks. clock , index) ;
1864
+ self . last_sc_write_per_thread . borrow_mut ( ) . set_at_index ( & clocks. clock , index) ;
1855
1865
}
1856
1866
1857
1867
// SC ATOMIC READ rule in the paper.
0 commit comments