c++ - 在移动的 lambda 中捕获和调试对局部变量引用的无效使用

标签 c++ debugging c++14 undefined-behavior sanitizer

在我的一个实际项目中,我遇到了一个难以调试的情况,我不小心访问了对已移动的 lambda 内部局部变量的引用。访问是从另一个线程完成的,但是移动的 lambda 一直保持事件状态,直到第二个线程完成。

该错误仅在禁用优化时出现,并且是由粗心的重构引起的。

我创建了一个最小示例 (available here on wandbox)重现问题:

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);
        });
}

令人惊讶的是,所有 sanitizer 和警告标志都没有起到帮助作用。

我尝试了以下编译器和 sanitizer 的组合,

-Wall -Wextra -Wpedantic -g -O0

标志始终启用:

  • 编译器:g++ 6.1.1 在 Arch Linux x64 上; clang++ 3.8.0 在 Arch Linux x64 上; g++ 5.3.1 在 Fedora x64 上; Fedora x64 上的 clang++ 3.7.0

  • sanitizer :-fsanitize=address-fsanitize=undefined-fsanitize=thread

没有一种组合产生任何有用的诊断。我希望 AddressSanitizer 告诉我我正在访问悬空引用,或者 UndefinedSanitizer 在访问它时捕获 UB,或者 ThreadSanitizer 告诉我一个单独的线程正在访问无效的内存位置。

是否有可靠的方法来诊断此问题?我是否应该将此示例作为功能请求/缺陷发布到任何 sanitizer 的错误跟踪器?

最佳答案

valgrind 的 memcheck 工具在默认设置下发现了这个问题。然而,这种讨厌的错误有机会逃脱 memcheck。我不确定问题是否会在实际程序中被发现。

第一个 lambda 被移动的事实与问题无关(尽管它可能使调试过程复杂化)。问题是由于访问已完成执行的函数中的局部变量(同样,从不同线程发生访问的事实只会使调查更加困难,但不会以任何其他方式导致错误)。第一个 lambda 保持事件状态的事实绝不应该保护您 - 局部变量属于 lambda 调用 而不是 lambda 本身。

关于c++ - 在移动的 lambda 中捕获和调试对局部变量引用的无效使用,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/39307730/

相关文章:

c++ - 带有 C++ 主文件的 FreeRTOS

c++ - 什么时候应该使用统一初始化?

c++ - 如何修复二进制运算符

c++ - Qt Creator 在指定的调试文件夹中创建调试和发布文件夹

c++ - SFINAE 优雅地检查 "template template class"(在模板参数中)

c++ - 将参数包转换为 vector

c++ - 这两种比较STL vector 的方法有什么区别?

java - 使用 Eclipse 和 JBoss 调试 JSP

python - 如何在 IDE 中调试 Cython

c++ - C++11 或 C++14 是否支持三值或多值逻辑?