Fix UI freeze while building with multiple cores / distributed build

When parallelizing the build, and a verbose compiler error/warning
is printed (i.e. a template overload error), the IDE wastes a lot
of time parsing always the same file patterns, and that freezes
completely the UI. The only workaround is to kill the build process
using a terminal.

Implement a file cache, thus mitigating the freeze issue.

Change-Id: Ibcbdb6e6161af7cef424e90f7cfdc2fc34f6d7c1
Reviewed-by: Tobias Hunger <tobias.hunger@qt.io>
Reviewed-by: Antonio Di Monaco <tony@becrux.com>
Reviewed-by: Orgad Shaneh <orgads@gmail.com>
This commit is contained in:
Antonio Di Monaco
2018-03-28 12:37:19 +02:00
parent a8eeb10630
commit 5784fce244
2 changed files with 60 additions and 14 deletions

View File

@@ -36,6 +36,13 @@
#include <QTimer> #include <QTimer>
#include <QDir> #include <QDir>
#include <algorithm>
namespace {
const int CACHE_SOFT_LIMIT = 500;
const int CACHE_HARD_LIMIT = 1000;
} // namespace
using namespace ProjectExplorer; using namespace ProjectExplorer;
/*! /*!
@@ -153,6 +160,12 @@ void AbstractProcessStep::setIgnoreReturnValue(bool b)
bool AbstractProcessStep::init(QList<const BuildStep *> &earlierSteps) bool AbstractProcessStep::init(QList<const BuildStep *> &earlierSteps)
{ {
Q_UNUSED(earlierSteps); Q_UNUSED(earlierSteps);
m_candidates.clear();
const Utils::FileNameList fl = project()->files(Project::AllFiles);
for (const Utils::FileName &file : fl)
m_candidates[file.fileName()].push_back(file);
return !m_process; return !m_process;
} }
@@ -365,42 +378,46 @@ void AbstractProcessStep::taskAdded(const Task &task, int linkedOutputLines, int
Task editable(task); Task editable(task);
QString filePath = task.file.toString(); QString filePath = task.file.toString();
if (!filePath.isEmpty() && !filePath.startsWith('<') && !QDir::isAbsolutePath(filePath)) {
auto it = m_filesCache.find(filePath);
if (it != m_filesCache.end()) {
editable.file = it.value().first;
it.value().second = ++m_cacheCounter;
} else if (!filePath.isEmpty() && !filePath.startsWith('<') && !QDir::isAbsolutePath(filePath)) {
// We have no save way to decide which file in which subfolder // We have no save way to decide which file in which subfolder
// is meant. Therefore we apply following heuristics: // is meant. Therefore we apply following heuristics:
// 1. Check if file is unique in whole project // 1. Check if file is unique in whole project
// 2. Otherwise try again without any ../ // 2. Otherwise try again without any ../
// 3. give up. // 3. give up.
QList<QFileInfo> possibleFiles; QString sourceFilePath = filePath;
QString fileName = Utils::FileName::fromString(filePath).fileName(); Utils::FileNameList possibleFiles = m_candidates.value(Utils::FileName::fromString(filePath).fileName());
foreach (const Utils::FileName &file, project()->files(Project::AllFiles)) {
QFileInfo candidate = file.toFileInfo();
if (candidate.fileName() == fileName)
possibleFiles << candidate;
}
if (possibleFiles.count() == 1) { if (possibleFiles.count() == 1) {
editable.file = Utils::FileName(possibleFiles.first()); editable.file = possibleFiles.first();
} else { } else {
// More then one filename, so do a better compare // More then one filename, so do a better compare
// Chop of any "../" // Chop of any "../"
while (filePath.startsWith("../")) while (filePath.startsWith("../"))
filePath.remove(0, 3); filePath.remove(0, 3);
int count = 0; int count = 0;
QString possibleFilePath; Utils::FileName possibleFilePath;
foreach (const QFileInfo &fi, possibleFiles) { foreach (const Utils::FileName &fn, possibleFiles) {
if (fi.filePath().endsWith(filePath)) { if (fn.endsWith(filePath)) {
possibleFilePath = fi.filePath(); possibleFilePath = fn;
++count; ++count;
} }
} }
if (count == 1) if (count == 1)
editable.file = Utils::FileName::fromString(possibleFilePath); editable.file = possibleFilePath;
else else
qWarning() << "Could not find absolute location of file " << filePath; qWarning() << "Could not find absolute location of file " << filePath;
} }
insertInCache(sourceFilePath, editable.file);
} }
emit addTask(editable, linkedOutputLines, skipLines); emit addTask(editable, linkedOutputLines, skipLines);
} }
@@ -425,5 +442,25 @@ void AbstractProcessStep::slotProcessFinished(int, QProcess::ExitStatus)
for (const QString &l : stdOutLine.split('\n')) for (const QString &l : stdOutLine.split('\n'))
stdError(l); stdError(l);
purgeCache(true);
cleanUp(process); cleanUp(process);
} }
void AbstractProcessStep::purgeCache(bool useSoftLimit)
{
const int limit = useSoftLimit ? CACHE_SOFT_LIMIT : CACHE_HARD_LIMIT;
if (m_filesCache.size() <= limit)
return;
const quint64 minCounter = m_cacheCounter - static_cast<quint64>(limit);
std::remove_if(m_filesCache.begin(), m_filesCache.end(),
[minCounter](const QPair<Utils::FileName, quint64> &entry) {
return entry.second <= minCounter;
});
}
void AbstractProcessStep::insertInCache(const QString &relativePath, const Utils::FileName &absPath)
{
purgeCache(false);
m_filesCache.insert(relativePath, qMakePair(absPath, ++m_cacheCounter));
}

View File

@@ -31,9 +31,12 @@
#include <projectexplorer/ioutputparser.h> #include <projectexplorer/ioutputparser.h>
#include <utils/qtcprocess.h> #include <utils/qtcprocess.h>
#include <utils/fileutils.h>
#include <QString> #include <QString>
#include <QTimer> #include <QTimer>
#include <QHash>
#include <QPair>
#include <memory> #include <memory>
@@ -87,11 +90,17 @@ private:
void outputAdded(const QString &string, BuildStep::OutputFormat format); void outputAdded(const QString &string, BuildStep::OutputFormat format);
void purgeCache(bool useSoftLimit);
void insertInCache(const QString &relativePath, const Utils::FileName &absPath);
QTimer m_timer; QTimer m_timer;
QFutureInterface<bool> *m_futureInterface = nullptr; QFutureInterface<bool> *m_futureInterface = nullptr;
std::unique_ptr<Utils::QtcProcess> m_process; std::unique_ptr<Utils::QtcProcess> m_process;
std::unique_ptr<IOutputParser> m_outputParserChain; std::unique_ptr<IOutputParser> m_outputParserChain;
ProcessParameters m_param; ProcessParameters m_param;
QHash<QString, QPair<Utils::FileName, quint64>> m_filesCache;
QHash<QString, Utils::FileNameList> m_candidates;
quint64 m_cacheCounter = 0;
bool m_ignoreReturnValue = false; bool m_ignoreReturnValue = false;
bool m_skipFlush = false; bool m_skipFlush = false;
}; };