Sound non-atomic write to shared data?

As I understand it, there are two main reasons atomics are required: data races and cache coherency.

here's a minified example of the behavior i'm talking about (playground):

#![feature(sync_unsafe_cell)]
use std::cell::SyncUnsafeCell;

static ST: SyncUnsafeCell<usize> = SyncUnsafeCell::new(4);

fn main() {
    unsafe {
        *ST.get() = 10;
        std::thread::spawn(|| {
            println!("{}", *ST.get());
        }).join().unwrap();
    }
}

Since the thread is spawned after ST is modified, we know this can't cause a data race, but does it comply with the rust memory model? Would it still be sound if the thread was spawned beforehand, and then unblocked after the assignment? Does write_volatile do anything here? What piece of documentation is relevant here?

Since the thread is spawned after ST is modified, we know this can't cause a data race, but does it comply with the rust memory model? Would it still be sound if the thread was spawned beforehand, and then unblocked after the assignment?

If that isn’t sound, then rayon isn’t sound, because it does all sorts of non-overlapping writes to shared memory using tasks running in a thread pool.

Does write_volatile do anything here?

I’ll quote the documentation about this:

Just like in C, whether an operation is volatile has no bearing whatsoever on questions involving concurrent access from multiple threads.

write_volatile is basically only relevant if you are doing memory-mapped IO — writes (or reads) with side effects.

1 Like

Yes, because std::thread::spawn implicitly guarantees an happens-before relationship between any operation done in the calling thread before calling it and any operation done in the spawned thread.

It depends on how you perform such "unblocking". If it involved no synchronization, i.e. no happens-before relation being established between the write and read to ST, then it would not be sound.

No, volatile operations do not provide synchronization guarantees, they are merely considered side-effectful.

What you're looking for is what memory model does Rust use, which is pretty much the C++11 memory model with a couple of small changes (see e.g. here)

1 Like