Commit 0b037a8c authored by Igor Kushnir's avatar Igor Kushnir

Kill ParseProjectJob before closing its project

Remove the now redundant deleting of the ParseProjectJob when its
project is destroyed. ParseProjectJob objects are created in
ProjectController::reparseProject(), which is called when the project is
already fully open. So the project is guaranteed to be closed before it
is destroyed.

A Project is destroyed via deleteLater() after it is closed, so if the
event loop is busy at the time, &IProject::destroyed can be emitted much
later than IProjectController::projectClosing(). With this commit
ParseProjectJob is killed earlier than it was destroyed without the
commit.

Early-return from ParseProjectJob::queueFilesToParse() not only when the
job has been destroyed, but when it has been killed too. The earlier
return not just avoids unnecessary work, but is essential during the
application shutdown: prevents a crash by not accessing IndexedString
after DUChain::shutdown(). Note that KJob::kill() calls deleteLater(),
so a job can be destroyed a long time after it is killed if the event
loop is busy, as it is at shutdown. ParseProjectJob is killed early in
the shutdown process from RunController::cleanup() - before
ProjectController::cleanup(), which would kill it otherwise, and
(importantly to prevent the crash) before DUChain::shutdown().

Remove the shuttingDown() check from ParseProjectJob::start(), because
this member function doesn't access globals on its own. The appropriate
safety checks are now performed in the scheduled
ParseProjectJob::queueFilesToParse().

Don't call deleteLater() in ParseProjectJob::doKill() just before
returning true: rely on auto-delete KJob base class to call it.

BUG: 427387
FIXED-IN: 5.6.1
parent 89077c7c
......@@ -30,6 +30,7 @@
#include <language/backgroundparser/backgroundparser.h>
#include <kcoreaddons_version.h>
#include <KLocalizedString>
#include <QApplication>
......@@ -57,7 +58,6 @@ public:
bool ParseProjectJob::doKill()
{
qCDebug(LANGUAGE) << "stopping project parse job";
deleteLater();
return true;
}
......@@ -74,8 +74,6 @@ ParseProjectJob::ParseProjectJob(IProject* project, bool forceUpdate, bool force
{
Q_D(ParseProjectJob);
connect(project, &IProject::destroyed, this, &ParseProjectJob::deleteNow);
if (forceAll || ICore::self()->projectController()->parseAllProjectSources()) {
d->filesToParse = project->fileSet();
} else {
......@@ -94,11 +92,6 @@ ParseProjectJob::ParseProjectJob(IProject* project, bool forceUpdate, bool force
setObjectName(i18np("Process 1 file in %2", "Process %1 files in %2", d->filesToParse.size(), project->name()));
}
void ParseProjectJob::deleteNow()
{
delete this;
}
void ParseProjectJob::updateProgress()
{
}
......@@ -121,10 +114,6 @@ void ParseProjectJob::start()
{
Q_D(ParseProjectJob);
if (ICore::self()->shuttingDown()) {
return;
}
if (d->filesToParse.isEmpty()) {
deleteLater();
return;
......@@ -140,6 +129,23 @@ void ParseProjectJob::queueFilesToParse()
{
Q_D(ParseProjectJob);
const auto isJobKilled = [this] {
#if KCOREADDONS_VERSION >= QT_VERSION_CHECK(5, 75, 0)
if (Q_UNLIKELY(isFinished())) {
#else
if (Q_UNLIKELY(error() == KilledJobError)) {
#endif
qCDebug(LANGUAGE) << "Aborting queuing project files to parse."
" This job has been killed:" << objectName();
return true;
}
return false;
};
if (isJobKilled()) {
return;
}
TopDUContext::Features processingLevel = d->filesToParse.size() <
ICore::self()->languageController()->completionSettings()->
minFilesForSimplifiedParsing() ?
......@@ -194,7 +200,12 @@ void ParseProjectJob::queueFilesToParse()
++processed;
if (processed == processAfter) {
QApplication::processEvents();
if (!crashGuard) {
if (Q_UNLIKELY(!crashGuard)) {
qCDebug(LANGUAGE) << "Aborting queuing project files to parse."
" This job has been destroyed.";
return;
}
if (isJobKilled()) {
return;
}
processed = 0;
......
......@@ -35,8 +35,6 @@ class ParseProjectJobPrivate;
///forceAll argument is true. That forceAll argument allows to
///trigger a full project reparse after the initial import, e.g.
///via the project manager's context menu.
///ParseProjectJob instances delete themselves as soon as the project
///is deleted or when a new job is started.
class KDEVPLATFORMLANGUAGE_EXPORT ParseProjectJob
: public KJob
{
......@@ -49,7 +47,6 @@ public:
bool doKill() override;
private Q_SLOTS:
void deleteNow();
void queueFilesToParse();
void updateReady(const KDevelop::IndexedString& url, const KDevelop::ReferencedTopDUContext& topContext);
......
......@@ -1067,6 +1067,9 @@ void ProjectController::takeProject(IProject* proj)
// loading might have failed
d->m_currentlyOpening.removeAll(proj->projectFile().toUrl());
d->m_projects.removeAll(proj);
if (auto* job = d->m_parseJobs.value(proj)) {
job->kill(); // Removes job from m_parseJobs.
}
emit projectClosing(proj);
//Core::self()->saveSettings(); // The project file is being closed.
// Now we can save settings for all of the Core
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment