I've come across an hard-to-debug situation in one of my real projects where I was accidentally accessing a reference to a local variable inside a lambda that had been moved. The access was being done from another thread, but the moved lambda was kept alive until the second thread was finished.
The bug only occurred with optimizations disabled and was caused by careless refactoring.
I've created a minimal example (available here on wandbox) that reproduces the issue:
struct state
{
int x = 100;
};
template <typename TF>
void eat1(TF&& f)
{
// Call the lambda.
f();
// Simulate waiting for the second thread
// to finish.
std::this_thread::sleep_for(1000ms);
}
template <typename TF>
void eat0(TF&& f)
{
// Move the lambda to some other handler.
eat1(std::forward<TF>(f));
}
void use_state(state& s)
{
// Will print `100`.
std::cout << s.x << "\n";
// Separate thread. Note that `s` is captured by
// reference.
std::thread t{[&s]
{
// Simulate computation delay.
std::this_thread::sleep_for(500ms);
// Will print garbage.
std::cout << s.x << "\n";
}};
t.detach();
}
int main()
{
eat0([]
{
// Local lambda variable that will be accessed
// after the lambda is moved.
state s;
// Function that takes `s` by reference and
// accesses it in a separate thread after the
// lambda is moved.
use_state(s);
});
}
Surprisingly, none of the sanitizers and warning flags managed to help here.
I've tried the following combinations of compilers and sanitizers, with
-Wall -Wextra -Wpedantic -g -O0
flags always enabled:
Compilers: g++ 6.1.1 on Arch Linux x64; clang++ 3.8.0 on Arch Linux x64; g++ 5.3.1 on Fedora x64; clang++ 3.7.0 on Fedora x64.
Sanitizers:
-fsanitize=address
;-fsanitize=undefined
,-fsanitize=thread
.
None of the combinations produced any helpful diagnostic. I expected either AddressSanitizer to tell me I was accessing a dangling reference, or UndefinedSanitizer to catch UB while accessing it, or ThreadSanitizer to tell me a separate thread was accessing an invalid memory location.
Is there a reliable way to diagnose this problem? Should I post this example to any of the sanitizers' bug trackers as a feature request/defect?