Skip to content

Commit

Permalink
Fix the deadlock in trikPythonRunner (#809)
Browse files Browse the repository at this point in the history
* Fix the deadlock in trikPythonRunner
When the destructor of `trikPythonRunner` is called, `mWorkerThread->quit()` is invoked. The `finished` signal of `mWorkerThread` is connected to `pythonEngineWorker::deleteLater`. However, when attempting to free resources immediately after script execution (in `PythonEngineWorker::doRun()`), calls to `QCoreApplication::processEvents` reset the `interrupt` flag set by `mWorkerThread->quit()`. As a result, `QEventLoop::exec` [calls](https://github.com/qt/qtbase/blob/7a7804d4b454021d68d1d5138d134ef62abfcdb3/src/corelib/kernel/qeventloop.cpp#L195) bloking [`MsgWaitForMultipleObjectsEx`](https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-msgwaitformultipleobjectsex)  (via `QEventDispatcherWin32::processEvents`)  with the [`INFINITE`](https://github.com/qt/qtbase/blob/7a7804d4b454021d68d1d5138d134ef62abfcdb3/src/corelib/kernel/qeventdispatcher_win.cpp#L549) flag (specifically for `QEventLoop::WaitForMoreEvents`), which leads to waiting for a [`PostMessage`](https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-postmessagea) call that can be triggered by `QEventLoop::wakeUp`, ` QAbstractEventDispatcher::interrupt`, or re-calling `qthread->quit`. Calls to` QCoreApplication::processEvents` with `QEventLoop::WaitForMoreEvents` generate the `QAbstractEventDispatcher::aboutToBlock` signal. This solution should not introduce regression because all current calls to `QCoreApplication::processEvents` occur with `QEventLoop::AllEvents` and will not be canceled.
  • Loading branch information
MinyazevR authored Dec 24, 2024
1 parent 5806271 commit 3bc464a
Showing 1 changed file with 11 additions and 0 deletions.
11 changes: 11 additions & 0 deletions trikScriptRunner/src/trikPythonRunner.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
#include <stdexcept>
#include <PythonQt.h>
#include <QCoreApplication>
#include <QAbstractEventDispatcher>

#include "trikPythonRunner.h"
#include "src/pythonEngineWorker.h"
Expand Down Expand Up @@ -51,6 +52,16 @@ TrikPythonRunner::~TrikPythonRunner()
connect(mWorkerThread, &QThread::finished, &wait, &QEventLoop::quit);
mScriptEngineWorker->stopScript();
mWorkerThread->quit();

// HACK: fix dead-lock in QThread::wait after QThread::quit
// Chaotic use of `processEvents' in code results in dead lock
// in the main thread event loop in the internal processEvents call.
// See commit message for details
if (auto *dispatcher = QAbstractEventDispatcher::instance(mWorkerThread)) {
connect(dispatcher, &QAbstractEventDispatcher::aboutToBlock
, dispatcher, &QAbstractEventDispatcher::interrupt);
}

// We need an event loop to process pending calls from dying thread to the current
// mWorkerThread.wait(); // <-- !!! blocks pending calls
wait.exec();
Expand Down

0 comments on commit 3bc464a

Please sign in to comment.