Don't update extra compilers individually after project load

Each call of CppModelManager::updateSourceFiles detaches the current
snapshot.
The extra compilers where set up and triggered individually, and
resulted in individual updateSourceFiles calls with the single result
file of the extra compiler. For Qt Creator this would lead to 200
calls in quick succession after project load, potentially leading to a
freeze of multiple seconds.

Instead of updating the result files of the extra compilers individually
after project load, integrate the update into the regular project source
file update. So we end up with only a single call of updateSourceFiles.
For this the project updater needs to trigger the extra compilers, and
wait for all to finish as well as the regular project part update,
before
triggering the parser.

Task-number: QTCREATORBUG-25783
Change-Id: I34f6df0fc0f96bcb42ee65019bee39cf49176c1f
Reviewed-by: Jarek Kobus <jaroslaw.kobus@qt.io>
Reviewed-by: Christian Kandeler <christian.kandeler@qt.io>
This commit is contained in:
Eike Ziller
2021-06-29 15:26:23 +02:00
parent 68846a7729
commit e3b639047f
9 changed files with 124 additions and 22 deletions

View File

@@ -1145,7 +1145,8 @@ void CppModelManager::updateCppEditorDocuments(bool projectsUpdated) const
}
}
QFuture<void> CppModelManager::updateProjectInfo(const ProjectInfo &newProjectInfo)
QFuture<void> CppModelManager::updateProjectInfo(const ProjectInfo &newProjectInfo,
const QSet<QString> &additionalFiles)
{
if (!newProjectInfo.isValid())
return QFuture<void>();
@@ -1236,6 +1237,7 @@ QFuture<void> CppModelManager::updateProjectInfo(const ProjectInfo &newProjectIn
// resolved includes that we could rely on.
updateCppEditorDocuments(/*projectsUpdated = */ true);
filesToReindex.unite(additionalFiles);
// Trigger reindexing
const QFuture<void> indexingFuture = updateSourceFiles(filesToReindex,
ForcedProgressNotification);

View File

@@ -116,7 +116,8 @@ public:
QList<ProjectInfo> projectInfos() const;
ProjectInfo projectInfo(ProjectExplorer::Project *project) const;
QFuture<void> updateProjectInfo(const ProjectInfo &newProjectInfo);
QFuture<void> updateProjectInfo(const ProjectInfo &newProjectInfo,
const QSet<QString> &additionalFiles = {});
/// \return The project part with the given project file
ProjectPart::Ptr projectPartForId(const QString &projectPartId) const override;

View File

@@ -27,14 +27,21 @@
#include "cppmodelmanager.h"
#include "cppprojectinfogenerator.h"
#include "generatedcodemodelsupport.h"
#include <coreplugin/progressmanager/progressmanager.h>
#include <projectexplorer/toolchainmanager.h>
#include <utils/algorithm.h>
#include <utils/fileutils.h>
#include <utils/qtcassert.h>
#include <utils/runextensions.h>
#include <QFutureInterface>
using namespace ProjectExplorer;
namespace CppTools {
CppProjectUpdater::CppProjectUpdater()
@@ -46,11 +53,25 @@ CppProjectUpdater::CppProjectUpdater()
m_futureSynchronizer.setCancelOnWait(true);
}
void CppProjectUpdater::update(const ProjectExplorer::ProjectUpdateInfo &projectUpdateInfo)
CppProjectUpdater::~CppProjectUpdater()
{
cancel();
}
void CppProjectUpdater::update(const ProjectUpdateInfo &projectUpdateInfo)
{
update(projectUpdateInfo, {});
}
void CppProjectUpdater::update(const ProjectUpdateInfo &projectUpdateInfo,
const QList<ProjectExplorer::ExtraCompiler *> &extraCompilers)
{
// Stop previous update.
cancel();
m_extraCompilers = Utils::transform(extraCompilers, [](ExtraCompiler *compiler) {
return QPointer<ExtraCompiler>(compiler);
});
m_projectUpdateInfo = projectUpdateInfo;
// Ensure that we do not operate on a deleted toolchain.
@@ -68,15 +89,52 @@ void CppProjectUpdater::update(const ProjectExplorer::ProjectUpdateInfo &project
});
m_generateFutureWatcher.setFuture(generateFuture);
m_futureSynchronizer.addFuture(generateFuture);
// extra compilers
for (QPointer<ExtraCompiler> compiler : qAsConst(m_extraCompilers)) {
if (compiler->isDirty()) {
auto watcher = new QFutureWatcher<void>;
// queued connection to delay after the extra compiler updated its result contents,
// which is also done in the main thread when compiler->run() finished
connect(watcher, &QFutureWatcherBase::finished,
this, [this, watcher] {
m_projectUpdateFutureInterface->setProgressValue(
m_projectUpdateFutureInterface->progressValue() + 1);
m_extraCompilersFutureWatchers.remove(watcher);
watcher->deleteLater();
if (!watcher->isCanceled())
checkForExtraCompilersFinished();
},
Qt::QueuedConnection);
m_extraCompilersFutureWatchers += watcher;
watcher->setFuture(QFuture<void>(compiler->run()));
m_futureSynchronizer.addFuture(watcher->future());
}
}
m_projectUpdateFutureInterface.reset(new QFutureInterface<void>);
m_projectUpdateFutureInterface->setProgressRange(0, m_extraCompilersFutureWatchers.size()
+ 1 /*generateFuture*/);
m_projectUpdateFutureInterface->setProgressValue(0);
m_projectUpdateFutureInterface->reportStarted();
Core::ProgressManager::addTask(m_projectUpdateFutureInterface->future(),
tr("Preparing C++ Code Model"),
"CppProjectUpdater");
}
void CppProjectUpdater::cancel()
{
if (m_projectUpdateFutureInterface && m_projectUpdateFutureInterface->isRunning())
m_projectUpdateFutureInterface->reportFinished();
m_generateFutureWatcher.setFuture({});
m_isProjectInfoGenerated = false;
qDeleteAll(m_extraCompilersFutureWatchers);
m_extraCompilersFutureWatchers.clear();
m_extraCompilers.clear();
m_futureSynchronizer.cancelAllFutures();
}
void CppProjectUpdater::onToolChainRemoved(ProjectExplorer::ToolChain *t)
void CppProjectUpdater::onToolChainRemoved(ToolChain *t)
{
QTC_ASSERT(t, return);
if (t == m_projectUpdateInfo.cToolChain || t == m_projectUpdateInfo.cxxToolChain)
@@ -93,8 +151,33 @@ void CppProjectUpdater::onProjectInfoGenerated()
if (m_generateFutureWatcher.isCanceled() || m_generateFutureWatcher.future().resultCount() < 1)
return;
auto updateFuture = CppModelManager::instance()->updateProjectInfo(
m_generateFutureWatcher.result());
m_projectUpdateFutureInterface->setProgressValue(m_projectUpdateFutureInterface->progressValue()
+ 1);
m_isProjectInfoGenerated = true;
checkForExtraCompilersFinished();
}
void CppProjectUpdater::checkForExtraCompilersFinished()
{
if (!m_extraCompilersFutureWatchers.isEmpty() || !m_isProjectInfoGenerated)
return; // still need to wait
m_projectUpdateFutureInterface->reportFinished();
m_projectUpdateFutureInterface.reset();
QList<ExtraCompiler *> extraCompilers;
QSet<QString> compilerFiles;
for (const QPointer<ExtraCompiler> &compiler : qAsConst(m_extraCompilers)) {
if (compiler) {
extraCompilers += compiler.data();
compilerFiles += Utils::transform<QSet>(compiler->targets(), &Utils::FilePath::toString);
}
}
GeneratedCodeModelSupport::update(extraCompilers);
m_extraCompilers.clear();
auto updateFuture = CppModelManager::instance()
->updateProjectInfo(m_generateFutureWatcher.result(), compilerFiles);
m_futureSynchronizer.addFuture(updateFuture);
}

View File

@@ -29,6 +29,7 @@
#include "cpptools_global.h"
#include "projectinfo.h"
#include <projectexplorer/extracompiler.h>
#include <utils/futuresynchronizer.h>
#include <QFutureWatcher>
@@ -54,18 +55,26 @@ class CPPTOOLS_EXPORT CppProjectUpdater final : public QObject, public CppProjec
public:
CppProjectUpdater();
~CppProjectUpdater() override;
void update(const ProjectExplorer::ProjectUpdateInfo &projectUpdateInfo) override;
void update(const ProjectExplorer::ProjectUpdateInfo &projectUpdateInfo,
const QList<ProjectExplorer::ExtraCompiler *> &extraCompilers);
void cancel() override;
private:
void onToolChainRemoved(ProjectExplorer::ToolChain *);
void onProjectInfoGenerated();
void checkForExtraCompilersFinished();
private:
ProjectExplorer::ProjectUpdateInfo m_projectUpdateInfo;
QList<QPointer<ProjectExplorer::ExtraCompiler>> m_extraCompilers;
QFutureWatcher<ProjectInfo> m_generateFutureWatcher;
bool m_isProjectInfoGenerated = false;
QSet<QFutureWatcher<void> *> m_extraCompilersFutureWatchers;
std::unique_ptr<QFutureInterface<void>> m_projectUpdateFutureInterface;
Utils::FutureSynchronizer m_futureSynchronizer;
};