Example pseudocode:
struct Values{
AtomicInt counter;
Int needed_counter;
// We update this.
SomeType unsynchronized_value;
}
fn update_array(Values[] values){
for(Values& val : values) {
// Let's assume that overflow is impossible.
if (val.counter.fetch_add(1, relaxed) != val.needed_counter) { // CONDITION
continue;
}
// This is noop for CPU but prevents compiler optimizations
// from moving operations around it.
compiler_fence(acquire_release);
UpdateUnsynchonisedValue(&mut val.unsynchronized_value);
}
// Flush all changes made in current thread at once.
memory_fence(release);
}
On the one hand, since CONDITION
can be false only for one thread so it seems that no synchronization is needed.
On the other hand, modern CPUs do out of order and speculative execution so I wonder if it can cause data race by starting executing UpdateUnsynchonisedValue
function before checking CONDITION
.
Since I put compiler_fence
, compiler should not reorder instructions in UpdateUnsynchonisedValue before checking CONDITION
so question only about CPUs behaviour and memory model of languages like C++ or Rust.