m-ou-se / rust-atomics-and-locks

Code examples, data structures, and links from my book, Rust Atomics and Locks.
Other
1.33k stars 120 forks source link

About example of Ch3 on SeqCst #44

Closed jammingyu closed 7 months ago

jammingyu commented 1 year ago

The content that the question is about

the example of ch3 for SeqCst

https://marabos.nl/atomics/memory-ordering.html#seqcst

The question

I think the example used is not appropriate for SeqCst. SeqCst guarantees that all operations are sequencially consistant globally, but that doesn't mean context switch does not happen between store and load operation.

therefore below order is possible in the example code

store at Thread A => store at Thread B => load at Thread A (access blocked) => load at Thread B (access blocked)

For the proof, if I run the below test, there are some cases where DATA is actually zero, which implies that both threads never accessed to data.


use std::sync::atomic::{AtomicBool, Ordering};
use std::thread;

static A: AtomicBool = AtomicBool::new(false);
static B: AtomicBool = AtomicBool::new(false);
static mut DATA: isize = 0;

fn trial() {
    A.store(false, Ordering::SeqCst);
    B.store(false, Ordering::SeqCst);
    unsafe { DATA = 0; }

    let a = thread::spawn(|| {
        A.store(true, Ordering::SeqCst);
        if !B.load(Ordering::SeqCst) {
            unsafe { DATA += 1 };
        }
    });
    let b = thread::spawn(|| {
        B.store(true, Ordering::SeqCst);
        if !A.load(Ordering::SeqCst) {
            unsafe { DATA += 1 };
        }
    });
    a.join().unwrap();
    b.join().unwrap();
}

fn main() {
    for _ in 0..10_000_000 {
        trial();
        if unsafe {DATA == 0} {
            unsafe {
                println!("{}", DATA);
            }
        }
    }
}
m-ou-se commented 9 months ago

I mention exactly that in the book:

If both store operations happen before either of the load operations, it’s possible that neither thread ends up accessing S. However, it’s impossible for both threads to access S and cause undefined behavior, since the sequentially consistent ordering guarantees only one of them can win the race.

The example guarantees there is no undefined behavior. It prevents a situation where both threads concurrently access the data. It does not guarantee that at least one thread accesses it.

jammingyu commented 7 months ago

Okay.

I think I unconsciously misinterpreted the purpose of example because I looked SeqCst example below link and this book at the same time,

https://en.cppreference.com/w/cpp/atomic/memory_order

Thanks for you reply :)