]> git.lizzy.rs Git - rust.git/blob - tests/pass/0weak_memory_consistency.rs
make tests pass again
[rust.git] / tests / pass / 0weak_memory_consistency.rs
1 //@ignore-target-windows: Concurrency on Windows is not supported yet.
2 //@compile-flags: -Zmiri-ignore-leaks -Zmiri-disable-stacked-borrows
3
4 // The following tests check whether our weak memory emulation produces
5 // any inconsistent execution outcomes
6 //
7 // Due to the random nature of choosing valid stores, it is always
8 // possible that our tests spuriously succeeds: even though our weak
9 // memory emulation code has incorrectly identified a store in
10 // modification order as being valid, it may be never chosen by
11 // the RNG and never observed in our tests.
12 //
13 // To mitigate this, each test is ran enough times such that the chance
14 // of spurious success is very low. These tests never supriously fail.
15
16 // Test cases and their consistent outcomes are from
17 // http://svr-pes20-cppmem.cl.cam.ac.uk/cppmem/
18 // Based on
19 // M. Batty, S. Owens, S. Sarkar, P. Sewell and T. Weber,
20 // "Mathematizing C++ concurrency", ACM SIGPLAN Notices, vol. 46, no. 1, pp. 55-66, 2011.
21 // Available: https://ss265.host.cs.st-andrews.ac.uk/papers/n3132.pdf.
22
23 use std::sync::atomic::Ordering::*;
24 use std::sync::atomic::{fence, AtomicBool, AtomicI32};
25 use std::thread::spawn;
26
27 #[derive(Copy, Clone)]
28 struct EvilSend<T>(pub T);
29
30 unsafe impl<T> Send for EvilSend<T> {}
31 unsafe impl<T> Sync for EvilSend<T> {}
32
33 // We can't create static items because we need to run each test
34 // multiple times
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
43     ret
44 }
45
46 // Spins until it acquires a pre-determined value.
47 fn acquires_value(loc: &AtomicI32, val: i32) -> i32 {
48     while loc.load(Acquire) != val {
49         std::hint::spin_loop();
50     }
51     val
52 }
53
54 fn test_corr() {
55     let x = static_atomic(0);
56     let y = static_atomic(0);
57
58     let j1 = spawn(move || {
59         x.store(1, Relaxed);
60         x.store(2, Relaxed);
61     });
62
63     #[rustfmt::skip]
64     let j2 = spawn(move || {
65         let r2 = x.load(Relaxed); // -------------------------------------+
66         y.store(1, Release); // ---------------------+                    |
67         r2 //                                        |                    |
68     }); //                                           |                    |
69     #[rustfmt::skip] //                              |synchronizes-with   |happens-before
70     let j3 = spawn(move || { //                      |                    |
71         acquires_value(&y, 1); // <------------------+                    |
72         x.load(Relaxed) // <----------------------------------------------+
73         // The two reads on x are ordered by hb, so they cannot observe values
74         // differently from the modification order. If the first read observed
75         // 2, then the second read must observe 2 as well.
76     });
77
78     j1.join().unwrap();
79     let r2 = j2.join().unwrap();
80     let r3 = j3.join().unwrap();
81     if r2 == 2 {
82         assert_eq!(r3, 2);
83     }
84 }
85
86 fn test_wrc() {
87     let x = static_atomic(0);
88     let y = static_atomic(0);
89
90     #[rustfmt::skip]
91     let j1 = spawn(move || {
92         x.store(1, Release); // ---------------------+---------------------+
93     }); //                                           |                     |
94     #[rustfmt::skip] //                              |synchronizes-with    |
95     let j2 = spawn(move || { //                      |                     |
96         acquires_value(&x, 1); // <------------------+                     |
97         y.store(1, Release); // ---------------------+                     |happens-before
98     }); //                                           |                     |
99     #[rustfmt::skip] //                              |synchronizes-with    |
100     let j3 = spawn(move || { //                      |                     |
101         acquires_value(&y, 1); // <------------------+                     |
102         x.load(Relaxed) // <-----------------------------------------------+
103     });
104
105     j1.join().unwrap();
106     j2.join().unwrap();
107     let r3 = j3.join().unwrap();
108
109     assert_eq!(r3, 1);
110 }
111
112 fn test_message_passing() {
113     let mut var = 0u32;
114     let ptr = &mut var as *mut u32;
115     let x = EvilSend(ptr);
116     let y = static_atomic(0);
117
118     #[rustfmt::skip]
119     let j1 = spawn(move || {
120         unsafe { *x.0 = 1 }; // -----------------------------------------+
121         y.store(1, Release); // ---------------------+                   |
122     }); //                                           |                   |
123     #[rustfmt::skip] //                              |synchronizes-with  | happens-before
124     let j2 = spawn(move || { //                      |                   |
125         acquires_value(&y, 1); // <------------------+                   |
126         unsafe { *x.0 } // <---------------------------------------------+
127     });
128
129     j1.join().unwrap();
130     let r2 = j2.join().unwrap();
131
132     assert_eq!(r2, 1);
133 }
134
135 // LB+acq_rel+acq_rel
136 fn test_load_buffering_acq_rel() {
137     let x = static_atomic(0);
138     let y = static_atomic(0);
139     let j1 = spawn(move || {
140         let r1 = x.load(Acquire);
141         y.store(1, Release);
142         r1
143     });
144
145     let j2 = spawn(move || {
146         let r2 = y.load(Acquire);
147         x.store(1, Release);
148         r2
149     });
150
151     let r1 = j1.join().unwrap();
152     let r2 = j2.join().unwrap();
153
154     // 3 consistent outcomes: (0,0), (0,1), (1,0)
155     assert_ne!((r1, r2), (1, 1));
156 }
157
158 fn test_mixed_access() {
159     /*
160     int main() {
161       atomic_int x = 0;
162       {{{
163         x.store(1, mo_relaxed);
164       }}}
165
166       x.store(2, mo_relaxed);
167
168       {{{
169         r1 = x.load(mo_relaxed);
170       }}}
171
172       return 0;
173     }
174         */
175     let x = static_atomic(0);
176
177     spawn(move || {
178         x.store(1, Relaxed);
179     })
180     .join()
181     .unwrap();
182
183     x.store(2, Relaxed);
184
185     let r2 = spawn(move || x.load(Relaxed)).join().unwrap();
186
187     assert_eq!(r2, 2);
188 }
189
190 // The following two tests are taken from Repairing Sequential Consistency in C/C++11
191 // by Lahav et al.
192 // https://plv.mpi-sws.org/scfix/paper.pdf
193
194 // Test case SB
195 fn test_sc_store_buffering() {
196     let x = static_atomic(0);
197     let y = static_atomic(0);
198
199     let j1 = spawn(move || {
200         x.store(1, SeqCst);
201         y.load(SeqCst)
202     });
203
204     let j2 = spawn(move || {
205         y.store(1, SeqCst);
206         x.load(SeqCst)
207     });
208
209     let a = j1.join().unwrap();
210     let b = j2.join().unwrap();
211
212     assert_ne!((a, b), (0, 0));
213 }
214
215 fn test_single_thread() {
216     let x = AtomicI32::new(42);
217
218     assert_eq!(x.load(Relaxed), 42);
219
220     x.store(43, Relaxed);
221
222     assert_eq!(x.load(Relaxed), 43);
223 }
224
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
261 pub fn main() {
262     for _ in 0..50 {
263         test_single_thread();
264         test_mixed_access();
265         test_load_buffering_acq_rel();
266         test_message_passing();
267         test_wrc();
268         test_corr();
269         test_sc_store_buffering();
270         test_sync_through_rmw_and_fences();
271     }
272 }