I have a set of global values that are initialized at program start and never change during the run. Those values are read from multiple threads, and I want to make sure the initialization is done correctly for all threads.
In this example,
int A, B;
void init() {
A = 1;
B = 2;
}
void read(int *a, int *b) {
*a = A;
*b = B;
}
void f() {
/* ... */
read(&a, &b);
/* ... */
}
int main() {
init();
startThread(f);
/* ... */
}
My understanding is that the compiler isn't allowed to put init
after startThread
because the side effects of the functions should be sequenced as written, so the machine instructions to store values in A
and B
will come before the thread start. However, this doesn't guarantee that the load in read
from a different thread will happen after the stores are complete because the stores might for some reason be deferred after the loads.
Here's an obvious fix.
void init() {
A = 1;
B = 2;
atomic_thread_fence(memory_order_release);
}
void read(int *a, int *b) {
atomic_thread_fence(memory_order_acquire);
*a = A;
*b = B;
}
It should work if I've read the specs correctly, but the problem is that now, all functions that read those global values need a fence at function entry, and I'd like to avoid this. Unfortunately, all defined memory orders including memory_order_seq_cst
are defined for cases where the reader is an atomic operation or has a fence.
Under the strong memory model of x86, the example code will work without any fix. I'm looking for a portable solution, but I'm also wondering whether it really could break on a different platform without synchronization.
If synchronization is necessary, what is a good way in this case? Is it possible without being intrusive to all the reader functions?