Tencent / wcdb

WCDB is a cross-platform database framework developed by WeChat.
Other
10.78k stars 1.41k forks source link

句柄回收在遇到异常时未完全消化,而导致的随机崩溃死锁BUG #1127

Closed siren186 closed 2 months ago

siren186 commented 2 months ago

The language of WCDB

C++

The version of WCDB

v2.1.6

The platform of WCDB

Windows Visual Studio 2019

The installation of WCDB

Git clone

What's the issue?

这个BUG是随机的,以下代码几乎必现。出问题的位置也很随机,会随机出现崩溃、死锁等。 以下是复现代码:

void background1() {
    try {
        WCDB::Database db(R"(E:\temp\TestWCDB\x64\Release\test.db)");
        db.runPausableTransactionWithOneLoop([&](WCDB::Handle& handle, bool& stop, bool isNewTransaction) -> bool {

            // 此花括号作用域内抛出任何异常,都会造成 WCDB 工作不稳定
            throw std::exception(); // 如果注释掉这一行的异常抛出,则啥毛病都没有,一切正常。

            stop = true;
            return true;
            });
    } catch (const std::exception&) {}
}

void background2() {
    WCDB::Database db(R"(E:\temp\TestWCDB\x64\Release\test.db)");
    // 线程在 WCDB::Database 对象析构时,有概率会死锁。无法结束。
}

void test() {
    {
        std::thread t1(background1);
        t1.join();
    }

    {
        std::thread t2(background2);
        t2.join();
    }
}

int main() {
    for (int i = 0; i < 1000; i++) { // 循环这么多次是为了增加复现的概率
        test();
    }
    return 0;
}
Qiuwen-chen commented 2 months ago

WCDB has disabled c++ exception. You can check -fno-exceptions compile option in CMakeLists.

siren186 commented 2 months ago

WCDB 应该是:面对调用者代码有异常产生时,保证:

针对以上两点,WCDB 中发现以下两处代码,无法保证异常安全:

https://github.com/Tencent/wcdb/blob/76a46d7beb905bdcd71831658a1637215a302dc0/src/common/core/InnerHandle.cpp#L430

https://github.com/Tencent/wcdb/blob/76a46d7beb905bdcd71831658a1637215a302dc0/src/common/core/InnerHandle.cpp#L410

原因在于:调用外部传入的回调函数后,外部用户代码抛出异常,造成紧跟在该回调函数后面的 WCDB 内部代码 rollbackTransactioncommitOrRollbackTransaction 未被执行到,从而导致 WCDB 内部资源未正确回收。

建议在调用该外部回调函数之前,加一个 XXXGuard guard; 临时变量,在变量的析构函数中,释放相关资源。 这样无论外部调用者再怎么抛异常,WCDB 内部都不会出任何问题。异常安全得到保证!

// 析构函数伪代码
~XXXGuard() {
    if (m_succeed) {
        commitOrRollbackTransaction();
    } else {
        rollbackTransaction();
    }
}

附: Visual Studio 2019 并不支持编译选项:-fno-exceptions,以下是相关编译日志

warning D9002: 忽略未知选项“-fno-exceptions”
Qiuwen-chen commented 2 months ago

There are lots of details should be considered to support C++ exception, I don't plan to do that for now.

I recommend you to disable C++ exception, which will make the compiled result much smaller.