If the compiler doesn’t provide modern (C++11) atomic builtins (which is
now quite unlikely), we implement our own using the __sync_synchronize()
memory barrier. As Behdad and others have pointed out, though, the
implementation didn’t follow the same semantics as we use with the C++11
builtins — __ATOMIC_SEQ_CST
.
Fix the use of memory barriers to provide __ATOMIC_SEQ_CST
semantics.
In particular, this fixes the following common pattern:
GObject *obj = my_object_new ();
g_atomic_pointer_set (&shared_ptr, obj);
Previously this would have expanded to:
GObject *obj = my_object_new ();
*shared_ptr = obj;
__sync_synchronize ();
While the compiler would not have reordered the stores to obj
and
shared_ptr
within the code on one thread (due to the dependency
between them), the memory system might have made the write to
shared_ptr
visible to other threads before the write to obj
— if
they then dereferenced shared_ptr
before seeing the write to obj
,
that would be a bug.
Instead, the expansion is now:
GObject *obj = my_object_new ();
__sync_synchronize ();
*shared_ptr = obj;
This ensures that the write to obj
is visible to all threads before
any write to shared_ptr
is visible to any threads. For completeness,
__sync_synchronize()
is augmented with a compiler barrier to ensure
that no loads/stores can be reordered locally before or after it.
Tested by disabling the C++11 atomic implementation and running:
meson test --repeat 1000 atomic atomic-test
Signed-off-by: Philip Withnall withnall@endlessm.com
Fixes: #1449 (closed)