Even if everything is declared `volatile`, in C or C++, the compiler or the processor can reorder the stores and reads, so that the reading thread can see the "global_pointer = my_buffer" before the "->something = 42". The only safe way to do it is to add the appropriate memory barriers on both the writing and reading sides, which force the compiler and the processor to not reorder the writes/reads.
my_buffer->something = 42;
write_barrier(); // Not the actual function; will vary depending on your environment.
global_pointer = my_buffer;
write_barrier(); // Not the actual function; will vary depending on your environment.
global_flag = 1;
And on the reading side, the corresponding read barriers.
It's simpler to use atomic loads and stores or locks, since their implementation already has the required barriers with all the details (quick: what's the difference between an acquire and a release atomic access?).
(Note that this is different in Java, where `volatile` always implies a memory barrier.)
> what's the difference between an acquire and a release atomic access
ACQUIRE prevents reordering any loads and stores from after the barrier to before. RELEASE is the opposite, no load or store before it may happen after the barrier.
ACQUIRE is what you use when locking a spinlock, RELEASE is when you unlock.
Put these incorrectly and you're not guaranteed to have all loads and stores happening when the spinlock is locked, and your program is no longer guaranteed to behave as expected.
It's simpler to use atomic loads and stores or locks, since their implementation already has the required barriers with all the details (quick: what's the difference between an acquire and a release atomic access?).
(Note that this is different in Java, where `volatile` always implies a memory barrier.)