ProjectExplorer: Use RAII pattern for parsing start/stop signalling

Change-Id: I13de537140f265db3e3d0ab1cd924d6897cd90c8
Reviewed-by: Christian Kandeler <christian.kandeler@qt.io>
This commit is contained in:
Tobias Hunger
2019-08-06 14:46:37 +02:00
parent 8868989d5c
commit 09530d6dcc
18 changed files with 143 additions and 69 deletions

View File

@@ -112,7 +112,6 @@ Project::RestoreResult AutotoolsProject::fromMap(const QVariantMap &map, QString
void AutotoolsProject::loadProjectTree()
{
emitParsingStarted();
if (m_makefileParserThread) {
// The thread is still busy parsing a previus configuration.
// Wait until the thread has been finished and delete it.
@@ -125,7 +124,8 @@ void AutotoolsProject::loadProjectTree()
}
// Parse the makefile asynchronously in a thread
m_makefileParserThread = new MakefileParserThread(projectFilePath().toString());
m_makefileParserThread = new MakefileParserThread(projectFilePath().toString(),
guardParsingRun());
connect(m_makefileParserThread, &MakefileParserThread::started,
this, &AutotoolsProject::makefileParsingStarted);
@@ -212,8 +212,6 @@ void AutotoolsProject::makefileParsingFinished()
m_makefileParserThread->deleteLater();
m_makefileParserThread = nullptr;
emitParsingFinished(true);
}
void AutotoolsProject::onFileChanged(const QString &file)

View File

@@ -31,7 +31,10 @@
using namespace AutotoolsProjectManager::Internal;
MakefileParserThread::MakefileParserThread(const QString &makefile) : m_parser(makefile)
MakefileParserThread::MakefileParserThread(const QString &makefile,
ProjectExplorer::Project::ParseGuard &&guard)
: m_parser(makefile)
, m_guard(std::move(guard))
{
connect(&m_parser, &MakefileParser::status,
this, &MakefileParserThread::status);
@@ -82,7 +85,7 @@ QStringList MakefileParserThread::cxxflags() const
bool MakefileParserThread::hasError() const
{
QMutexLocker locker(&m_mutex);
return m_hasError;
return !m_guard.isSuccess();
}
bool MakefileParserThread::isCanceled() const
@@ -104,7 +107,8 @@ void MakefileParserThread::run()
// this prevents long locks if the caller reads a value before the signal
// finished() has been emitted.
QMutexLocker locker(&m_mutex);
m_hasError = !success;
if (success)
m_guard.markAsSuccess();
m_executable = m_parser.executable();
m_sources = m_parser.sources();
m_makefiles = m_parser.makefiles();

View File

@@ -29,6 +29,7 @@
#include "makefileparser.h"
#include <projectexplorer/project.h>
#include <projectexplorer/projectmacro.h>
#include <QMutex>
@@ -53,7 +54,7 @@ class MakefileParserThread : public QThread
using Macros = ProjectExplorer::Macros;
public:
MakefileParserThread(const QString &makefile);
MakefileParserThread(const QString &makefile, ProjectExplorer::Project::ParseGuard &&guard);
/** @see QThread::run() */
void run() override;
@@ -134,7 +135,6 @@ private:
MakefileParser m_parser; ///< Is not accessible outside the thread
mutable QMutex m_mutex;
bool m_hasError = false; ///< Return value for MakefileParserThread::hasError()
QString m_executable; ///< Return value for MakefileParserThread::executable()
QStringList m_sources; ///< Return value for MakefileParserThread::sources()
QStringList m_makefiles; ///< Return value for MakefileParserThread::makefiles()
@@ -142,6 +142,8 @@ private:
Macros m_macros; ///< Return value for MakefileParserThread::macros()
QStringList m_cflags; ///< Return value for MakefileParserThread::cflags()
QStringList m_cxxflags; ///< Return value for MakefileParserThread::cxxflags()
ProjectExplorer::Project::ParseGuard m_guard;
};
} // namespace Internal

View File

@@ -371,7 +371,7 @@ void CMakeProject::startParsing(int reparseParameters)
CMakeBuildConfiguration *bc = activeBc(this);
QTC_ASSERT(bc, return );
emitParsingStarted();
m_parseGuard = std::move(guardParsingRun());
m_waitingForScan = reparseParameters & BuildDirManager::REPARSE_SCAN;
m_waitingForParse = true;
@@ -466,12 +466,14 @@ void CMakeProject::combineScanAndParse(CMakeBuildConfiguration *bc)
if (m_waitingForParse || m_waitingForScan)
return;
if (m_combinedScanAndParseResult)
if (m_combinedScanAndParseResult) {
m_parseGuard.markAsSuccess();
updateProjectData(bc);
}
{
TraceTimer parsingDoneTimer(" parsing finished signal");
emitParsingFinished(m_combinedScanAndParseResult);
m_parseGuard = {};
}
}

View File

@@ -121,6 +121,8 @@ private:
QTimer m_delayedParsingTimer;
int m_delayedParsingParameters = 0;
ParseGuard m_parseGuard;
friend class Internal::CMakeBuildConfiguration;
friend class Internal::CMakeBuildSettingsWidget;
};

View File

@@ -450,17 +450,18 @@ void CompilationDatabaseProject::reparseProject()
if (m_parser) {
QTC_CHECK(isParsing());
m_parser->stop();
emitParsingFinished(false);
}
m_parser = new CompilationDbParser(displayName(), projectFilePath(), rootPathFromSettings(),
m_mimeBinaryCache, this);
m_parser = new CompilationDbParser(displayName(),
projectFilePath(),
rootPathFromSettings(),
m_mimeBinaryCache,
guardParsingRun(),
this);
connect(m_parser, &CompilationDbParser::finished, this, [this](bool success) {
if (success)
buildTreeAndProjectParts();
m_parser = nullptr;
emitParsingFinished(success);
});
emitParsingStarted();
m_parser->start();
}

View File

@@ -43,14 +43,18 @@ using namespace Utils;
namespace CompilationDatabaseProjectManager {
namespace Internal {
CompilationDbParser::CompilationDbParser(const QString &projectName, const FilePath &projectPath,
const FilePath &rootPath, MimeBinaryCache &mimeBinaryCache,
CompilationDbParser::CompilationDbParser(const QString &projectName,
const FilePath &projectPath,
const FilePath &rootPath,
MimeBinaryCache &mimeBinaryCache,
ProjectExplorer::Project::ParseGuard &&guard,
QObject *parent)
: QObject(parent),
m_projectName(projectName),
m_projectFilePath(projectPath),
m_rootPath(rootPath),
m_mimeBinaryCache(mimeBinaryCache)
: QObject(parent)
, m_projectName(projectName)
, m_projectFilePath(projectPath)
, m_rootPath(rootPath)
, m_mimeBinaryCache(mimeBinaryCache)
, m_guard(std::move(guard))
{
connect(&m_parserWatcher, &QFutureWatcher<void>::finished, this, [this] {
m_dbContents = m_parserWatcher.result();
@@ -112,6 +116,7 @@ void CompilationDbParser::stop()
m_treeScanner->disconnect();
m_treeScanner->future().cancel();
}
m_guard = {};
deleteLater();
}

View File

@@ -27,6 +27,8 @@
#include "compilationdatabaseutils.h"
#include <projectexplorer/project.h>
#include <utils/fileutils.h>
#include <QFutureWatcher>
@@ -48,15 +50,22 @@ class CompilationDbParser : public QObject
{
Q_OBJECT
public:
explicit CompilationDbParser(const QString &projectName, const Utils::FilePath &projectPath,
const Utils::FilePath &rootPath, MimeBinaryCache &mimeBinaryCache,
explicit CompilationDbParser(const QString &projectName,
const Utils::FilePath &projectPath,
const Utils::FilePath &rootPath,
MimeBinaryCache &mimeBinaryCache,
ProjectExplorer::Project::ParseGuard &&guard,
QObject *parent = nullptr);
void start();
void stop();
QList<ProjectExplorer::FileNode *> scannedFiles() const;
DbContents dbContents() const { return m_dbContents; }
DbContents dbContents() const
{
m_guard.markAsSuccess();
return m_dbContents;
}
signals:
void finished(bool success);
@@ -72,6 +81,8 @@ private:
ProjectExplorer::TreeScanner *m_treeScanner = nullptr;
QFutureWatcher<DbContents> m_parserWatcher;
DbContents m_dbContents;
ProjectExplorer::Project::ParseGuard m_guard;
};
} // namespace Internal

View File

@@ -409,7 +409,7 @@ FilePath GenericProject::findCommonSourceRoot()
void GenericProject::refresh(RefreshOptions options)
{
emitParsingStarted();
ParseGuard guard = guardParsingRun();
parseProject(options);
if (options & Files) {
@@ -442,7 +442,7 @@ void GenericProject::refresh(RefreshOptions options)
refreshCppCodeModel();
updateDeploymentData();
emitParsingFinished(true);
guard.markAsSuccess();
}
/**

View File

@@ -135,7 +135,7 @@ void NimProject::collectProjectFiles()
void NimProject::updateProject()
{
emitParsingStarted();
ParseGuard guard = guardParsingRun();
auto newRoot = std::make_unique<NimProjectNode>(*this, projectDirectory());
@@ -147,7 +147,7 @@ void NimProject::updateProject()
newRoot->setDisplayName(displayName());
setRootProjectNode(std::move(newRoot));
emitParsingFinished(true);
guard.markAsSuccess();
}
Tasks NimProject::projectIssues(const Kit *k) const

View File

@@ -1014,15 +1014,6 @@ public:
setDisplayName(TEST_PROJECT_DISPLAYNAME);
}
void testStartParsing()
{
emitParsingStarted();
}
void testParsingFinished(bool success) {
emitParsingFinished(success);
}
bool needsConfiguration() const final { return false; }
};
@@ -1081,14 +1072,17 @@ void ProjectExplorerPlugin::testProject_parsingSuccess()
QSignalSpy startSpy(&project, &Project::parsingStarted);
QSignalSpy stopSpy(&project, &Project::parsingFinished);
project.testStartParsing();
QCOMPARE(startSpy.count(), 1);
QCOMPARE(stopSpy.count(), 0);
{
Project::ParseGuard guard = project.guardParsingRun();
QCOMPARE(startSpy.count(), 1);
QCOMPARE(stopSpy.count(), 0);
QVERIFY(project.isParsing());
QVERIFY(!project.hasParsingData());
QVERIFY(project.isParsing());
QVERIFY(!project.hasParsingData());
guard.markAsSuccess();
}
project.testParsingFinished(true);
QCOMPARE(startSpy.count(), 1);
QCOMPARE(stopSpy.count(), 1);
QCOMPARE(stopSpy.at(0), {QVariant(true)});
@@ -1104,14 +1098,15 @@ void ProjectExplorerPlugin::testProject_parsingFail()
QSignalSpy startSpy(&project, &Project::parsingStarted);
QSignalSpy stopSpy(&project, &Project::parsingFinished);
project.testStartParsing();
QCOMPARE(startSpy.count(), 1);
QCOMPARE(stopSpy.count(), 0);
{
Project::ParseGuard guard = project.guardParsingRun();
QCOMPARE(startSpy.count(), 1);
QCOMPARE(stopSpy.count(), 0);
QVERIFY(project.isParsing());
QVERIFY(!project.hasParsingData());
QVERIFY(project.isParsing());
QVERIFY(!project.hasParsingData());
}
project.testParsingFinished(false);
QCOMPARE(startSpy.count(), 1);
QCOMPARE(stopSpy.count(), 1);
QCOMPARE(stopSpy.at(0), {QVariant(false)});

View File

@@ -204,6 +204,51 @@ public:
bool needsInitialExpansion() const;
void setNeedsInitialExpansion(bool needsInitialExpansion);
class ParseGuard
{
public:
ParseGuard()
: ParseGuard(nullptr)
{}
~ParseGuard()
{
if (m_project)
m_project->emitParsingFinished(m_success);
}
void markAsSuccess() const { m_success = true; }
bool isSuccess() const { return m_success; }
bool isNull() const { return !m_project; }
ParseGuard(const ParseGuard &other) = delete;
ParseGuard &operator=(const ParseGuard &other) = delete;
ParseGuard(ParseGuard &&other)
{
std::swap(m_project, other.m_project);
std::swap(m_success, other.m_success);
}
ParseGuard &operator=(ParseGuard &&other)
{
std::swap(m_project, other.m_project);
std::swap(m_success, other.m_success);
return *this;
}
private:
ParseGuard(Project *p)
: m_project(p)
{
if (m_project)
m_project->emitParsingStarted();
}
Project *m_project = nullptr;
mutable bool m_success = false;
friend class Project;
};
signals:
void displayNameChanged();
void fileListChanged();
@@ -235,16 +280,12 @@ signals:
void rootProjectDirectoryChanged();
protected:
ParseGuard guardParsingRun() { return ParseGuard(this); }
virtual RestoreResult fromMap(const QVariantMap &map, QString *errorMessage);
void createTargetFromMap(const QVariantMap &map, int index);
virtual bool setupTarget(Target *t);
// Helper methods to manage parsing state and signalling
// Call in GUI thread before the actual parsing starts
void emitParsingStarted();
// Call in GUI thread right after the actual parsing is done
void emitParsingFinished(bool success);
void setDisplayName(const QString &name);
// Used to pre-check kits in the TargetSetupPage. RequiredKitPredicate
// is used to select kits available in the TargetSetupPage
@@ -270,6 +311,12 @@ protected:
Utils::Environment activeBuildEnvironment() const;
private:
// Helper methods to manage parsing state and signalling
// Call in GUI thread before the actual parsing starts
void emitParsingStarted();
// Call in GUI thread right after the actual parsing is done
void emitParsingFinished(bool success);
void addTarget(std::unique_ptr<Target> &&target);
void handleSubTreeChanged(FolderNode *node);

View File

@@ -634,7 +634,7 @@ private:
void PythonProject::refresh(Target *target)
{
emitParsingStarted();
ParseGuard guard = guardParsingRun();
parseProject();
const QDir baseDir(projectDirectory().toString());
@@ -661,7 +661,7 @@ void PythonProject::refresh(Target *target)
if (target)
target->setApplicationTargets(appTargets);
emitParsingFinished(true);
guard.markAsSuccess();
}
/**

View File

@@ -463,7 +463,7 @@ bool QbsProject::checkCancelStatus()
qCDebug(qbsPmLog) << "Cancel request while parsing, starting re-parse";
m_qbsProjectParser->deleteLater();
m_qbsProjectParser = nullptr;
emitParsingFinished(false);
m_guard = {};
parseCurrentBuildConfiguration();
return true;
}
@@ -551,7 +551,8 @@ void QbsProject::handleQbsParsingDone(bool success)
updateAfterParse();
else if (envChanged)
updateCppCodeModel();
emitParsingFinished(success);
m_guard.markAsSuccess();
m_guard = {};
}
void QbsProject::rebuildProjectTree()
@@ -729,6 +730,8 @@ void QbsProject::configureAsExampleProject()
void QbsProject::parse(const QVariantMap &config, const Environment &env, const QString &dir,
const QString &configName)
{
m_guard = guardParsingRun();
prepareForParsing();
QTC_ASSERT(!m_qbsProjectParser, return);
@@ -736,7 +739,6 @@ void QbsProject::parse(const QVariantMap &config, const Environment &env, const
QbsManager::updateProfileIfNecessary(activeTarget()->kit());
m_qbsProjectParser->parse(config, env, dir, configName);
emitParsingStarted();
}
void QbsProject::prepareForParsing()

View File

@@ -167,6 +167,8 @@ private:
bool m_extraCompilersPending = false;
QHash<QString, Utils::Environment> m_envCache;
ParseGuard m_guard;
};
} // namespace Internal

View File

@@ -438,15 +438,15 @@ void QmakeProject::startAsyncTimer(QmakeProFile::AsyncUpdateDelay delay)
m_asyncUpdateTimer.stop();
m_asyncUpdateTimer.setInterval(qMin(m_asyncUpdateTimer.interval(),
delay == QmakeProFile::ParseLater ? UPDATE_INTERVAL : 0));
if (!isParsing())
emitParsingStarted();
m_asyncUpdateTimer.start();
}
void QmakeProject::incrementPendingEvaluateFutures()
{
if (m_pendingEvaluateFuturesCount == 0)
m_guard = guardParsingRun();
++m_pendingEvaluateFuturesCount;
QTC_ASSERT(isParsing(), emitParsingStarted());
m_asyncUpdateFutureInterface->setProgressRange(m_asyncUpdateFutureInterface->progressMinimum(),
m_asyncUpdateFutureInterface->progressMaximum() + 1);
}
@@ -486,7 +486,8 @@ void QmakeProject::decrementPendingEvaluateFutures()
updateBuildSystemData();
if (activeTarget())
activeTarget()->updateDefaultDeployConfigurations();
emitParsingFinished(true); // Qmake always returns (some) data, even when it failed:-)
m_guard.markAsSuccess(); // Qmake always returns (some) data, even when it failed:-)
m_guard = {};
}
}
}

View File

@@ -179,6 +179,8 @@ private:
ProjectExplorer::Target *m_activeTarget = nullptr;
mutable ProjectExplorer::ProjectImporter *m_projectImporter = nullptr;
ParseGuard m_guard;
};
} // namespace QmakeProjectManager

View File

@@ -152,7 +152,7 @@ void QmlProject::parseProject(RefreshOptions options)
void QmlProject::refresh(RefreshOptions options)
{
emitParsingStarted();
ParseGuard guard = guardParsingRun();
parseProject(options);
if (options & Files)
@@ -170,7 +170,7 @@ void QmlProject::refresh(RefreshOptions options)
modelManager->updateProjectInfo(projectInfo, this);
emitParsingFinished(true);
guard.markAsSuccess();
}
QString QmlProject::mainFile() const