4

According to cppreference, hardware could require that an object to be referenced by an atomic_ref<T> have stricter alignment than other T objects, and whether operations on an atomic_ref are lock-free can depend on the alignment of the referenced object.

Why is it only necessary that objects to be referenced by an atomic_ref have proper alignment, whereas std::atomic does not seem to impose this requirement?

Peter Cordes
  • 328,167
  • 45
  • 605
  • 847
mentalmushroom
  • 2,261
  • 1
  • 26
  • 34
  • 1
    Where did you get that std::atomic does not have this requirement? Because it does! – mpoeter Mar 10 '23 at 13:35
  • What if `int` is unaligned (as could be on Intel, although a silly idea) but `std::atomic` needs `sizeof(int)` alignment? Not possible? – curiousguy Mar 10 '23 at 16:56
  • 1
    @curiousguy: The alignment requirement / guarantee (`alignof(int)`) is set by the compiler / ABI (the C++ implementation), not just the target ISA. All mainstream C++ compilers use `alignof(int) == 4`, so it's UB to deref (or technically even to create) an unaligned `int*`. See [Why does unaligned access to mmap'ed memory sometimes segfault on AMD64?](https://stackoverflow.com/q/47510783) - it can actually break even when compiling for x86-64, despite the fact that accesses narrower than 16 bytes wide don't need alignment in asm for that target. – Peter Cordes Mar 10 '23 at 20:03

1 Answers1

11

std::atomic allocates the T object as a class member, so its internals can apply sufficient alignment.

It's not well-defined to point an atomic<T>* at an existing T object, although in many implementations it happens to work. That's the problem that std::atomic_ref<T> is designed to solve; doing atomic accesses on a plain T object that exists separately and which you might do some non-atomic accesses to in other phases of your program. Before C++20, the standard didn't provide a way to do that at all; that's why a programmer-visible requirement to align a plain T object yourself is new in C++20 if you want to use atomic_ref, like

alignas(std::atomic_ref<T>::required_alignment) T foo;

std::atomic_ref<T>::required_alignment will be equal to alignof(std::atomic<T>), even if alignof(T) is lower. At least in normal implementations.


Without std::atomic_ref, you could of course use the compiler-specific extensions like GNU C __atomic builtins on plain objects.

That's how std::atomic<T> and std::atomic_ref<T> are implemented in GCC / clang headers, rather than direct support in the compiler proper like with the C11 _Atomic keyword. (Which has to do its own over-alignment of things it's applied to. There was GCC bug 65146 for a while where _Atomic didn't force the alignment of int64_t even on 32-bit targets where it only had 4-byte alignment such as x86; it worked outside structs because GCC chose to align int64_t for performance, but was not actually atomic inside structs. G++'s std::atomic<> had fixed that bug earlier, in the <atomic> header, but C _Atomic is internal to the compiler.)

Peter Cordes
  • 328,167
  • 45
  • 605
  • 847