Skip to content

Instantly share code, notes, and snippets.

@grahamking
Created June 5, 2021 00:00
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save grahamking/eadd0170650d1825de9a7f4b1d5774c9 to your computer and use it in GitHub Desktop.
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.
// 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