`is_always_lock_free` gives `true` but `is_lock_free()` gives `false` on macOS, why?
Asked Answered
D

1

29

I'm experimenting with C++ atomic's std::atomic<T>::is_always_lock_free and std::atomic<T>::is_lock_free.

I wrote a simple struct A and want to know if the atomic version of A is lock-free:

#include <iostream>
#include <atomic>

using namespace std;

struct A {
  int x;
  int y;
  int z;
};

int main() {
  atomic<A> b;

  cout << boolalpha;
  cout << "b.is_always_lock_free = " << b.is_always_lock_free << endl;
  cout << "b.is_lock_free = " << b.is_lock_free() << endl;

  return 0;
}

On a x86-64 Linux, I compiled it with g++ 9.4.0 and C++17, the output is normal:

b.is_always_lock_free = false
b.is_lock_free = false

However, I also compiled it with clang++ 16.0.0 on my Mac (ARM64), the output is strange:

b.is_always_lock_free = true
b.is_lock_free = false

Why is_always_lock_free = true and is_lock_free = false? If it can always be lock-free, why b is not lock-free?

Dietrich answered 30/4, 2023 at 3:46 Comment(6)
And at least with clang 14 on MacOS, the type is in fact lock free, and loads and stores give you a simple ldp/stp (which is atomic on ARMv8.4).Mediator
Just for the record on why GCC on x86-64 reports non-lock-free for types >8 bytes but <= 16, even with -march=native (on machines with -mcx16): GCC7 always avoids inlining lock cmpxchg16b and reports non-lock-free because it doesn't have the expected read-side scaling: readers contend with each other. gcc.gnu.org/ml/gcc-patches/2017-01/msg02344.html . This might perhaps change once GCC starts taking advantage of Intel's 16-byte load/store atomicity guarantees, retroactively documented in the last couple years, for Intel CPUs with the AVX feature flag.Otoole
for the record, C++20 standard in section 31.8.2 states that The return value of the is_lock_free member function is consistent with the value of is_always_lock_free for the same type.Siderolite
@Siderolite it can be situational with concrete platform\implementaitons where suddenly atomic requirementis greater than alignment of std::atomic<T> in this unit, Note that is_always_lock_free is a constant for all compile-units and apparently determined by clang before translation starts, but modules can be compiled with different settings, which doesn't cause problem in this particular case (a local variable). But it can, if that's a shared one. is_lock_free() is a function. I wonder if result would be same if variable would be global and in other unit.Tympanites
Can anyone reproduce? I don't have an ARM for testing but the assembly code, both OP's version and my minimized version, shows that b.is_always_lock_free resolves to 0. godbolt.org/z/xP3z54aczCentralize
@yyyy: Yes, I can confirm that on MacOS 13.6 on an M1 Pro laptop, using clang++ 15.0.0 from Apple's command line developer tools with -std=c++20, the output is true / false. Godbolt won't help here because it compiles most things in a Linux environment, and this issue comes from the MacOS system libraries.Mediator
A
6

It's a bug in the standard library libc++ (that is not present in libstdc++). Effectively it calls the builtin:

atomic_always_lock_free with sizeof(__cxx_atomic_impl<A>) which is 16,

whereas it calls

atomic_is_lock_free with sizeof(A) which is 12,

thus yielding inconsistent results.

Afroasiatic answered 11/11, 2023 at 14:40 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.