-
Notifications
You must be signed in to change notification settings - Fork 11.9k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
atomic::compare_exchange returns wrong value #30023
Comments
Could this be related to #29793 ? |
#29793 looks like a different bug: it talks about corruption of the atomic variable itself, and it tests atomic. |
Argh! Ignore me please, that one is ARM specific. I got confused. |
So this bug seems to have something to do with types with trailing padding. sizeof(T) == 6 but sizeof(_Atomic(T)) == 8. Adding two more bytes onto T works around the issue. I'm rebinning this as Clang since it reproduces with Clang's intrinsics. #include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <atomic>
template<int kSize>
struct MyStruct {
char data[kSize];
explicit MyStruct(char v = 0) noexcept {
memset(&data[0], v, sizeof(data));
}
bool operator==(const MyStruct &other) const {
return memcmp(&data[0], &other.data[0], sizeof(data)) == 0;
}
bool operator != (const MyStruct &other) const {
return !(*this == other);
}
int get_int() const {
return data[0];
}
};
int main() {
typedef MyStruct<6> T;
_Atomic(T) a(T(0));
T cmp(17);
if (__c11_atomic_compare_exchange_weak(&a, &cmp, T(18),
std::memory_order_seq_cst,
std::memory_order_seq_cst)) {
fprintf(stderr, "%d: bad atomic value %d\n", __LINE__, cmp.get_int());
exit(1);
}
if (cmp != T(0)) {
fprintf(stderr, "%d: bad atomic value %d\n", __LINE__, cmp.get_int());
exit(1);
}
} |
This is still an issue on current trunk 18.0.0git 2e45326. This affects classes for which |
The problem appears to be that clang/libc++ is using a lock-free implementation (lock cmpxchg) where this is inappropriate (it is only appropriate for register sized types); |
simpler example (godbolt): #include <atomic>
#include <cstddef>
template<std::size_t N>
struct S {
char a;
char padding_bytes[N];
};
#if PADDING > 0
static constexpr std::size_t padding = PADDING;
#else
static constexpr std::size_t padding = 0;
#endif
int main() {
std::atomic<S<padding>> x{{1}};
S<padding> expected{2};
const S<padding> desired{3};
x.compare_exchange_strong(expected, desired);
return expected.a;
} |
my bad this part is actually a godbolt error #56778 (comment)
|
This indeed looks wrong, based on the fact that Clang does the right thing with libstdc++ I indeed expect it is our bug. |
Hello @shafik @13steinj @ecatmur , The issue of You can find the libc++ status here: That being said, I am actively working on this paper just now. |
I feel that I should point out that That said, I don't doubt that this issue is connected to the resolution of P0528R3 and I'm grateful that you're working on it. Can I ask that you add tests for this case? |
Yes sure I will definitely add tests for that. Yes the struct itself does not have padding. Actually it is us (Libc++) who adds the padding via _Atomic ( which is meant to make types size power of 2, which IIRC is the IR layer requirement to use compare exchange). |
Investigated a bit and indeed this is a different issue. there might be multiple problems but the first problem I have seen is in CGAtomic.cpp where we emit LLVM IR from from the builtin __c11_atomic_compare_exchange_strong The following code would assign if (ShouldCastToIntPtrTy) {
Ptr = Atomics.castToAtomicIntPointer(Ptr);
if (Val1.isValid())
Val1 = Atomics.convertToAtomicIntPointer(Val1);
if (Val2.isValid())
Val2 = Atomics.convertToAtomicIntPointer(Val2);
} later after cmpxchg, when it stores the results of updated CGF.Builder.CreateStore(Old, Val1); This would generate the following IR cmpxchg.store_expected: ; preds = %entry
store i64 %5, ptr %atomic-temp, align 8
br label %cmpxchg.continue So the updated |
@llvm/issue-subscribers-clang-codegen Author: None (a8b4922b-9ceb-405e-9794-fa0271302cc2)
| | |
| --- | --- |
| Bugzilla Link | [30675](https://llvm.org/bz30675) |
| Version | trunk |
| OS | Linux |
| CC | @chandlerc,@eugenis,@kcc,@mclow,@rengolin |
Extended Descriptionllvm/clang/libc++ are on rev 271514. Test is: #include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <atomic>
template<int kSize>
struct MyStruct {
char data[kSize];
explicit MyStruct(char v = 0) noexcept {
memset(&data[0], v, sizeof(data));
}
bool operator == (const MyStruct &other) const {
return memcmp(&data[0], &other.data[0], sizeof(data)) == 0;
}
bool operator != (const MyStruct &other) const {
return !(*this == other);
}
operator int() const {
return data[0];
}
};
int main() {
typedef MyStruct<6> T;
std::atomic<T> a(T(0));
T cmp(17);
if (a.compare_exchange_strong(cmp, T(18), std::memory_order_acquire)) {
fprintf(stderr, "%d: bad atomic value %d\n", __LINE__, (int)cmp);
exit(1);
}
if (cmp != T(0)) {
fprintf(stderr, "%d: bad atomic value %d\n", __LINE__, (int)cmp);
exit(1);
}
} The test works with libstdc++:
But fails with libc++:
|
@llvm/issue-subscribers-c-11 Author: None (a8b4922b-9ceb-405e-9794-fa0271302cc2)
| | |
| --- | --- |
| Bugzilla Link | [30675](https://llvm.org/bz30675) |
| Version | trunk |
| OS | Linux |
| CC | @chandlerc,@eugenis,@kcc,@mclow,@rengolin |
Extended Descriptionllvm/clang/libc++ are on rev 271514. Test is: #include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <atomic>
template<int kSize>
struct MyStruct {
char data[kSize];
explicit MyStruct(char v = 0) noexcept {
memset(&data[0], v, sizeof(data));
}
bool operator == (const MyStruct &other) const {
return memcmp(&data[0], &other.data[0], sizeof(data)) == 0;
}
bool operator != (const MyStruct &other) const {
return !(*this == other);
}
operator int() const {
return data[0];
}
};
int main() {
typedef MyStruct<6> T;
std::atomic<T> a(T(0));
T cmp(17);
if (a.compare_exchange_strong(cmp, T(18), std::memory_order_acquire)) {
fprintf(stderr, "%d: bad atomic value %d\n", __LINE__, (int)cmp);
exit(1);
}
if (cmp != T(0)) {
fprintf(stderr, "%d: bad atomic value %d\n", __LINE__, (int)cmp);
exit(1);
}
} The test works with libstdc++:
But fails with libc++:
|
Extended Description
llvm/clang/libc++ are on rev 271514.
Test is:
The test works with libstdc++:
But fails with libc++:
The text was updated successfully, but these errors were encountered: