Skip to content
This repository was archived by the owner on May 28, 2025. It is now read-only.

Commit e067258

Browse files
committed
Auto merge of rust-lang#2370 - RalfJung:weak-mem-test, r=RalfJung
add another weak mem consistency test `@cbeuw` your code seems to do this correctly? :D
2 parents 6f6e01e + b8a0c49 commit e067258

File tree

2 files changed

+102
-16
lines changed

2 files changed

+102
-16
lines changed

tests/pass/0weak_memory_consistency.rs

Lines changed: 48 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,8 @@
2020
// "Mathematizing C++ concurrency", ACM SIGPLAN Notices, vol. 46, no. 1, pp. 55-66, 2011.
2121
// Available: https://ss265.host.cs.st-andrews.ac.uk/papers/n3132.pdf.
2222

23-
use std::sync::atomic::AtomicUsize;
2423
use std::sync::atomic::Ordering::*;
24+
use std::sync::atomic::{fence, AtomicBool, AtomicI32};
2525
use std::thread::spawn;
2626

2727
#[derive(Copy, Clone)]
@@ -32,13 +32,19 @@ unsafe impl<T> Sync for EvilSend<T> {}
3232

3333
// We can't create static items because we need to run each test
3434
// multiple times
35-
fn static_atomic(val: usize) -> &'static AtomicUsize {
36-
let ret = Box::leak(Box::new(AtomicUsize::new(val)));
35+
fn static_atomic(val: i32) -> &'static AtomicI32 {
36+
let ret = Box::leak(Box::new(AtomicI32::new(val)));
37+
ret.store(val, Relaxed); // work around https://github.com/rust-lang/miri/issues/2164
38+
ret
39+
}
40+
fn static_atomic_bool(val: bool) -> &'static AtomicBool {
41+
let ret = Box::leak(Box::new(AtomicBool::new(val)));
42+
ret.store(val, Relaxed); // work around https://github.com/rust-lang/miri/issues/2164
3743
ret
3844
}
3945

4046
// Spins until it acquires a pre-determined value.
41-
fn acquires_value(loc: &AtomicUsize, val: usize) -> usize {
47+
fn acquires_value(loc: &AtomicI32, val: i32) -> i32 {
4248
while loc.load(Acquire) != val {
4349
std::hint::spin_loop();
4450
}
@@ -207,7 +213,7 @@ fn test_sc_store_buffering() {
207213
}
208214

209215
fn test_single_thread() {
210-
let x = AtomicUsize::new(42);
216+
let x = AtomicI32::new(42);
211217

212218
assert_eq!(x.load(Relaxed), 42);
213219

@@ -216,6 +222,42 @@ fn test_single_thread() {
216222
assert_eq!(x.load(Relaxed), 43);
217223
}
218224

225+
fn test_sync_through_rmw_and_fences() {
226+
// Example from https://github.com/llvm/llvm-project/issues/56450#issuecomment-1183695905
227+
#[no_mangle]
228+
pub fn rdmw(storing: &AtomicI32, sync: &AtomicI32, loading: &AtomicI32) -> i32 {
229+
storing.store(1, Relaxed);
230+
fence(Release);
231+
sync.fetch_add(0, Relaxed);
232+
fence(Acquire);
233+
loading.load(Relaxed)
234+
}
235+
236+
let x = static_atomic(0);
237+
let y = static_atomic(0);
238+
let z = static_atomic(0);
239+
240+
// Since each thread is so short, we need to make sure that they truely run at the same time
241+
// Otherwise t1 will finish before t2 even starts
242+
let go = static_atomic_bool(false);
243+
244+
let t1 = spawn(move || {
245+
while !go.load(Relaxed) {}
246+
rdmw(y, x, z)
247+
});
248+
249+
let t2 = spawn(move || {
250+
while !go.load(Relaxed) {}
251+
rdmw(z, x, y)
252+
});
253+
254+
go.store(true, Relaxed);
255+
256+
let a = t1.join().unwrap();
257+
let b = t2.join().unwrap();
258+
assert_ne!((a, b), (0, 0));
259+
}
260+
219261
pub fn main() {
220262
for _ in 0..50 {
221263
test_single_thread();
@@ -225,5 +267,6 @@ pub fn main() {
225267
test_wrc();
226268
test_corr();
227269
test_sc_store_buffering();
270+
test_sync_through_rmw_and_fences();
228271
}
229272
}

tests/pass/weak_memory/weak.rs

Lines changed: 54 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,8 @@
88
// Spurious failure is possible, if you are really unlucky with
99
// the RNG and always read the latest value from the store buffer.
1010

11-
use std::sync::atomic::AtomicUsize;
1211
use std::sync::atomic::Ordering::*;
12+
use std::sync::atomic::{fence, AtomicUsize};
1313
use std::thread::spawn;
1414

1515
#[derive(Copy, Clone)]
@@ -70,9 +70,9 @@ fn seq_cst() -> bool {
7070
r3 == 1
7171
}
7272

73-
fn initialization_write() -> bool {
73+
fn initialization_write(add_fence: bool) -> bool {
7474
let x = static_atomic(11);
75-
assert_eq!(x.load(Relaxed), 11);
75+
assert_eq!(x.load(Relaxed), 11); // work around https://github.com/rust-lang/miri/issues/2164
7676

7777
let wait = static_atomic(0);
7878

@@ -85,6 +85,9 @@ fn initialization_write() -> bool {
8585

8686
let j2 = spawn(move || {
8787
reads_value(wait, 1);
88+
if add_fence {
89+
fence(AcqRel);
90+
}
8891
x.load(Relaxed)
8992
});
9093

@@ -94,15 +97,55 @@ fn initialization_write() -> bool {
9497
r2 == 11
9598
}
9699

97-
// Asserts that the function returns true at least once in 100 runs
98-
macro_rules! assert_once {
99-
($f:ident) => {
100-
assert!(std::iter::repeat_with(|| $f()).take(100).any(|x| x));
101-
};
100+
fn faa_replaced_by_load() -> bool {
101+
// Example from https://github.com/llvm/llvm-project/issues/56450#issuecomment-1183695905
102+
#[no_mangle]
103+
pub fn rdmw(storing: &AtomicUsize, sync: &AtomicUsize, loading: &AtomicUsize) -> usize {
104+
storing.store(1, Relaxed);
105+
fence(Release);
106+
// sync.fetch_add(0, Relaxed);
107+
sync.load(Relaxed);
108+
fence(Acquire);
109+
loading.load(Relaxed)
110+
}
111+
112+
let x = static_atomic(0);
113+
assert_eq!(x.load(Relaxed), 0); // work around https://github.com/rust-lang/miri/issues/2164
114+
let y = static_atomic(0);
115+
assert_eq!(y.load(Relaxed), 0); // work around https://github.com/rust-lang/miri/issues/2164
116+
let z = static_atomic(0);
117+
assert_eq!(z.load(Relaxed), 0); // work around https://github.com/rust-lang/miri/issues/2164
118+
119+
// Since each thread is so short, we need to make sure that they truely run at the same time
120+
// Otherwise t1 will finish before t2 even starts
121+
let go = static_atomic(0);
122+
123+
let t1 = spawn(move || {
124+
while go.load(Relaxed) == 0 {}
125+
rdmw(y, x, z)
126+
});
127+
128+
let t2 = spawn(move || {
129+
while go.load(Relaxed) == 0 {}
130+
rdmw(z, x, y)
131+
});
132+
133+
go.store(1, Relaxed);
134+
135+
let a = t1.join().unwrap();
136+
let b = t2.join().unwrap();
137+
(a, b) == (0, 0)
138+
}
139+
140+
/// Asserts that the function returns true at least once in 100 runs
141+
fn assert_once(f: fn() -> bool) {
142+
assert!(std::iter::repeat_with(|| f()).take(100).any(|x| x));
102143
}
103144

104145
pub fn main() {
105-
assert_once!(relaxed);
106-
assert_once!(seq_cst);
107-
assert_once!(initialization_write);
146+
assert_once(relaxed);
147+
assert_once(seq_cst);
148+
assert_once(|| initialization_write(false));
149+
assert_once(|| initialization_write(true));
150+
assert_once(faa_replaced_by_load);
108151
}

0 commit comments

Comments
 (0)