Thread atomic rust

use std::sync::atomic::Ordering::{Acquire, Relaxed, Release, SeqCst};

use std::sync::atomic::Ordering;

use loom::{
    sync::{atomic::AtomicUsize, Arc},
    thread,
};
use rand::Rng;

fn two_numbers_with_a_dependency(read_ordering_a: Ordering, read_ordering_b: Ordering, write_ordering_a: Ordering, write_ordering_b: Ordering) {
    loom::model(move || {
        let num_a = Arc::new(AtomicUsize::new(1));
        let num_b = Arc::new(AtomicUsize::new(0));
        let num_a2 = num_a.clone();
        let num_b2 = num_b.clone();

        let t1 = thread::spawn(move || {
            for idx in 1..5 {
                num_b2.store(idx, Relaxed);
                num_a2.store(idx + 1, Release);
            }
        });

        let t2: thread::JoinHandle<()> = thread::spawn(move || {
            for i in 1..5 {
                let b = num_b.load(Relaxed);
                let a = num_a.load(Acquire);

                dbg!(b, a);
                println!();
                assert!(a>= b);
            }
        });

        t1.join();
        t2.join();
    });
}

#[test]
fn atomics_acquire_release_does_failing() {
    two_numbers_with_a_dependency(Release, Relaxed, Acquire, Relaxed);
}

why it will occur b = 2 and a = 1

If a == 1, then num_a2.store(idx + 1, Release) has not been observed by t2, and thus the memory ordering rules are silent about what writes by t1 are visible to t2. It's thus allowed for num_b.load(Relaxed) to return any value between 0 and 5, since those are the values written to the atomic.

Had a == 3, the allowed values for num_b.load(Relaxed) would be between 2 and 5, since you're guaranteed by the memory ordering rules that num_a.load(Acquire) will synchronize-with num_a2.store(idx + 1, Release), and thus that num_b.store(idx, Relaxed) for idx == 2 must have happened.

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.