Skip to content

Commit 03733e2

Browse files
committed
Auto merge of #130979 - matthiaskrgr:rollup-u7ylca5, r=matthiaskrgr
Rollup of 5 pull requests Successful merges: - #128778 (atomics: allow atomic and non-atomic reads to race) - #130918 (simplify LLVM submodule handling) - #130960 (Only add an automatic SONAME for Rust dylibs) - #130973 (compiletest: rename "runtest/crash.rs" to "runtest/crashes.rs" to be in line with the test directory) - #130976 (remove couple redundant clones) r? `@ghost` `@rustbot` modify labels: rollup
2 parents 080eb0d + 4bae8d4 commit 03733e2

18 files changed

+262
-275
lines changed

src/concurrency/data_race.rs

+33-30
Original file line numberDiff line numberDiff line change
@@ -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)]
311319
struct 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
};

tests/fail/data_race/mixed_size_read.rs

-28
This file was deleted.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
error: Undefined Behavior: Race condition detected between (1) multiple differently-sized atomic loads, including one load on thread `unnamed-ID` and (2) 2-byte atomic store on thread `unnamed-ID` at ALLOC. (2) just happened here
2+
--> tests/fail/data_race/mixed_size_read_read_write.rs:LL:CC
3+
|
4+
LL | a16.store(0, Ordering::SeqCst);
5+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Race condition detected between (1) multiple differently-sized atomic loads, including one load on thread `unnamed-ID` and (2) 2-byte atomic store on thread `unnamed-ID` at ALLOC. (2) just happened here
6+
|
7+
help: and (1) occurred earlier here
8+
--> tests/fail/data_race/mixed_size_read_read_write.rs:LL:CC
9+
|
10+
LL | a16.load(Ordering::SeqCst);
11+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^
12+
= help: overlapping unsynchronized atomic accesses must use the same access size
13+
= help: see https://doc.rust-lang.org/nightly/std/sync/atomic/index.html#memory-model-for-atomic-accesses for more information about the Rust memory model
14+
= help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior
15+
= help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information
16+
= note: BACKTRACE (of the first span) on thread `unnamed-ID`:
17+
= note: inside closure at tests/fail/data_race/mixed_size_read_read_write.rs:LL:CC
18+
19+
note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace
20+
21+
error: aborting due to 1 previous error
22+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
error: Undefined Behavior: Race condition detected between (1) multiple differently-sized atomic loads, including one load on thread `unnamed-ID` and (2) 1-byte atomic store on thread `unnamed-ID` at ALLOC. (2) just happened here
2+
--> tests/fail/data_race/mixed_size_read_read_write.rs:LL:CC
3+
|
4+
LL | a8[0].store(0, Ordering::SeqCst);
5+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Race condition detected between (1) multiple differently-sized atomic loads, including one load on thread `unnamed-ID` and (2) 1-byte atomic store on thread `unnamed-ID` at ALLOC. (2) just happened here
6+
|
7+
help: and (1) occurred earlier here
8+
--> tests/fail/data_race/mixed_size_read_read_write.rs:LL:CC
9+
|
10+
LL | a16.load(Ordering::SeqCst);
11+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^
12+
= help: overlapping unsynchronized atomic accesses must use the same access size
13+
= help: see https://doc.rust-lang.org/nightly/std/sync/atomic/index.html#memory-model-for-atomic-accesses for more information about the Rust memory model
14+
= help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior
15+
= help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information
16+
= note: BACKTRACE (of the first span) on thread `unnamed-ID`:
17+
= note: inside closure at tests/fail/data_race/mixed_size_read_read_write.rs:LL:CC
18+
19+
note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace
20+
21+
error: aborting due to 1 previous error
22+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
//@compile-flags: -Zmiri-preemption-rate=0.0 -Zmiri-disable-weak-memory-emulation
2+
// Avoid accidental synchronization via address reuse inside `thread::spawn`.
3+
//@compile-flags: -Zmiri-address-reuse-cross-thread-rate=0
4+
// Two variants: the atomic store matches the size of the first or second atomic load.
5+
//@revisions: match_first_load match_second_load
6+
7+
use std::sync::atomic::{AtomicU16, AtomicU8, Ordering};
8+
use std::thread;
9+
10+
fn convert(a: &AtomicU16) -> &[AtomicU8; 2] {
11+
unsafe { std::mem::transmute(a) }
12+
}
13+
14+
// We can't allow mixed-size accesses; they are not possible in C++ and even
15+
// Intel says you shouldn't do it.
16+
fn main() {
17+
let a = AtomicU16::new(0);
18+
let a16 = &a;
19+
let a8 = convert(a16);
20+
21+
thread::scope(|s| {
22+
s.spawn(|| {
23+
a16.load(Ordering::SeqCst);
24+
});
25+
s.spawn(|| {
26+
a8[0].load(Ordering::SeqCst);
27+
});
28+
s.spawn(|| {
29+
thread::yield_now(); // make sure this happens last
30+
if cfg!(match_first_load) {
31+
a16.store(0, Ordering::SeqCst);
32+
//~[match_first_load]^ ERROR: Race condition detected between (1) multiple differently-sized atomic loads, including one load on thread `unnamed-1` and (2) 2-byte atomic store on thread `unnamed-3`
33+
} else {
34+
a8[0].store(0, Ordering::SeqCst);
35+
//~[match_second_load]^ ERROR: Race condition detected between (1) multiple differently-sized atomic loads, including one load on thread `unnamed-1` and (2) 1-byte atomic store on thread `unnamed-3`
36+
}
37+
});
38+
});
39+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
error: Undefined Behavior: Race condition detected between (1) 1-byte atomic load on thread `unnamed-ID` and (2) 2-byte atomic store on thread `unnamed-ID` at ALLOC. (2) just happened here
2+
--> tests/fail/data_race/mixed_size_read_write.rs:LL:CC
3+
|
4+
LL | a16.store(1, Ordering::SeqCst);
5+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Race condition detected between (1) 1-byte atomic load on thread `unnamed-ID` and (2) 2-byte atomic store on thread `unnamed-ID` at ALLOC. (2) just happened here
6+
|
7+
help: and (1) occurred earlier here
8+
--> tests/fail/data_race/mixed_size_read_write.rs:LL:CC
9+
|
10+
LL | a8[0].load(Ordering::SeqCst);
11+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
12+
= help: overlapping unsynchronized atomic accesses must use the same access size
13+
= help: see https://doc.rust-lang.org/nightly/std/sync/atomic/index.html#memory-model-for-atomic-accesses for more information about the Rust memory model
14+
= help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior
15+
= help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information
16+
= note: BACKTRACE (of the first span) on thread `unnamed-ID`:
17+
= note: inside closure at tests/fail/data_race/mixed_size_read_write.rs:LL:CC
18+
19+
note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace
20+
21+
error: aborting due to 1 previous error
22+
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
//@compile-flags: -Zmiri-preemption-rate=0.0 -Zmiri-disable-weak-memory-emulation
2+
// Avoid accidental synchronization via address reuse inside `thread::spawn`.
3+
//@compile-flags: -Zmiri-address-reuse-cross-thread-rate=0
4+
// Two revisions, depending on which access goes first.
5+
//@revisions: read_write write_read
6+
7+
use std::sync::atomic::{AtomicU16, AtomicU8, Ordering};
8+
use std::thread;
9+
10+
fn convert(a: &AtomicU16) -> &[AtomicU8; 2] {
11+
unsafe { std::mem::transmute(a) }
12+
}
13+
14+
// We can't allow mixed-size accesses; they are not possible in C++ and even
15+
// Intel says you shouldn't do it.
16+
fn main() {
17+
let a = AtomicU16::new(0);
18+
let a16 = &a;
19+
let a8 = convert(a16);
20+
21+
thread::scope(|s| {
22+
s.spawn(|| {
23+
if cfg!(read_write) {
24+
// Let the other one go first.
25+
thread::yield_now();
26+
}
27+
a16.store(1, Ordering::SeqCst);
28+
//~[read_write]^ ERROR: Race condition detected between (1) 1-byte atomic load on thread `unnamed-2` and (2) 2-byte atomic store on thread `unnamed-1`
29+
});
30+
s.spawn(|| {
31+
if cfg!(write_read) {
32+
// Let the other one go first.
33+
thread::yield_now();
34+
}
35+
a8[0].load(Ordering::SeqCst);
36+
//~[write_read]^ ERROR: Race condition detected between (1) 2-byte atomic store on thread `unnamed-1` and (2) 1-byte atomic load on thread `unnamed-2`
37+
});
38+
});
39+
}

tests/fail/data_race/mixed_size_read.stderr renamed to tests/fail/data_race/mixed_size_read_write.write_read.stderr

+7-7
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,20 @@
1-
error: Undefined Behavior: Race condition detected between (1) 2-byte atomic load on thread `unnamed-ID` and (2) 1-byte atomic load on thread `unnamed-ID` at ALLOC. (2) just happened here
2-
--> tests/fail/data_race/mixed_size_read.rs:LL:CC
1+
error: Undefined Behavior: Race condition detected between (1) 2-byte atomic store on thread `unnamed-ID` and (2) 1-byte atomic load on thread `unnamed-ID` at ALLOC. (2) just happened here
2+
--> tests/fail/data_race/mixed_size_read_write.rs:LL:CC
33
|
44
LL | a8[0].load(Ordering::SeqCst);
5-
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Race condition detected between (1) 2-byte atomic load on thread `unnamed-ID` and (2) 1-byte atomic load on thread `unnamed-ID` at ALLOC. (2) just happened here
5+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Race condition detected between (1) 2-byte atomic store on thread `unnamed-ID` and (2) 1-byte atomic load on thread `unnamed-ID` at ALLOC. (2) just happened here
66
|
77
help: and (1) occurred earlier here
8-
--> tests/fail/data_race/mixed_size_read.rs:LL:CC
8+
--> tests/fail/data_race/mixed_size_read_write.rs:LL:CC
99
|
10-
LL | a16.load(Ordering::SeqCst);
11-
| ^^^^^^^^^^^^^^^^^^^^^^^^^^
10+
LL | a16.store(1, Ordering::SeqCst);
11+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
1212
= help: overlapping unsynchronized atomic accesses must use the same access size
1313
= help: see https://doc.rust-lang.org/nightly/std/sync/atomic/index.html#memory-model-for-atomic-accesses for more information about the Rust memory model
1414
= help: this indicates a bug in the program: it performed an invalid operation, and caused Undefined Behavior
1515
= help: see https://doc.rust-lang.org/nightly/reference/behavior-considered-undefined.html for further information
1616
= note: BACKTRACE (of the first span) on thread `unnamed-ID`:
17-
= note: inside closure at tests/fail/data_race/mixed_size_read.rs:LL:CC
17+
= note: inside closure at tests/fail/data_race/mixed_size_read_write.rs:LL:CC
1818

1919
note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace
2020

0 commit comments

Comments
 (0)