QbsProjectManager: Fix handling of overlapping parse requests.

The current code simply asserts when a new parse request comes in while
parsing. However, that condition is easily triggered, for instance if a
project file is saved to disk during a parse operation. Such updates
currently have no effect at all (other than triggering an error
message).
Instead, we now cancel the old parse job and start a new one.

Change-Id: If2eeb93b85b5163dcea99785a0fc89a254d082db
Reviewed-by: Joerg Bornemann <joerg.bornemann@digia.com>
This commit is contained in:
Christian Kandeler
2014-07-14 10:22:37 +02:00
parent 051bccf89e
commit 20bfe889e9
4 changed files with 37 additions and 2 deletions

View File

@@ -102,6 +102,7 @@ QbsProject::QbsProject(QbsManager *manager, const QString &fileName) :
m_qbsUpdateFutureInterface(0), m_qbsUpdateFutureInterface(0),
m_forceParsing(false), m_forceParsing(false),
m_parsingScheduled(false), m_parsingScheduled(false),
m_cancelingParsing(false),
m_currentBc(0) m_currentBc(0)
{ {
m_parsingDelay.setInterval(1000); // delay parsing by 1s. m_parsingDelay.setInterval(1000); // delay parsing by 1s.
@@ -275,12 +276,23 @@ void QbsProject::handleQbsParsingDone(bool success)
{ {
QTC_ASSERT(m_qbsProjectParser, return); QTC_ASSERT(m_qbsProjectParser, return);
// If this parse operation was canceled, start a new one right away, ignoring the old result.
if (m_cancelingParsing) {
m_cancelingParsing = false;
m_qbsProjectParser->deleteLater();
m_qbsProjectParser = 0;
parseCurrentBuildConfiguration(m_forceParsing);
return;
}
generateErrors(m_qbsProjectParser->error()); generateErrors(m_qbsProjectParser->error());
if (success) { if (success) {
m_qbsProject = m_qbsProjectParser->qbsProject(); m_qbsProject = m_qbsProjectParser->qbsProject();
QTC_CHECK(m_qbsProject.isValid()); QTC_CHECK(m_qbsProject.isValid());
readQbsData(); readQbsData();
} else {
m_qbsUpdateFutureInterface->reportCanceled();
} }
m_qbsProjectParser->deleteLater(); m_qbsProjectParser->deleteLater();
@@ -369,12 +381,27 @@ void QbsProject::parseCurrentBuildConfiguration(bool force)
m_parsingScheduled = false; m_parsingScheduled = false;
if (!m_forceParsing) if (!m_forceParsing)
m_forceParsing = force; m_forceParsing = force;
if (m_cancelingParsing)
return;
if (!activeTarget()) if (!activeTarget())
return; return;
QbsBuildConfiguration *bc = qobject_cast<QbsBuildConfiguration *>(activeTarget()->activeBuildConfiguration()); QbsBuildConfiguration *bc = qobject_cast<QbsBuildConfiguration *>(activeTarget()->activeBuildConfiguration());
if (!bc) if (!bc)
return; return;
// New parse requests override old ones.
// NOTE: We need to wait for the current operation to finish, since otherwise there could
// be a conflict. Consider the case where the old qbs::ProjectSetupJob is writing
// to the build graph file when the cancel request comes in. If we don't wait for
// acknowledgment, it might still be doing that when the new one already reads from the
// same file.
if (m_qbsProjectParser) {
m_cancelingParsing = true;
m_qbsProjectParser->cancel();
return;
}
parse(bc->qbsConfiguration(), bc->environment(), bc->buildDirectory().toString()); parse(bc->qbsConfiguration(), bc->environment(), bc->buildDirectory().toString());
} }

View File

@@ -153,6 +153,7 @@ private:
QFutureInterface<bool> *m_qbsUpdateFutureInterface; QFutureInterface<bool> *m_qbsUpdateFutureInterface;
bool m_forceParsing; bool m_forceParsing;
bool m_parsingScheduled; bool m_parsingScheduled;
bool m_cancelingParsing;
QFuture<void> m_codeModelFuture; QFuture<void> m_codeModelFuture;

View File

@@ -136,6 +136,12 @@ bool QbsProjectParser::parse(const QVariantMap &config, const Environment &env,
return true; return true;
} }
void QbsProjectParser::cancel()
{
QTC_ASSERT(m_qbsSetupProjectJob, return);
m_qbsSetupProjectJob->cancel();
}
qbs::Project QbsProjectParser::qbsProject() const qbs::Project QbsProjectParser::qbsProject() const
{ {
return m_project; return m_project;
@@ -153,8 +159,8 @@ void QbsProjectParser::handleQbsParsingDone(bool success)
m_project = m_qbsSetupProjectJob->project(); m_project = m_qbsSetupProjectJob->project();
m_error = m_qbsSetupProjectJob->error(); m_error = m_qbsSetupProjectJob->error();
if (!success) // Do not report the operation as canceled here, as we might want to make overlapping
m_fi->reportCanceled(); // parses appear atomic to the user.
emit done(success); emit done(success);
} }

View File

@@ -54,6 +54,7 @@ public:
void setForced(bool); void setForced(bool);
bool parse(const QVariantMap &config, const Utils::Environment &env, const QString &dir); bool parse(const QVariantMap &config, const Utils::Environment &env, const QString &dir);
void cancel();
qbs::Project qbsProject() const; qbs::Project qbsProject() const;
qbs::ErrorInfo error(); qbs::ErrorInfo error();