title: Qt 5 - How to make QThread exit from its `wait` function where it got stuck after executing a QProcess on Windows?
body: <p>I believe I've found a bug in the Windows' Qt 5 implementation. It doesn't reproduce with Qt 6, so I don't think I should post it to Qt's maintainers now. But still I'd like to ask here (1) if it's a bug indeed (or is my code just incorrect somewhere), and (2) what workaround can I write to avoid this issue, provided that I can't upgrade to Qt 6 right now.</p>
  <p>I have a class <code>BackgroundExecutor</code> which owns a <code>QThread</code> and has a function for posting new tasks (<code>std::function</code> instances) to it. In its destructor, <code>BackgroundExecutor</code> calls <code>quit</code> and <code>wait</code> member functions of its thread object.</p>
  <p>Things get interesting when one of the posted tasks processed by the background <code>QThread</code> happens to have executed some external <code>QProcess</code> (I think it affects the state of the thread's <code>QEventLoop</code> somehow). In this case, the <code>wait</code> call on a <code>QThread</code> has a chance to hang forever.</p>
  <p>The call stack of the main thread looks like this:</p>
  <pre><code>    ntdll.dll!NtWaitForSingleObject()  Unknown
      KernelBase.dll!WaitForSingleObjectEx() Unknown
  &gt;   Qt5Cored.dll!QThread::wait(QDeadlineTimer deadline) Line 630    C++
      QThreadAndQProcessBug.exe!BackgroundExecutor::~BackgroundExecutor() Line 87 C++
      QThreadAndQProcessBug.exe!RunTest() Line 123    C++
      QThreadAndQProcessBug.exe!RunTestMultipleTimes() Line 132   C++
  </code></pre>
  <p>And here's the call stack of the background thread:</p>
  <pre><code>    win32u.dll!NtUserMsgWaitForMultipleObjectsEx() Unknown
      user32.dll!RealMsgWaitForMultipleObjectsEx()    Unknown
  &gt;   Qt5Cored.dll!QEventDispatcherWin32::processEvents(QFlags&lt;enum QEventLoop::ProcessEventsFlag&gt; flags) Line 625    C++
      Qt5Cored.dll!QEventLoop::processEvents(QFlags&lt;enum QEventLoop::ProcessEventsFlag&gt; flags) Line 140   C++
      Qt5Cored.dll!QEventLoop::exec(QFlags&lt;enum QEventLoop::ProcessEventsFlag&gt; flags) Line 232    C++
      Qt5Cored.dll!QThread::exec() Line 547   C++
      Qt5Cored.dll!QThread::run() Line 617    C++
      Qt5Cored.dll!QThreadPrivate::start(void * arg) Line 407 C++
  </code></pre>
  <p>It's stuck at the line 625 (as of Qt 5.15.2) of <em>&quot;qeventdispatcher_win.cpp&quot;</em>, inside the <code>QEventDispatcherWin32::processEvents</code> function: <code>waitRet = MsgWaitForMultipleObjectsEx(nCount, pHandles, INFINITE, QS_ALLINPUT, MWMO_ALERTABLE | MWMO_INPUTAVAILABLE);</code>.</p>
  <p>The full text of the program which reproduces the issue (though it might take some time - one of my PCs requires only 1000 iterations on average, while the other one might execute 100'000 iterations before the hang would occur):</p>
  <pre><code>#include &lt;QCoreApplication&gt;
  #include &lt;QTimer&gt;
  #include &lt;QObject&gt;
  #include &lt;QProcess&gt;
  #include &lt;QThread&gt;
  
  #include &lt;functional&gt;
  #include &lt;future&gt;
  #include &lt;memory&gt;
  #include &lt;iostream&gt;
  
  Q_DECLARE_METATYPE(std::function&lt;void()&gt;); // for passing std::function&lt;void()&gt; through Qt's signals
  
  static void EnsureStdFunctionOfVoidMetaTypeRegistered()
  {
    static std::once_flag std_function_metatype_registered{};
    std::call_once(std_function_metatype_registered, []() {
      qRegisterMetaType&lt;std::function&lt;void()&gt;&gt;(&quot;std::function&lt;void()&gt;&quot;);
      });
  }
  
  class WorkerObject; // worker object that &quot;lives&quot; in a background thread of a BackgroundExecutor
  
  class BackgroundExecutor final
  {
  public:
    BackgroundExecutor();
    ~BackgroundExecutor();
  
    // posts a new task for the background QThread,
    // returns a std::future which can be waited on to ensure the task is done
    [[nodiscard]] std::future&lt;void&gt; PostTask(std::function&lt;void()&gt; task);
  
  private:
    WorkerObject* _background_worker = nullptr;
    QThread* _qt_thread = nullptr;
  };
  
  class WorkerObject final : public QObject
  {
    Q_OBJECT;
  
  public:
    WorkerObject()
    {
      connect(this, &amp;WorkerObject::TaskPosted, this, &amp;WorkerObject::ProcessPostedTask);
    }
  
    // can be called from any thread;
    // &quot;moves&quot; the task to the background worker thread via Qt's signals/slots mechanism
    // so that it could be processed there
    void PostTask(const std::function&lt;void()&gt;&amp; task)
    {
      EnsureStdFunctionOfVoidMetaTypeRegistered();
      Q_EMIT TaskPosted(task);
    }
  
  private Q_SLOTS:
    void ProcessPostedTask(const std::function&lt;void()&gt;&amp; posted_task)
    {
      std::invoke(posted_task);
    }
  
  Q_SIGNALS:
    void TaskPosted(const std::function&lt;void()&gt;&amp;);
  };
  
  BackgroundExecutor::BackgroundExecutor()
  {
    {
      std::unique_ptr&lt;QThread&gt; qt_thread_safe(new QThread()); // exception safety
      _background_worker = new WorkerObject();
      _qt_thread = qt_thread_safe.release();
    }
  
    _background_worker-&gt;moveToThread(_qt_thread);
  
    QObject::connect(_qt_thread, &amp;QThread::finished, _background_worker, &amp;WorkerObject::deleteLater);
    QObject::connect(_qt_thread, &amp;QThread::finished, _qt_thread, &amp;QThread::deleteLater);
  
    _qt_thread-&gt;start();
  }
  
  BackgroundExecutor::~BackgroundExecutor()
  {
    _qt_thread-&gt;quit();
    _qt_thread-&gt;wait(); // !!! might hang !!!
  }
  
  [[nodiscard]] std::future&lt;void&gt; BackgroundExecutor::PostTask(std::function&lt;void()&gt; task)
  {
    std::shared_ptr task_promise = std::make_shared&lt;std::promise&lt;void&gt;&gt;();
    std::future task_future = task_promise-&gt;get_future();
  
    std::function&lt;void()&gt; task_wrapper = [task_promise = std::move(task_promise), task = std::move(task)]()
    {
      std::invoke(task);
      task_promise-&gt;set_value();
    };
  
    _background_worker-&gt;PostTask(task_wrapper);
    return task_future;
  }
  
  static void RunQProcessAndWaitForFinished()
  {
    QProcess process;
    process.setProgram(&quot;C:\\Windows\\System32\\cmd.exe&quot;);
    process.setArguments({ &quot;/C&quot;, &quot;C:\\Windows\\System32\\timeout.exe&quot;, QString::number(30) });
  
    process.start();
    process.waitForStarted(-1);
    process.waitForFinished(-1);
  }
  
  static void RunTest()
  {
    BackgroundExecutor executor;
    std::future task_future = executor.PostTask([]() {
      RunQProcessAndWaitForFinished();
      });
    task_future.get();
  }
  
  static void RunTestMultipleTimes()
  {
    constexpr int repeat = 500'000;
    for (int i = 0; i &lt; repeat; ++i)
    {
      std::cout &lt;&lt; &quot;starting iteration &quot; &lt;&lt; i &lt;&lt; '\n';
      RunTest();
    }
    std::cout &lt;&lt; &quot;all iterations finished&quot; &lt;&lt; '\n';
  }
  
  int main(int argc, char** argv)
  {
    QCoreApplication qt_app{ argc, argv };
  
    QTimer::singleShot(
      0,
      [&amp;]()
      {
        RunTestMultipleTimes();
        qt_app.exit(0);
      });
  
    return qt_app.exec();
  }
  
  #include &quot;main.moc&quot;
  
  </code></pre>