Created
June 5, 2021 00:00
-
-
Save grahamking/eadd0170650d1825de9a7f4b1d5774c9 to your computer and use it in GitHub Desktop.
Memory ordering experiment in Rust. See https://preshing.com/20120515/memory-reordering-caught-in-the-act/ . Try changing the two HERE lines.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// 1. Run it as is (SeqCst). Should be just timing output | |
// 2. Change the two HERE lines to Ordering::Relaxed and run it again. | |
// That should give lots of (seemingly impossible) memory reorderings. | |
use rand; | |
use std::sync::atomic::{AtomicU8, Ordering}; | |
use std::sync::*; | |
use std::thread; | |
fn main() { | |
let x = Arc::new(AtomicU8::new(0)); | |
let (x1, x2) = (x.clone(), x.clone()); | |
let y = Arc::new(AtomicU8::new(0)); | |
let (y1, y2) = (y.clone(), y.clone()); | |
let r1 = Arc::new(AtomicU8::new(0)); | |
let r11 = r1.clone(); | |
let r2 = Arc::new(AtomicU8::new(0)); | |
let r22 = r2.clone(); | |
let enter = Arc::new(Barrier::new(3)); | |
let (enter1, enter2) = (enter.clone(), enter.clone()); | |
let exit = Arc::new(Barrier::new(3)); | |
let (exit1, exit2) = (exit.clone(), exit.clone()); | |
thread::spawn(move || { | |
loop { | |
enter1.wait(); // sync point, both threads at the starting line | |
while rand::random::<u32>() % 8 != 0 {} // delay arbitrarily so threads get out of sync | |
// x = 222 | |
x1.store(222, Ordering::SeqCst); // HERE try changing this line to Ordering::Relaxed | |
// r1 = y | |
let y = y1.load(Ordering::SeqCst); | |
r11.store(y, Ordering::SeqCst); | |
exit1.wait(); // both threads pause, main thread checks the values of r1 and r2 | |
} | |
}); | |
thread::spawn(move || { | |
loop { | |
enter2.wait(); | |
while rand::random::<u32>() % 8 != 0 {} | |
// y = 222 | |
y2.store(222, Ordering::SeqCst); // HERE try changing this line to Ordering::Relaxed | |
// r2 = x | |
let x = x2.load(Ordering::SeqCst); | |
r22.store(x, Ordering::SeqCst); | |
exit2.wait(); | |
} | |
}); | |
let mut detected = 0; | |
let mut iterations = 1; | |
let start_run = std::time::Instant::now(); | |
while start_run.elapsed().as_secs() < 5 { | |
// reset | |
x.store(0, Ordering::SeqCst); | |
y.store(0, Ordering::SeqCst); | |
// let both threads run a single iteration | |
enter.wait(); | |
exit.wait(); | |
// check if out-of-order execution happened | |
if r1.load(Ordering::SeqCst) == 0 && r2.load(Ordering::SeqCst) == 0 { | |
detected += 1; | |
println!( | |
"{} reorders detected after {} iterations", | |
detected, iterations | |
); | |
} | |
iterations += 1; | |
} | |
println!("{} per sec", iterations as f64 / 5.0); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment