When thread A releases a location in memory and then thread B subsequently acquires the same location in memory, causality is established. Every write (including non-atomic and relaxed atomic writes) that **happened before** A's release will be observed by B after its acquisition. However no causality is established with any other threads. Similarly, no causality is established if A and B access different locations in memory.
Here what does "happened before" A for a non-atomic write precisely mean?
Will any write appearing before in the code qualify, or is some logical causality required?
// thread 1
let my_ptr: *mut usize = //...
std::ptr::write(my_ptr, 5); // C
shared_atomic_ptr.store(my_ptr, Ordering::Release); // A
// thread 2
let my_ptr: *const = shared_atomic_ptr.load(my_ptr, Ordering::Acquire); // B
let val = unsafe { std::ptr::read(my_ptr) };
Assuming that in B, we read the value from A's write;
are we guaranteed that val will be 5, or is it possible for the write C to be reordered after A?
in the same thread of A, memory writes that are "sequenced-before" A are defined to "happen-before" A;
recursively, suppose event T "happens-before" A, then all events that "happen-before" T also transitively "happen-before" A
yes.
when they are the same thread, they have the "sequenced-before" relation, which is mandated by the so-called "program order".
this is crucial to establish the "synchronize-with" relation between threads using atomics.
in real code, the synchronized data can only be safely accessed after you examined the loaded atomic value, e.g. something like:
if atomic_acqure(&shared_variable) == SOME_VALUE {
// within this `if` clause, data access is syncrhonized with `shared_variable`
}
// not synchronized outside the `if` branch
in this scenario, yes, the write C will be visible in thread 2.
assuming there are no other concurrent writes to the same pointer, then you are guaranteed to read the value 5.
Writing code that tries to access the pointer from second thread while either condition (not within clause or some other concurrent write) is undefined behaviour. Code might seem to work for you at the time but anything can happen when you haven't examined the machine code and there is no guarantee machine code will not change upon recompilation. The machine code could still be performing undefined behaviour regarding the CPU instruction set and so execution changes depending on exact computer ran on.
Do I really need a clause statement?
Here the acquired value is the pointer itself. If by design there are no other writer, I assume this should be safe no?
(assuming there is always a valid pointer in there I mean)