Fix a deadlock when closing Creator while loading a project

Replace the call to SynchronousProcess::run() by asynchronous
call that invokes a process.

This change may be tested by applying the additional patch
mentioned in QTCREATORBUG-25385 description.

Don't call the SynchronousProcess::run() from the main thread when
the Quit event was already scheduled for qApp, since the Quit event
will get removed from the awaiting queue by a call to
QEventLoop::exec().

Fixes: QTCREATORBUG-25385
Change-Id: I8af39552443bfa9b3af6e31ddce85a01b91bbbd8
Reviewed-by: Eike Ziller <eike.ziller@qt.io>
This commit is contained in:
Jarek Kobus
2021-02-25 12:53:41 +01:00
parent 2de66272e8
commit 675a72e296
2 changed files with 58 additions and 32 deletions

View File

@@ -39,6 +39,7 @@
#include <android/androidconstants.h> #include <android/androidconstants.h>
#include <coreplugin/icore.h> #include <coreplugin/icore.h>
#include <coreplugin/progressmanager/progressmanager.h> #include <coreplugin/progressmanager/progressmanager.h>
#include <coreplugin/reaper.h>
#include <cpptools/cppprojectupdater.h> #include <cpptools/cppprojectupdater.h>
#include <cpptools/cpptoolsconstants.h> #include <cpptools/cpptoolsconstants.h>
#include <cpptools/generatedcodemodelsupport.h> #include <cpptools/generatedcodemodelsupport.h>
@@ -59,6 +60,7 @@
#include <utils/macroexpander.h> #include <utils/macroexpander.h>
#include <utils/mimetypes/mimetype.h> #include <utils/mimetypes/mimetype.h>
#include <utils/qtcassert.h> #include <utils/qtcassert.h>
#include <utils/runextensions.h>
#include <QClipboard> #include <QClipboard>
#include <QDir> #include <QDir>
@@ -207,6 +209,7 @@ CMakeBuildSystem::CMakeBuildSystem(CMakeBuildConfiguration *bc)
CMakeBuildSystem::~CMakeBuildSystem() CMakeBuildSystem::~CMakeBuildSystem()
{ {
m_futureSynchronizer.waitForFinished();
if (!m_treeScanner.isFinished()) { if (!m_treeScanner.isFinished()) {
auto future = m_treeScanner.future(); auto future = m_treeScanner.future();
future.cancel(); future.cancel();
@@ -576,7 +579,7 @@ void CMakeBuildSystem::combineScanAndParse()
emitBuildSystemUpdated(); emitBuildSystemUpdated();
QTimer::singleShot(0, this, &CMakeBuildSystem::runCTest); runCTest();
} }
void CMakeBuildSystem::checkAndReportError(QString &errorMessage) void CMakeBuildSystem::checkAndReportError(QString &errorMessage)
@@ -921,42 +924,62 @@ void CMakeBuildSystem::runCTest()
} }
qCDebug(cmakeBuildSystemLog) << "Requesting ctest run after cmake run"; qCDebug(cmakeBuildSystemLog) << "Requesting ctest run after cmake run";
BuildDirParameters parameters(cmakeBuildConfiguration()); const BuildDirParameters parameters(cmakeBuildConfiguration());
QTC_ASSERT(parameters.isValid(), return); QTC_ASSERT(parameters.isValid(), return);
FilePath workingDirectory = workDirectory(parameters); const CommandLine cmd { m_ctestPath, { "-N", "--show-only=json-v1" } };
CommandLine cmd{m_ctestPath, {"-N", "--show-only=json-v1"}}; const QString workingDirectory = workDirectory(parameters).toString();
SynchronousProcess ctest; const QStringList environment = cmakeBuildConfiguration()->environment().toStringList();
ctest.setTimeoutS(1);
ctest.setEnvironment(cmakeBuildConfiguration()->environment().toStringList());
ctest.setWorkingDirectory(workingDirectory.toString());
const SynchronousProcessResponse response = ctest.run(cmd); auto future = Utils::runAsync([cmd, workingDirectory, environment]
if (response.result == SynchronousProcessResponse::Finished) { (QFutureInterface<QByteArray> &futureInterface) {
const QJsonDocument json = QJsonDocument::fromJson(response.allRawOutput()); QProcess process;
if (!json.isEmpty() && json.isObject()) { process.setEnvironment(environment);
const QJsonObject jsonObj = json.object(); process.setWorkingDirectory(workingDirectory);
const QJsonObject btGraph = jsonObj.value("backtraceGraph").toObject(); process.start(cmd.executable().toString(), cmd.splitArguments(), QIODevice::ReadOnly);
const QJsonArray cmakelists = btGraph.value("files").toArray();
const QJsonArray nodes = btGraph.value("nodes").toArray(); if (!process.waitForStarted(1000) || !process.waitForFinished(1000)) {
const QJsonArray tests = jsonObj.value("tests").toArray(); if (process.state() == QProcess::NotRunning)
for (const QJsonValue &testVal : tests) { return;
const QJsonObject test = testVal.toObject(); process.terminate();
QTC_ASSERT(!test.isEmpty(), continue); if (process.waitForFinished(1000))
const int bt = test.value("backtrace").toInt(-1); return;
QTC_ASSERT(bt != -1, continue); process.kill();
const QJsonObject btRef = nodes.at(bt).toObject(); process.waitForFinished(1000);
int file = btRef.value("file").toInt(-1); return;
int line = btRef.value("line").toInt(-1); }
QTC_ASSERT(file != -1 && line != -1, continue); if (process.exitCode() || process.exitStatus() != QProcess::NormalExit)
m_testNames.append({test.value("name").toString(), return;
FilePath::fromString(cmakelists.at(file).toString()), futureInterface.reportResult(process.readAllStandardOutput());
line });
});
Utils::onFinished(future, this, [this](const QFuture<QByteArray> &future) {
if (future.resultCount()) {
const QJsonDocument json = QJsonDocument::fromJson(future.result());
if (!json.isEmpty() && json.isObject()) {
const QJsonObject jsonObj = json.object();
const QJsonObject btGraph = jsonObj.value("backtraceGraph").toObject();
const QJsonArray cmakelists = btGraph.value("files").toArray();
const QJsonArray nodes = btGraph.value("nodes").toArray();
const QJsonArray tests = jsonObj.value("tests").toArray();
for (const QJsonValue &testVal : tests) {
const QJsonObject test = testVal.toObject();
QTC_ASSERT(!test.isEmpty(), continue);
const int bt = test.value("backtrace").toInt(-1);
QTC_ASSERT(bt != -1, continue);
const QJsonObject btRef = nodes.at(bt).toObject();
int file = btRef.value("file").toInt(-1);
int line = btRef.value("line").toInt(-1);
QTC_ASSERT(file != -1 && line != -1, continue);
m_testNames.append({ test.value("name").toString(),
FilePath::fromString(cmakelists.at(file).toString()), line });
}
} }
} }
} emit testInformationUpdated();
emit testInformationUpdated(); });
m_futureSynchronizer.addFuture(future);
} }
CMakeBuildConfiguration *CMakeBuildSystem::cmakeBuildConfiguration() const CMakeBuildConfiguration *CMakeBuildSystem::cmakeBuildConfiguration() const

View File

@@ -35,6 +35,8 @@
#include <utils/fileutils.h> #include <utils/fileutils.h>
#include <utils/temporarydirectory.h> #include <utils/temporarydirectory.h>
#include <QFutureSynchronizer>
namespace ProjectExplorer { class ExtraCompiler; } namespace ProjectExplorer { class ExtraCompiler; }
namespace CppTools { namespace CppTools {
@@ -181,6 +183,7 @@ private:
// CTest integration // CTest integration
QString m_ctestPath; QString m_ctestPath;
QList<ProjectExplorer::TestCaseInfo> m_testNames; QList<ProjectExplorer::TestCaseInfo> m_testNames;
QFutureSynchronizer<QByteArray> m_futureSynchronizer;
}; };
} // namespace Internal } // namespace Internal