Skip to content

gatomic: Reorder memory barriers in fallback atomic operations

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)

Merge request reports