@@ -191,7 +191,8 @@ struct AtomicMemoryCellClocks {
191191 /// The size of accesses to this atomic location.
192192 /// We use this to detect non-synchronized mixed-size accesses. Since all accesses must be
193193 /// aligned to their size, this is sufficient to detect imperfectly overlapping accesses.
194- size : Size ,
194+ /// `None` indicates that we saw multiple different sizes, which is okay as long as all accesses are reads.
195+ size : Option < Size > ,
195196}
196197
197198#[ derive( Copy , Clone , PartialEq , Eq , Debug ) ]
@@ -265,6 +266,14 @@ impl AccessType {
265266 let mut msg = String :: new ( ) ;
266267
267268 if let Some ( size) = size {
269+ if size == Size :: ZERO {
270+ // In this case there were multiple read accesss with different sizes and then a write.
271+ // We will be reporting *one* of the other reads, but we don't have enough information
272+ // to determine which one had which size.
273+ assert ! ( self == AccessType :: AtomicLoad ) ;
274+ assert ! ( ty. is_none( ) ) ;
275+ return format ! ( "multiple differently-sized atomic loads, including one load" ) ;
276+ }
268277 msg. push_str ( & format ! ( "{}-byte {}" , size. bytes( ) , msg) )
269278 }
270279
@@ -305,8 +314,7 @@ impl AccessType {
305314 }
306315}
307316
308- /// Memory Cell vector clock metadata
309- /// for data-race detection.
317+ /// Per-byte vector clock metadata for data-race detection.
310318#[ derive( Clone , PartialEq , Eq , Debug ) ]
311319struct MemoryCellClocks {
312320 /// The vector-clock timestamp and the thread that did the last non-atomic write. We don't need
@@ -325,8 +333,8 @@ struct MemoryCellClocks {
325333 read : VClock ,
326334
327335 /// Atomic access, acquire, release sequence tracking clocks.
328- /// For non-atomic memory in the common case this
329- /// value is set to None .
336+ /// For non-atomic memory this value is set to None.
337+ /// For atomic memory, each byte carries this information .
330338 atomic_ops : Option < Box < AtomicMemoryCellClocks > > ,
331339}
332340
@@ -336,7 +344,7 @@ impl AtomicMemoryCellClocks {
336344 read_vector : Default :: default ( ) ,
337345 write_vector : Default :: default ( ) ,
338346 sync_vector : Default :: default ( ) ,
339- size,
347+ size : Some ( size ) ,
340348 }
341349 }
342350}
@@ -383,17 +391,23 @@ impl MemoryCellClocks {
383391 & mut self ,
384392 thread_clocks : & ThreadClockSet ,
385393 size : Size ,
394+ write : bool ,
386395 ) -> Result < & mut AtomicMemoryCellClocks , DataRace > {
387396 match self . atomic_ops {
388397 Some ( ref mut atomic) => {
389398 // We are good if the size is the same or all atomic accesses are before our current time.
390- if atomic. size == size {
399+ if atomic. size == Some ( size) {
391400 Ok ( atomic)
392401 } else if atomic. read_vector <= thread_clocks. clock
393402 && atomic. write_vector <= thread_clocks. clock
394403 {
395- // This is now the new size that must be used for accesses here.
396- atomic. size = size;
404+ // We are fully ordered after all previous accesses, so we can change the size.
405+ atomic. size = Some ( size) ;
406+ Ok ( atomic)
407+ } else if !write && atomic. write_vector <= thread_clocks. clock {
408+ // This is a read, and it is ordered after the last write. It's okay for the
409+ // sizes to mismatch, as long as no writes with a different size occur later.
410+ atomic. size = None ;
397411 Ok ( atomic)
398412 } else {
399413 Err ( DataRace )
@@ -499,7 +513,7 @@ impl MemoryCellClocks {
499513 Ok ( ( ) )
500514 }
501515
502- /// Detect data-races with an atomic read, caused by a non-atomic access that does
516+ /// Detect data-races with an atomic read, caused by a non-atomic write that does
503517 /// not happen-before the atomic-read.
504518 fn atomic_read_detect (
505519 & mut self ,
@@ -508,14 +522,10 @@ impl MemoryCellClocks {
508522 access_size : Size ,
509523 ) -> Result < ( ) , DataRace > {
510524 trace ! ( "Atomic read with vectors: {:#?} :: {:#?}" , self , thread_clocks) ;
511- let atomic = self . atomic_access ( thread_clocks, access_size) ?;
525+ let atomic = self . atomic_access ( thread_clocks, access_size, /*write*/ false ) ?;
512526 atomic. read_vector . set_at_index ( & thread_clocks. clock , index) ;
513- // Make sure the last non-atomic write and all non-atomic reads were before this access.
514- if self . write_was_before ( & thread_clocks. clock ) && self . read <= thread_clocks. clock {
515- Ok ( ( ) )
516- } else {
517- Err ( DataRace )
518- }
527+ // Make sure the last non-atomic write was before this access.
528+ if self . write_was_before ( & thread_clocks. clock ) { Ok ( ( ) ) } else { Err ( DataRace ) }
519529 }
520530
521531 /// Detect data-races with an atomic write, either with a non-atomic read or with
@@ -527,7 +537,7 @@ impl MemoryCellClocks {
527537 access_size : Size ,
528538 ) -> Result < ( ) , DataRace > {
529539 trace ! ( "Atomic write with vectors: {:#?} :: {:#?}" , self , thread_clocks) ;
530- let atomic = self . atomic_access ( thread_clocks, access_size) ?;
540+ let atomic = self . atomic_access ( thread_clocks, access_size, /*write*/ true ) ?;
531541 atomic. write_vector . set_at_index ( & thread_clocks. clock , index) ;
532542 // Make sure the last non-atomic write and all non-atomic reads were before this access.
533543 if self . write_was_before ( & thread_clocks. clock ) && self . read <= thread_clocks. clock {
@@ -552,11 +562,9 @@ impl MemoryCellClocks {
552562 }
553563 thread_clocks. clock . index_mut ( index) . set_read_type ( read_type) ;
554564 if self . write_was_before ( & thread_clocks. clock ) {
565+ // We must be ordered-after all atomic writes.
555566 let race_free = if let Some ( atomic) = self . atomic ( ) {
556- // We must be ordered-after all atomic accesses, reads and writes.
557- // This ensures we don't mix atomic and non-atomic accesses.
558567 atomic. write_vector <= thread_clocks. clock
559- && atomic. read_vector <= thread_clocks. clock
560568 } else {
561569 true
562570 } ;
@@ -957,9 +965,7 @@ impl VClockAlloc {
957965 let mut other_size = None ; // if `Some`, this was a size-mismatch race
958966 let write_clock;
959967 let ( other_access, other_thread, other_clock) =
960- // First check the atomic-nonatomic cases. If it looks like multiple
961- // cases apply, this one should take precedence, else it might look like
962- // we are reporting races between two non-atomic reads.
968+ // First check the atomic-nonatomic cases.
963969 if !access. is_atomic ( ) &&
964970 let Some ( atomic) = mem_clocks. atomic ( ) &&
965971 let Some ( idx) = Self :: find_gt_index ( & atomic. write_vector , & active_clocks. clock )
@@ -977,10 +983,10 @@ impl VClockAlloc {
977983 } else if let Some ( idx) = Self :: find_gt_index ( & mem_clocks. read , & active_clocks. clock ) {
978984 ( AccessType :: NaRead ( mem_clocks. read [ idx] . read_type ( ) ) , idx, & mem_clocks. read )
979985 // Finally, mixed-size races.
980- } else if access. is_atomic ( ) && let Some ( atomic) = mem_clocks. atomic ( ) && atomic. size != access_size {
986+ } else if access. is_atomic ( ) && let Some ( atomic) = mem_clocks. atomic ( ) && atomic. size != Some ( access_size) {
981987 // This is only a race if we are not synchronized with all atomic accesses, so find
982988 // the one we are not synchronized with.
983- other_size = Some ( atomic. size ) ;
989+ other_size = Some ( atomic. size . unwrap_or ( Size :: ZERO ) ) ;
984990 if let Some ( idx) = Self :: find_gt_index ( & atomic. write_vector , & active_clocks. clock )
985991 {
986992 ( AccessType :: AtomicStore , idx, & atomic. write_vector )
@@ -1007,10 +1013,7 @@ impl VClockAlloc {
10071013 assert ! ( !involves_non_atomic) ;
10081014 Some ( "overlapping unsynchronized atomic accesses must use the same access size" )
10091015 } else if access. is_read ( ) && other_access. is_read ( ) {
1010- assert ! ( involves_non_atomic) ;
1011- Some (
1012- "overlapping atomic and non-atomic accesses must be synchronized, even if both are read-only" ,
1013- )
1016+ panic ! ( "there should be no same-size read-read races" )
10141017 } else {
10151018 None
10161019 } ;
0 commit comments