Qmake: Prevent unresponsive processes from hanging Qt Creator

That is, processes run via qmake's system() function.
Fun fact disovered while implementing this: "Canceling" the loading of a
qmake project did nothing at all except making the progress bar red at
the end of the parsing procedure. Now at least we terminate the
currently running processes invoked by system(), so the parsing threads
can continue and eventually finish.

Task-number: QTCREATORBUG-24825
Task-number: QTCREATORBUG-25000
Task-number: QTCREATORBUG-25194
Change-Id: Ic92ef200d3a49bbdeff0429ae7038fe3f1935b38
Reviewed-by: Joerg Bornemann <joerg.bornemann@qt.io>
This commit is contained in:
Christian Kandeler
2021-01-05 12:33:09 +01:00
parent 5b4e652b26
commit a30aa4421a
4 changed files with 44 additions and 2 deletions

View File

@@ -666,6 +666,18 @@ void QmakeBuildSystem::asyncUpdate()
Constants::PROFILE_EVALUATE); Constants::PROFILE_EVALUATE);
m_asyncUpdateFutureInterface.reportStarted(); m_asyncUpdateFutureInterface.reportStarted();
const auto watcher = new QFutureWatcher<void>(this);
connect(watcher, &QFutureWatcher<void>::canceled, this, [this, watcher] {
if (!m_qmakeGlobals)
return;
watcher->disconnect();
m_qmakeGlobals->killProcesses();
});
connect(watcher, &QFutureWatcher<void>::finished, this, [watcher] {
watcher->disconnect();
watcher->deleteLater();
});
watcher->setFuture(m_asyncUpdateFutureInterface.future());
const Kit *const k = kit(); const Kit *const k = kit();
QtSupport::BaseQtVersion *const qtVersion = QtSupport::QtKitAspect::qtVersion(k); QtSupport::BaseQtVersion *const qtVersion = QtSupport::QtKitAspect::qtVersion(k);

View File

@@ -473,13 +473,28 @@ void QMakeEvaluator::runProcess(QProcess *proc, const QString &command) const
proc->setProcessEnvironment(env); proc->setProcessEnvironment(env);
} }
# endif # endif
# ifdef PROEVALUATOR_THREAD_SAFE
m_option->mutex.lock();
if (m_option->canceled) {
m_option->mutex.unlock();
return;
}
m_option->runningProcs << proc;
# endif
# ifdef Q_OS_WIN # ifdef Q_OS_WIN
proc->setNativeArguments(QLatin1String("/v:off /s /c \"") + command + QLatin1Char('"')); proc->setNativeArguments(QLatin1String("/v:off /s /c \"") + command + QLatin1Char('"'));
proc->start(m_option->getEnv(QLatin1String("COMSPEC")), QStringList()); proc->start(m_option->getEnv(QLatin1String("COMSPEC")), QStringList());
# else # else
proc->start(QLatin1String("/bin/sh"), QStringList() << QLatin1String("-c") << command); proc->start(QLatin1String("/bin/sh"), QStringList() << QLatin1String("-c") << command);
# endif
# ifdef PROEVALUATOR_THREAD_SAFE
m_option->mutex.unlock();
# endif # endif
proc->waitForFinished(-1); proc->waitForFinished(-1);
# ifdef PROEVALUATOR_THREAD_SAFE
QMutexLocker(&m_option->mutex);
m_option->runningProcs.removeOne(proc);
# endif
} }
#endif #endif
@@ -490,7 +505,7 @@ QByteArray QMakeEvaluator::getCommandOutput(const QString &args, int *exitCode)
QProcess proc; QProcess proc;
runProcess(&proc, args); runProcess(&proc, args);
*exitCode = (proc.exitStatus() == QProcess::NormalExit) ? proc.exitCode() : -1; *exitCode = (proc.exitStatus() == QProcess::NormalExit) ? proc.exitCode() : -1;
QByteArray errout = proc.readAllStandardError(); QByteArray errout = proc.isReadable() ? proc.readAllStandardError() : QByteArray();
# ifdef PROEVALUATOR_FULL # ifdef PROEVALUATOR_FULL
// FIXME: Qt really should have the option to set forwarding per channel // FIXME: Qt really should have the option to set forwarding per channel
fputs(errout.constData(), stderr); fputs(errout.constData(), stderr);
@@ -503,7 +518,7 @@ QByteArray QMakeEvaluator::getCommandOutput(const QString &args, int *exitCode)
QString::fromLocal8Bit(errout)); QString::fromLocal8Bit(errout));
} }
# endif # endif
out = proc.readAllStandardOutput(); out = proc.isReadable() ? proc.readAllStandardOutput() : QByteArray();
# ifdef Q_OS_WIN # ifdef Q_OS_WIN
// FIXME: Qt's line end conversion on sequential files should really be fixed // FIXME: Qt's line end conversion on sequential files should really be fixed
out.replace("\r\n", "\n"); out.replace("\r\n", "\n");

View File

@@ -89,6 +89,17 @@ QMakeGlobals::~QMakeGlobals()
qDeleteAll(baseEnvs); qDeleteAll(baseEnvs);
} }
void QMakeGlobals::killProcesses()
{
#ifdef PROEVALUATOR_THREAD_SAFE
QMutexLocker lock(&mutex);
canceled = true;
for (QProcess * const proc : runningProcs)
proc->kill();
runningProcs.clear();
#endif
}
QString QMakeGlobals::cleanSpec(QMakeCmdLineParserState &state, const QString &spec) QString QMakeGlobals::cleanSpec(QMakeCmdLineParserState &state, const QString &spec)
{ {
QString ret = QDir::cleanPath(spec); QString ret = QDir::cleanPath(spec);

View File

@@ -112,6 +112,8 @@ public:
QString extra_cmds[4]; QString extra_cmds[4];
bool runSystemFunction = false; bool runSystemFunction = false;
void killProcesses();
#ifdef PROEVALUATOR_DEBUG #ifdef PROEVALUATOR_DEBUG
int debugLevel; int debugLevel;
#endif #endif
@@ -157,6 +159,8 @@ private:
#ifdef PROEVALUATOR_THREAD_SAFE #ifdef PROEVALUATOR_THREAD_SAFE
QMutex mutex; QMutex mutex;
bool canceled = false;
QList<QProcess *> runningProcs;
#endif #endif
QHash<QMakeBaseKey, QMakeBaseEnv *> baseEnvs; QHash<QMakeBaseKey, QMakeBaseEnv *> baseEnvs;