How to produce memory reordering with Relaxed?

Hi, I am learning the memory ordering and trying to produce it with Relaxed but it doesn't happen at all.

use std::sync::atomic::{AtomicI32, Ordering};

fn main() {
    for _ in 0..1_000_000 {
        let a: AtomicI32 = AtomicI32::new(0);
        let b: AtomicI32 = AtomicI32::new(0);

        std::thread::scope(|s| {
            for _ in 0..10 {
                s.spawn(|| {
                    let x = b.load(Ordering::Relaxed);
                    let y = a.load(Ordering::Relaxed);
                    if x == 20 && y == 0 {
                        println!("Reordering happened");
                    }
                });
            }
            s.spawn(|| {
                a.store(10, Ordering::Relaxed);
                b.store(20, Ordering::Relaxed);
            });
        });
    }
}

I ran cargo run --release thinking the optimzation will make it happen more frequently, but it just doesn't happen. Is there a way to increase the chance for it to happen? I am running this on Mac M1.

Thanks a lot for reading and helping!

The time taken to spawn and schedule threads is enormously larger than the time taken to execute two atomic operations. You need to make your threads do things continuously in loops, instead of each exiting after one attempt, to have any significant chance of their execution overlapping interestingly.

2 Likes

Something like this, using just two threads.

use std::sync::atomic::{AtomicI32, Ordering};

fn main() {
    let a = AtomicI32::new(0);
    let b = AtomicI32::new(0);

    std::thread::scope(|s| {
        let join_handle = s.spawn(|| loop {
            let x = b.load(Ordering::Relaxed);
            let y = a.load(Ordering::Relaxed);
            assert!(!(x == 20 && y == 0));
        });

        let mut count = 0_u64;
        while !join_handle.is_finished() {
            count += 1;
            a.store(10, Ordering::Relaxed);
            b.store(20, Ordering::Relaxed);
            a.store(3, Ordering::Relaxed);
            b.store(4, Ordering::Relaxed);
            a.store(0, Ordering::Relaxed);
            b.store(0, Ordering::Relaxed);
        }

        println!("Count: {count}");
    });
}
2 Likes

Thank you!!! I changed the program like this and it happens pretty often.

use std::sync::atomic::{AtomicI32, Ordering};

fn main() {
    for i in 0..100 {
        let a: AtomicI32 = AtomicI32::new(0);
        let b: AtomicI32 = AtomicI32::new(0);

        std::thread::scope(|s| {
            let handle = s.spawn(|| {
                a.store(10, Ordering::Release);
                b.store(20, Ordering::Relaxed);
            });
            while !handle.is_finished() {
                let x = b.load(Ordering::Relaxed);
                let y = a.load(Ordering::Acquire);
                if x == 20 && y == 0 {
                    println!("Reordering happened at loop number {}", i);
                }
            }
        });
    }
}