我可以在 googletest 中测试死锁吗?

Can I test deadlock in googletest?

我有一个创建多个线程的小 class。当我调用 stop() 方法时,它会设置一个停止线程的标志并加入线程直到它们停止。这是 stop() 方法的代码:

void stop() {
    running_flag_ = false;
    for (auto& th : threads_) {
        if (th.joinable())
            th.join();
    }
}

我想知道是否有办法测试此方法是否按预期工作,这意味着此方法应该始终 return。我觉得dead test在这里不太适合。知道是否有办法对此进行测试吗?

当你遇到死锁时,一些线程正在等待锁,直到它们被释放。如果不添加额外的代码,就无法从这种状态恢复进程。

解决此问题的唯一方法是检测超时并在超时时使单元测试应用程序崩溃。前段时间我使用这段代码来检测问题,所以构建机器不会挂得太久:

class TerminateOnDeadlockGuard final
{
public:
    using Clock = std::chrono::system_clock;
    using Duration = Clock::duration;

    explicit TerminateOnDeadlockGuard(Duration timeout = std::chrono::seconds{30});

    ~TerminateOnDeadlockGuard();

private:
    void waitForCompletion();

private:
    const Duration m_timeout;
    std::mutex m_mutex;
    std::condition_variable m_wasCompleted;
    std::condition_variable m_guardStarted;
    std::thread m_guardThread;
};
//---------
TerminateOnDeadlockGuard::TerminateOnDeadlockGuard(Duration timeout) : m_timeout{timeout}
{
    m_guardThread = std::thread(&TerminateOnDeadlockGuard::waitForCompletion, this);
    std::unique_lock lock{m_mutex};
    m_guardStarted.wait(lock);
}

TerminateOnDeadlockGuard::~TerminateOnDeadlockGuard()
{
    m_wasCompleted.notify_one();
    m_guardThread.join();
}

void TerminateOnDeadlockGuard::waitForCompletion()
{
    std::unique_lock lock{m_mutex};
    m_guardStarted.notify_one();
    if (std::cv_status::timeout == m_wasCompleted.wait_for(lock, m_timeout))
    {
        std::cerr << "Test timeout!!!" << std::endl;
        std::exit(-1); // you can use `abort` here to create crash log.
    }
}

用法:

TEST(SomeTestSuite, myTest)
{
    TerminateOnDeadlockGuard guardDeadLock;

    // test code here
}

现在 google death test 可以帮上忙。 这个 fork process at let child process to terminate.

using namespace std::chrono_literals;

class NoDeadLockTest : public ::testing::Test
{
public:
    void someTest()
    {
        TerminateOnDeadlockGuard guardDeadLock{5s};
        actualTest();
        std::exit(::testing::Test::HasFailure() ? 2 : 0);
    }

    void actualTest()
    {
#if VERSION == 0
        ASSERT_TRUE(true);
#elif VERSION == 1
        ASSERT_TRUE(false);
#elif VERSION == 2
        std::this_thread::sleep_for(10s);
#endif
    }
};

TEST_F(NoDeadLockTest, myTest)
{
    EXPECT_EXIT(someTest(), testing::ExitedWithCode(0), "");
}

我已经在 Godbolt 上检查过了,它远非完美:https://godbolt.org/z/nasros577 但表明如果付出额外的努力,还是有希望的。

我中继推荐使用一些工具来检测死锁和修复代码。我使用了线程消毒剂 - 我试过了,非常有效。