Search code examples
c++atomicstdatomic

Is there any case that the atomicity of std::atomic is not guaranteed, when is_lock_free() == false?


Even if is_lock_free() == false , typically maybe, the atomicity of std::atomic can be guaranteed. Is that right?

I wander that there are computer environments or cases where the atomicity is not guaranteed, when is_lock_free() == false.


Solution

  • Non-lock-free atomics aren't address-free, so shared memory between two processes would mean they don't respect each other's lock. (Same for two virtual mappings of a page holding the same atomic object in the same process, unless the hash table of locks is only indexed on the low bits of the address, the offset-within-page, but that would probably be a bad design.)

    ISO C++ doesn't define memory-mapping functions or processes, so the standard itself doesn't go into much detail other than saying lock-free atomics should be address-free (which makes them work in shared memory). That is the case on real-world implementations.

    Otherwise it's always atomic. The usual implementation mechanism is simple: use the address to index a table of spinlocks or mutexes.

    Of course, this assumes a program free of undefined behaviour, but so does everything else in C++. Any UB anywhere during the execution of a C++ program means all guarantees of anything before or after that point are out the window.

    In practice there aren't a lot of ways you could break atomicity of a non-lock-free std::atomic, other than really obvious stuff like using memcpy or a plain-uint64_t* to access the object representation, instead of going through the std::atomic<uint64_t> class.

    Lock-free std::atomic is actually easier to break with UB, e.g. by dereferencing an atomic<T>* that's not aligned to alignof(std::atomic<T>). On many ISAs that support unaligned loads/stores, that will run but not actually be atomic, because the C++ implementation was relying on hardware guarantees for atomicity of aligned loads and stores.