20
20
// "Mathematizing C++ concurrency", ACM SIGPLAN Notices, vol. 46, no. 1, pp. 55-66, 2011.
21
21
// Available: https://ss265.host.cs.st-andrews.ac.uk/papers/n3132.pdf.
22
22
23
- use std:: sync:: atomic:: AtomicUsize ;
24
23
use std:: sync:: atomic:: Ordering :: * ;
24
+ use std:: sync:: atomic:: { fence, AtomicBool , AtomicI32 } ;
25
25
use std:: thread:: spawn;
26
26
27
27
#[ derive( Copy , Clone ) ]
@@ -32,13 +32,19 @@ unsafe impl<T> Sync for EvilSend<T> {}
32
32
33
33
// We can't create static items because we need to run each test
34
34
// 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
37
43
ret
38
44
}
39
45
40
46
// 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 {
42
48
while loc. load ( Acquire ) != val {
43
49
std:: hint:: spin_loop ( ) ;
44
50
}
@@ -207,7 +213,7 @@ fn test_sc_store_buffering() {
207
213
}
208
214
209
215
fn test_single_thread ( ) {
210
- let x = AtomicUsize :: new ( 42 ) ;
216
+ let x = AtomicI32 :: new ( 42 ) ;
211
217
212
218
assert_eq ! ( x. load( Relaxed ) , 42 ) ;
213
219
@@ -216,6 +222,42 @@ fn test_single_thread() {
216
222
assert_eq ! ( x. load( Relaxed ) , 43 ) ;
217
223
}
218
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
+
219
261
pub fn main ( ) {
220
262
for _ in 0 ..50 {
221
263
test_single_thread ( ) ;
@@ -225,5 +267,6 @@ pub fn main() {
225
267
test_wrc ( ) ;
226
268
test_corr ( ) ;
227
269
test_sc_store_buffering ( ) ;
270
+ test_sync_through_rmw_and_fences ( ) ;
228
271
}
229
272
}
0 commit comments