Why is std::atomic<T>::is_lock_free() not static as well as constexpr? Why is std::atomic<T>::is_lock_free() not static as well as constexpr? multithreading multithreading

Why is std::atomic<T>::is_lock_free() not static as well as constexpr?


As explained on cppreference:

All atomic types except for std::atomic_flag may be implemented using mutexes or other locking operations, rather than using the lock-free atomic CPU instructions. Atomic types are also allowed to be sometimes lock-free, e.g. if only aligned memory accesses are naturally atomic on a given architecture, misaligned objects of the same type have to use locks.

The C++ standard recommends (but does not require) that lock-free atomic operations are also address-free, that is, suitable for communication between processes using shared memory.

As mentioned by multiple others, std::is_always_lock_free might be what you are really looking for.


Edit: To clarify, C++ object types have an alignment value that restricts the addresses of their instances to only certain multiples of powers of two ([basic.align]). These alignment values are implementation-defined for fundamental types, and need not equal the size of the type. They can also be more strict than what the hardware could actually support.

For example, x86 (mostly) supports unaligned accesses. However, you will find most compilers having alignof(double) == sizeof(double) == 8 for x86, as unaligned accesses have a host of disadvantages (speed, caching, atomicity...). But e.g. #pragma pack(1) struct X { char a; double b; }; or alignas(1) double x; allows you to have "unaligned" doubles. So when cppreference talks about "aligned memory accesses", it presumably does so in terms of the natural alignment of the type for the hardware, not using a C++ type in a way that contradicts its alignment requirements (which would be UB).

Here is more information: What's the actual effect of successful unaligned accesses on x86?

Please also check out the insightful comments by @Peter Cordes below!


You may use std::is_always_lock_free

is_lock_free depends on the actual system and can't be determined at compile time.

Relevant explanation:

Atomic types are also allowed to be sometimes lock-free, e.g. if only aligned memory accesses are naturally atomic on a given architecture, misaligned objects of the same type have to use locks.


I've got installed Visual Studio 2019 on my Windows-PC and this devenv has also an ARMv8-compiler. ARMv8 allows unaligned accesses, but compare and swaps, locked adds etc. are mandated to be aligned. And also pure load / pure store using ldp or stp (load-pair or store-pair of 32-bit registers) are only guaranteed to be atomic when they're naturally aligned.

So I wrote a little program to check what is_lock_free() returns for an arbitrary atomic-pointer. So here's the code:

#include <atomic>#include <cstddef>using namespace std;bool isLockFreeAtomic( atomic<uint64_t> *a64 ){    return a64->is_lock_free();}

And this is the disassembly of isLockFreeAtomic

|?isLockFreeAtomic@@YA_NPAU?$atomic@_K@std@@@Z| PROC    movs        r0,#1    bx          lrENDP

This is just returns true, aka 1.

This implementation chooses to use alignof( atomic<int64_t> ) == 8 so every atomic<int64_t> is correctly aligned. This avoids the need for runtime alignment checks on every load and store.

(Editor's note: this is common; most real-life C++ implementations work this way. This is why std::is_always_lock_free is so useful: because it's usually true for types where is_lock_free() is ever true.)