From e3b639047f6a4a9c042987062bafdbf1726267cb Mon Sep 17 00:00:00 2001 From: Eike Ziller Date: Tue, 29 Jun 2021 15:26:23 +0200 Subject: [PATCH] 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 Reviewed-by: Christian Kandeler --- .../cmakeprojectmanager/cmakebuildsystem.cpp | 6 +- src/plugins/cpptools/cppmodelmanager.cpp | 4 +- src/plugins/cpptools/cppmodelmanager.h | 3 +- src/plugins/cpptools/cppprojectupdater.cpp | 91 ++++++++++++++++++- src/plugins/cpptools/cppprojectupdater.h | 9 ++ src/plugins/projectexplorer/extracompiler.cpp | 18 ++-- src/plugins/projectexplorer/extracompiler.h | 8 +- src/plugins/qbsprojectmanager/qbsproject.cpp | 4 + .../qmakeprojectmanager/qmakeproject.cpp | 3 +- 9 files changed, 124 insertions(+), 22 deletions(-) diff --git a/src/plugins/cmakeprojectmanager/cmakebuildsystem.cpp b/src/plugins/cmakeprojectmanager/cmakebuildsystem.cpp index 2ec4e68e6c3..71fac0757cf 100644 --- a/src/plugins/cmakeprojectmanager/cmakebuildsystem.cpp +++ b/src/plugins/cmakeprojectmanager/cmakebuildsystem.cpp @@ -630,8 +630,7 @@ void CMakeBuildSystem::updateProjectData() { qDeleteAll(m_extraCompilers); m_extraCompilers = findExtraCompilers(); - CppTools::GeneratedCodeModelSupport::update(m_extraCompilers); - qCDebug(cmakeBuildSystemLog) << "Extra compilers updated."; + qCDebug(cmakeBuildSystemLog) << "Extra compilers created."; } QtSupport::CppKitInfo kitInfo(kit()); @@ -658,7 +657,8 @@ void CMakeBuildSystem::updateProjectData() } } - m_cppCodeModelUpdater->update({p, kitInfo, cmakeBuildConfiguration()->environment(), rpps}); + m_cppCodeModelUpdater->update({p, kitInfo, cmakeBuildConfiguration()->environment(), rpps}, + m_extraCompilers); } { const bool mergedHeaderPathsAndQmlImportPaths = kit()->value( diff --git a/src/plugins/cpptools/cppmodelmanager.cpp b/src/plugins/cpptools/cppmodelmanager.cpp index f92248b2228..2366b5d4daf 100644 --- a/src/plugins/cpptools/cppmodelmanager.cpp +++ b/src/plugins/cpptools/cppmodelmanager.cpp @@ -1145,7 +1145,8 @@ void CppModelManager::updateCppEditorDocuments(bool projectsUpdated) const } } -QFuture CppModelManager::updateProjectInfo(const ProjectInfo &newProjectInfo) +QFuture CppModelManager::updateProjectInfo(const ProjectInfo &newProjectInfo, + const QSet &additionalFiles) { if (!newProjectInfo.isValid()) return QFuture(); @@ -1236,6 +1237,7 @@ QFuture CppModelManager::updateProjectInfo(const ProjectInfo &newProjectIn // resolved includes that we could rely on. updateCppEditorDocuments(/*projectsUpdated = */ true); + filesToReindex.unite(additionalFiles); // Trigger reindexing const QFuture indexingFuture = updateSourceFiles(filesToReindex, ForcedProgressNotification); diff --git a/src/plugins/cpptools/cppmodelmanager.h b/src/plugins/cpptools/cppmodelmanager.h index 402f37bee6d..81e8ec3710b 100644 --- a/src/plugins/cpptools/cppmodelmanager.h +++ b/src/plugins/cpptools/cppmodelmanager.h @@ -116,7 +116,8 @@ public: QList projectInfos() const; ProjectInfo projectInfo(ProjectExplorer::Project *project) const; - QFuture updateProjectInfo(const ProjectInfo &newProjectInfo); + QFuture updateProjectInfo(const ProjectInfo &newProjectInfo, + const QSet &additionalFiles = {}); /// \return The project part with the given project file ProjectPart::Ptr projectPartForId(const QString &projectPartId) const override; diff --git a/src/plugins/cpptools/cppprojectupdater.cpp b/src/plugins/cpptools/cppprojectupdater.cpp index 794fd7abe7f..f34628af51a 100644 --- a/src/plugins/cpptools/cppprojectupdater.cpp +++ b/src/plugins/cpptools/cppprojectupdater.cpp @@ -27,14 +27,21 @@ #include "cppmodelmanager.h" #include "cppprojectinfogenerator.h" +#include "generatedcodemodelsupport.h" + +#include #include +#include +#include #include #include #include +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 &extraCompilers) { // Stop previous update. cancel(); + m_extraCompilers = Utils::transform(extraCompilers, [](ExtraCompiler *compiler) { + return QPointer(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 compiler : qAsConst(m_extraCompilers)) { + if (compiler->isDirty()) { + auto watcher = new QFutureWatcher; + // 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(compiler->run())); + m_futureSynchronizer.addFuture(watcher->future()); + } + } + + m_projectUpdateFutureInterface.reset(new QFutureInterface); + 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 extraCompilers; + QSet compilerFiles; + for (const QPointer &compiler : qAsConst(m_extraCompilers)) { + if (compiler) { + extraCompilers += compiler.data(); + compilerFiles += Utils::transform(compiler->targets(), &Utils::FilePath::toString); + } + } + GeneratedCodeModelSupport::update(extraCompilers); + m_extraCompilers.clear(); + + auto updateFuture = CppModelManager::instance() + ->updateProjectInfo(m_generateFutureWatcher.result(), compilerFiles); m_futureSynchronizer.addFuture(updateFuture); } diff --git a/src/plugins/cpptools/cppprojectupdater.h b/src/plugins/cpptools/cppprojectupdater.h index 6b994871ef9..c356ab63d19 100644 --- a/src/plugins/cpptools/cppprojectupdater.h +++ b/src/plugins/cpptools/cppprojectupdater.h @@ -29,6 +29,7 @@ #include "cpptools_global.h" #include "projectinfo.h" +#include #include #include @@ -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 &extraCompilers); void cancel() override; private: void onToolChainRemoved(ProjectExplorer::ToolChain *); void onProjectInfoGenerated(); + void checkForExtraCompilersFinished(); private: ProjectExplorer::ProjectUpdateInfo m_projectUpdateInfo; + QList> m_extraCompilers; QFutureWatcher m_generateFutureWatcher; + bool m_isProjectInfoGenerated = false; + QSet *> m_extraCompilersFutureWatchers; + std::unique_ptr> m_projectUpdateFutureInterface; Utils::FutureSynchronizer m_futureSynchronizer; }; diff --git a/src/plugins/projectexplorer/extracompiler.cpp b/src/plugins/projectexplorer/extracompiler.cpp index 6dd653774ec..893997de3fe 100644 --- a/src/plugins/projectexplorer/extracompiler.cpp +++ b/src/plugins/projectexplorer/extracompiler.cpp @@ -123,11 +123,6 @@ ExtraCompiler::ExtraCompiler(const Project *project, const Utils::FilePath &sour if (file.open(QFile::ReadOnly | QFile::Text)) setContent(target, file.readAll()); } - - if (d->dirty) { - d->dirty = false; - QTimer::singleShot(0, this, [this]() { run(d->source); }); // delay till available. - } } ExtraCompiler::~ExtraCompiler() = default; @@ -173,6 +168,11 @@ QThreadPool *ExtraCompiler::extraCompilerThreadPool() return s_extraCompilerThreadPool(); } +bool ExtraCompiler::isDirty() const +{ + return d->dirty; +} + void ExtraCompiler::onTargetsBuilt(Project *project) { if (project != d->project || BuildManager::isBuilding(project)) @@ -346,15 +346,16 @@ void ProcessExtraCompiler::run(const QByteArray &sourceContents) runImpl(contents); } -void ProcessExtraCompiler::run(const Utils::FilePath &fileName) +QFuture ProcessExtraCompiler::run() { + const Utils::FilePath fileName = source(); ContentProvider contents = [fileName]() { QFile file(fileName.toString()); if (!file.open(QFile::ReadOnly | QFile::Text)) return QByteArray(); return file.readAll(); }; - runImpl(contents); + return runImpl(contents); } Utils::FilePath ProcessExtraCompiler::workingDirectory() const @@ -379,7 +380,7 @@ Tasks ProcessExtraCompiler::parseIssues(const QByteArray &stdErr) return {}; } -void ProcessExtraCompiler::runImpl(const ContentProvider &provider) +QFuture ProcessExtraCompiler::runImpl(const ContentProvider &provider) { if (m_watcher) delete m_watcher; @@ -392,6 +393,7 @@ void ProcessExtraCompiler::runImpl(const ContentProvider &provider) &ProcessExtraCompiler::runInThread, this, command(), workingDirectory(), arguments(), provider, buildEnvironment())); + return m_watcher->future(); } void ProcessExtraCompiler::runInThread( diff --git a/src/plugins/projectexplorer/extracompiler.h b/src/plugins/projectexplorer/extracompiler.h index 5c0854a3b4b..e38a5900a0d 100644 --- a/src/plugins/projectexplorer/extracompiler.h +++ b/src/plugins/projectexplorer/extracompiler.h @@ -80,6 +80,9 @@ public: static QThreadPool *extraCompilerThreadPool(); + virtual QFuture run() = 0; + bool isDirty() const; + signals: void contentsChanged(const Utils::FilePath &file); @@ -94,7 +97,6 @@ private: void setDirty(); // This method may not block! virtual void run(const QByteArray &sourceContent) = 0; - virtual void run(const Utils::FilePath &file) = 0; const std::unique_ptr d; }; @@ -115,7 +117,7 @@ protected: // * prepareToRun returns true // * The process is not yet running void run(const QByteArray &sourceContents) override; - void run(const Utils::FilePath &fileName) override; + QFuture run() override; // Information about the process to run: virtual Utils::FilePath workingDirectory() const; @@ -133,7 +135,7 @@ protected: private: using ContentProvider = std::function; - void runImpl(const ContentProvider &sourceContents); + QFuture runImpl(const ContentProvider &sourceContents); void runInThread(QFutureInterface &futureInterface, const Utils::FilePath &cmd, const Utils::FilePath &workDir, const QStringList &args, const ContentProvider &provider, diff --git a/src/plugins/qbsprojectmanager/qbsproject.cpp b/src/plugins/qbsprojectmanager/qbsproject.cpp index dcf5552468d..1de06cedaf1 100644 --- a/src/plugins/qbsprojectmanager/qbsproject.cpp +++ b/src/plugins/qbsprojectmanager/qbsproject.cpp @@ -193,6 +193,10 @@ QbsBuildSystem::QbsBuildSystem(QbsBuildConfiguration *bc) } } CppTools::GeneratedCodeModelSupport::update(m_extraCompilers); + for (ExtraCompiler *compiler : m_extraCompilers) { + if (compiler->isDirty()) + compiler->run(); + } m_sourcesForGeneratedFiles.clear(); }); connect(m_session, &QbsSession::errorOccurred, this, [](QbsSession::Error e) { diff --git a/src/plugins/qmakeprojectmanager/qmakeproject.cpp b/src/plugins/qmakeprojectmanager/qmakeproject.cpp index a1ff1f21f11..acb1c14e1f4 100644 --- a/src/plugins/qmakeprojectmanager/qmakeproject.cpp +++ b/src/plugins/qmakeprojectmanager/qmakeproject.cpp @@ -399,8 +399,7 @@ void QmakeBuildSystem::updateCppCodeModel() rpps.append(rpp); } - CppTools::GeneratedCodeModelSupport::update(generators); - m_cppCodeModelUpdater->update({project(), kitInfo, activeParseEnvironment(), rpps}); + m_cppCodeModelUpdater->update({project(), kitInfo, activeParseEnvironment(), rpps}, generators); } void QmakeBuildSystem::updateQmlJSCodeModel()