Details
-
Bug
-
Resolution: Done
-
P2: Important
-
5.15.2, 6.2
-
None
-
-
f274f91cebb0a4fd2ebe37bb3a605c47d6acd404 ba0a105c617ad40f2a3eb2bb2ac2993ed6b11133 (qt/qtbase/6.1) 864b17ec1bbea826a19dfad113e560c58cc65e61 (qt/tqtc-qtbase/tqtc/lts-5.15) 955ce882f7c94f6085e26ef3f987fbfa72a1a8d7 (qt/qtbase/6.0)
Description
QThread::quit() has a race condition on Windows that means the quit() call is ignored. The following example fails for me within 30 seconds. With failure I mean the event loop doesn't quit correctly and the program prints the "Failed to quit" message and then triggers the qFatal for deleting a running thread. In my real world code it fails with a much higher frequency - in 1 of ~50 runs, but this is the best I could do with a minimal example. When I try to reduce the example further, the race condition isn't triggered and the for-loop continues to run. The problem does not reproduce with Qt 5.15.2 on Fedora 32 x86_64 Linux. I've only seen it with 5.15.2 on Windows x64. I have not tried reproducing with older versions of Qt 5 or with Qt 6.
#include <future> #include <iostream> #include <QCoreApplication> #include <QThread> #include <QTimer> class TestThread : public QThread { public: ~TestThread() { quit(); if (!wait(10000)) { std::cerr << "Failed to quit" << std::endl; } } void run() override { QObject object; m_promise.set_value(&object); QThread::run(); } QObject *objectInThread() { return m_future.get(); } private: std::promise<QObject*> m_promise; std::shared_future<QObject*> m_future = m_promise.get_future(); }; int main(int argc, char **argv) { QCoreApplication app(argc, argv); for (int i = 0; i < 10000000; ++i) { std::cout << "iteration " << i << std::endl; TestThread t; t.start(); std::promise<bool> prom; std::shared_future<bool> fut = prom.get_future(); QMetaObject::invokeMethod(t.objectInThread(), [&] { // When we call prom.set_value we signal to the main thread to call QThread::quit() // on "our" thread. prom.set_value(true); // To trigger the race condition we then immediately after schedule some work // for the event loop. QTimer::singleShot(0, [] {}); }, Qt::QueuedConnection); fut.get(); } }