Details
-
Bug
-
Resolution: Done
-
P1: Critical
-
Qt Creator 4.15.0-beta1
-
None
-
675a72e29648844eca625202ee56da9280b6ac22 (qt-creator/qt-creator/4.15)
Description
If Creator is being shut down just after session switch, and a new session contains a big project (like creator), the Creator may deadlock. All the windows are being closed, but Creator doesn't finish.
It looks like sometimes QCoreApplication::aboutToClose() signal is not sent when it's being expected. When attaching debugger to the still running Creator, it shows that QCoreApplication's event loop is not finished and is still processing events. That's why the aboutToClose() signal is not emitted.
OTOH, MainWindow::closeEvent() was executed, and the passed event was accepted.
So it looks like something is preventing the QCoreApplication's event loop from stopping execution.
Update:
After the MainWindow::closeEvent() is executed, the Qt posts QEvent::Quit to the QCoreApplication object. As long as this event is awaiting in queue to be processed (which ultimately cause aboutToClose() signal emission) there shouldn't be any calls to QCoreApplication::removePostedEvents(qApp, 0) (or alternatively QCoreApplication::removePostedEvents(qApp, QEvent::Quit)), otherwise the quit event will get removed.
The implementation of QEventLoop::exec() contains:
.... // remove posted quit events when entering a new event loop QCoreApplication *app = QCoreApplication::instance(); if (app && app->thread() == thread()) QCoreApplication::removePostedEvents(app, QEvent::Quit); ....
This means, that if in meantime someone will execute a new event loop in the main thread, we may hit the mentioned deadlock.
In fact it's the case. I've traced, that after leaving MainWindow::closeEvent() and before the Quit event is processed, we are calling a QEventLoop::exec() in main thread, probably as a result of processing pending events.
This is done from: void CMakeBuildSystem::runCTest():
const SynchronousProcessResponse response = ctest.run(cmd);
which calls in turn SynchronousProcess::run():
d->m_eventLoop.exec(QEventLoop::ExcludeUserInputEvents);
This explains the reached deadlock.
So, the order of execution in main thread is as follows:
1. void CMakeBuildSystem::combineScanAndParse(), which queues a call to &CMakeBuildSystem::runCTest
2. MainWindow::closeEvent(), which queues the QEvent::Quit
3. The first in queue was CMakeBuildSystem::runCTest, so it is called now, which erases the QEvent::Quit from QCoreApplication awaiting events queue.
4. We are in deadlock.
Update on how to reproduce it in 100%:
Apply the following patch, which introduce a 1 second sleep in the right point in time:
diff --git a/src/plugins/cmakeprojectmanager/cmakebuildsystem.cpp b/src/plugins/cmakeprojectmanager/cmakebuildsystem.cpp index abd515f1a0..07086084cc 100644 --- a/src/plugins/cmakeprojectmanager/cmakebuildsystem.cpp +++ b/src/plugins/cmakeprojectmanager/cmakebuildsystem.cpp @@ -70,6 +70,8 @@ #include <QPushButton> #include <QRegularExpression> +#include <QThread> + using namespace ProjectExplorer; using namespace Utils; @@ -555,6 +557,9 @@ void CMakeBuildSystem::combineScanAndParse() emitBuildSystemUpdated(); + qDebug() << "Scheduling CMakeBuildSystem::runCTest()"; + qApp->thread()->sleep(1); + QTimer::singleShot(0, this, &CMakeBuildSystem::runCTest); } @@ -893,6 +898,7 @@ int CMakeBuildSystem::takeReparseParameters() void CMakeBuildSystem::runCTest() { + qDebug() << "Executing CMakeBuildSystem::runCTest()"; if (!cmakeBuildConfiguration()->error().isEmpty() || m_ctestPath.isEmpty()) { qCDebug(cmakeBuildSystemLog) << "Cancel ctest run after failed cmake run"; emit testInformationUpdated();
Steps to reproduce (with this patch applied):
1. Open a session
2. While the session is being loaded observe the debug output of Creator you are running. When you notice a debug printout "Scheduling CMakeBuildSystem::runCTest()" quickly close the creator (you have only 1 second for it). Now you are in deadlock