Uploaded image for project: 'Qt'
  1. Qt
  2. QTBUG-91539

QThread::quit() is unreliable on Windows

    XMLWordPrintable

Details

    • Windows
    • 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();
          }
      }
      
      

      Attachments

        For Gerrit Dashboard: QTBUG-91539
        # Subject Branch Project Status CR V

        Activity

          People

            manordheim Mårten Nordheim
            ts Thomas Sondergaard
            Votes:
            0 Vote for this issue
            Watchers:
            10 Start watching this issue

            Dates

              Created:
              Updated:
              Resolved: