-
Notifications
You must be signed in to change notification settings - Fork 1k
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
TSan: false positive due to compiler optimization #1398
Comments
Please provide a link to source code and a full report. |
This was with clang-12. I stubled over this issue while running some integrations from https://github.com/arangodb/arangodb, but you probably don't want to debug this issue with that source base! I will try to find some time and create a stripped down example to reproduce this. |
I am able to reproduce this with the following sample program: #include <atomic>
#include <deque>
#include <memory>
#include <thread>
struct Dummy {
unsigned number;
};
struct Versions {
std::atomic<unsigned> last_sequence;
std::atomic<unsigned> last_published_sequence;
};
struct Foo {
explicit Foo(Versions* versions) : versions(versions) {}
std::deque<Dummy> snapshots;
bool cond = true;
const Versions* versions;
unsigned foo() {
unsigned oldest_snapshot;
if (snapshots.empty()) {
oldest_snapshot = cond
? versions->last_sequence.load(std::memory_order_relaxed)
: versions->last_published_sequence.load(std::memory_order_relaxed);
} else {
oldest_snapshot = snapshots.begin()->number;
}
return oldest_snapshot;
}
};
int main() {
std::unique_ptr<Versions> versions = std::make_unique<Versions>();
std::atomic<bool> stop{false};
std::thread t1([&stop, &versions]() {
unsigned i = 0;
while (!stop)
versions->last_sequence.store(++i, std::memory_order_relaxed);
});
std::thread t2([&stop, &versions]() {
Foo f(versions.get());
while (!stop)
volatile unsigned v = f.foo();
});
std::this_thread::sleep_for(std::chrono::seconds(1));
stop = true;
t1.join();
t2.join();
return 0;
} We only ever access
|
I can reproduce this with I've added attribute((noinline)) to foo and got this:
|
Without -fsanitize=thread the code looks strange as well:
WAT?
WAT?
|
It seems that this is a generic codegen bug that also affects normal builds. I would assume one of these duplicated loads is "atomic" and another is "non-atomic". In normal build this only leads to a performance regression. In tsan it becomes more visible. |
Yes, I also found the code rather strange, but TBH I did not check what it looks like without tsan. Seems like this should be reported to clang then. |
Here is a bit more simplified code:
I've also re-tested with the current llvm HEAD: code with -O1 (looks bad):
code with -O1 -fsanitize=thread (looks bad as well):
This needs to be filed at https://bugs.llvm.org/ |
I think I don't have an account yet at https://bugs.llvm.org - could you file it instead? Otherwise I'll contact the admin to create an account for me. |
I will file it. |
Ouch! I see a bad miscompilation in release build at least on arm64, but I won't be surprised if this can lead to some miscompilations on slightly modified code on x86 as well.
|
Filed https://bugs.llvm.org/show_bug.cgi?id=50139 |
Thanks for filing! Seems to be a good test case... :) |
It's now at llvm/llvm-project#49483 |
While running some code using RocksDB I received the following data race warning:
The relevant code in
DBImpl::ReleaseSnapshot
looks like this:The generated assembler code looks like this:
Effectively the compiler generated code that stores different addresses in
rbx
based on the conditions, and later simply dereferences that register to populate the variableoldest_snapshot
. However, this causes false positive data race warnings in TSan.I did not yet have the time to try to create a stripped down example that reproduces this problem, but I hope I have provided all relevant information.
The text was updated successfully, but these errors were encountered: