22
22
// Available: https://ss265.host.cs.st-andrews.ac.uk/papers/n3132.pdf.
23
23
24
24
use std:: sync:: atomic:: Ordering :: * ;
25
- use std:: sync:: atomic:: { AtomicBool , AtomicI32 , fence} ;
25
+ use std:: sync:: atomic:: { AtomicBool , AtomicI32 , Ordering , fence} ;
26
26
use std:: thread:: spawn;
27
27
28
28
#[ derive( Copy , Clone ) ]
@@ -45,8 +45,8 @@ fn static_atomic_bool(val: bool) -> &'static AtomicBool {
45
45
}
46
46
47
47
// Spins until it acquires a pre-determined value.
48
- fn acquires_value ( loc : & AtomicI32 , val : i32 ) -> i32 {
49
- while loc. load ( Acquire ) != val {
48
+ fn loads_value ( loc : & AtomicI32 , ord : Ordering , val : i32 ) -> i32 {
49
+ while loc. load ( ord ) != val {
50
50
std:: hint:: spin_loop ( ) ;
51
51
}
52
52
val
@@ -69,7 +69,7 @@ fn test_corr() {
69
69
} ) ; // | |
70
70
#[ rustfmt:: skip] // |synchronizes-with |happens-before
71
71
let j3 = spawn ( move || { // | |
72
- acquires_value ( & y, 1 ) ; // <------ ------------+ |
72
+ loads_value ( & y, Acquire , 1 ) ; // <------------+ |
73
73
x. load ( Relaxed ) // <----------------------------------------------+
74
74
// The two reads on x are ordered by hb, so they cannot observe values
75
75
// differently from the modification order. If the first read observed
@@ -94,12 +94,12 @@ fn test_wrc() {
94
94
} ) ; // | |
95
95
#[ rustfmt:: skip] // |synchronizes-with |
96
96
let j2 = spawn ( move || { // | |
97
- acquires_value ( & x, 1 ) ; // <------ ------------+ |
97
+ loads_value ( & x, Acquire , 1 ) ; // <------------+ |
98
98
y. store ( 1 , Release ) ; // ---------------------+ |happens-before
99
99
} ) ; // | |
100
100
#[ rustfmt:: skip] // |synchronizes-with |
101
101
let j3 = spawn ( move || { // | |
102
- acquires_value ( & y, 1 ) ; // <------ ------------+ |
102
+ loads_value ( & y, Acquire , 1 ) ; // <------------+ |
103
103
x. load ( Relaxed ) // <-----------------------------------------------+
104
104
} ) ;
105
105
@@ -125,7 +125,7 @@ fn test_message_passing() {
125
125
#[ rustfmt:: skip] // |synchronizes-with | happens-before
126
126
let j2 = spawn ( move || { // | |
127
127
let x = x; // avoid field capturing | |
128
- acquires_value ( & y, 1 ) ; // <------ ------------+ |
128
+ loads_value ( & y, Acquire , 1 ) ; // <------------+ |
129
129
unsafe { * x. 0 } // <---------------------------------------------+
130
130
} ) ;
131
131
@@ -268,9 +268,6 @@ fn test_iriw_sc_rlx() {
268
268
let x = static_atomic_bool ( false ) ;
269
269
let y = static_atomic_bool ( false ) ;
270
270
271
- x. store ( false , Relaxed ) ;
272
- y. store ( false , Relaxed ) ;
273
-
274
271
let a = spawn ( move || x. store ( true , Relaxed ) ) ;
275
272
let b = spawn ( move || y. store ( true , Relaxed ) ) ;
276
273
let c = spawn ( move || {
@@ -290,6 +287,84 @@ fn test_iriw_sc_rlx() {
290
287
assert ! ( c || d) ;
291
288
}
292
289
290
+ // Similar to `test_iriw_sc_rlx` but with fences instead of SC accesses.
291
+ fn test_cpp20_sc_fence_fix ( ) {
292
+ let x = static_atomic_bool ( false ) ;
293
+ let y = static_atomic_bool ( false ) ;
294
+
295
+ let thread1 = spawn ( || {
296
+ let a = x. load ( Relaxed ) ;
297
+ fence ( SeqCst ) ;
298
+ let b = y. load ( Relaxed ) ;
299
+ ( a, b)
300
+ } ) ;
301
+
302
+ let thread2 = spawn ( || {
303
+ x. store ( true , Relaxed ) ;
304
+ } ) ;
305
+ let thread3 = spawn ( || {
306
+ y. store ( true , Relaxed ) ;
307
+ } ) ;
308
+
309
+ let thread4 = spawn ( || {
310
+ let c = y. load ( Relaxed ) ;
311
+ fence ( SeqCst ) ;
312
+ let d = x. load ( Relaxed ) ;
313
+ ( c, d)
314
+ } ) ;
315
+
316
+ let ( a, b) = thread1. join ( ) . unwrap ( ) ;
317
+ thread2. join ( ) . unwrap ( ) ;
318
+ thread3. join ( ) . unwrap ( ) ;
319
+ let ( c, d) = thread4. join ( ) . unwrap ( ) ;
320
+ let bad = a == true && b == false && c == true && d == false ;
321
+ assert ! ( !bad) ;
322
+ }
323
+
324
+ // https://plv.mpi-sws.org/scfix/paper.pdf
325
+ // 2.2 Second Problem: SC Fences are Too Weak
326
+ fn test_cpp20_rwc_syncs ( ) {
327
+ /*
328
+ int main() {
329
+ atomic_int x = 0;
330
+ atomic_int y = 0;
331
+ {{{ x.store(1,mo_relaxed);
332
+ ||| { r1=x.load(mo_relaxed).readsvalue(1);
333
+ fence(mo_seq_cst);
334
+ r2=y.load(mo_relaxed); }
335
+ ||| { y.store(1,mo_relaxed);
336
+ fence(mo_seq_cst);
337
+ r3=x.load(mo_relaxed); }
338
+ }}}
339
+ return 0;
340
+ }
341
+ */
342
+ let x = static_atomic ( 0 ) ;
343
+ let y = static_atomic ( 0 ) ;
344
+
345
+ let j1 = spawn ( move || {
346
+ x. store ( 1 , Relaxed ) ;
347
+ } ) ;
348
+
349
+ let j2 = spawn ( move || {
350
+ loads_value ( & x, Relaxed , 1 ) ;
351
+ fence ( SeqCst ) ;
352
+ y. load ( Relaxed )
353
+ } ) ;
354
+
355
+ let j3 = spawn ( move || {
356
+ y. store ( 1 , Relaxed ) ;
357
+ fence ( SeqCst ) ;
358
+ x. load ( Relaxed )
359
+ } ) ;
360
+
361
+ j1. join ( ) . unwrap ( ) ;
362
+ let b = j2. join ( ) . unwrap ( ) ;
363
+ let c = j3. join ( ) . unwrap ( ) ;
364
+
365
+ assert ! ( ( b, c) != ( 0 , 0 ) ) ;
366
+ }
367
+
293
368
pub fn main ( ) {
294
369
for _ in 0 ..50 {
295
370
test_single_thread ( ) ;
@@ -301,5 +376,7 @@ pub fn main() {
301
376
test_sc_store_buffering ( ) ;
302
377
test_sync_through_rmw_and_fences ( ) ;
303
378
test_iriw_sc_rlx ( ) ;
379
+ test_cpp20_sc_fence_fix ( ) ;
380
+ test_cpp20_rwc_syncs ( ) ;
304
381
}
305
382
}
0 commit comments