diff options
author | Jarek Kobus <jaroslaw.kobus@qt.io> | 2021-03-01 15:01:35 +0100 |
---|---|---|
committer | Jarek Kobus <jaroslaw.kobus@qt.io> | 2021-03-17 19:50:28 +0000 |
commit | 8fad758f6038875f5219c79fd35e937956e4c890 (patch) | |
tree | 11597fa49736b850e9addc9f1cf8717222edbbdb /src/plugins/classview/classviewmanager.cpp | |
parent | ced7a2e51ff2d710dd3c18b0259acb595011cc69 (diff) |
ClassView: Fix a possible crash on session switch
Don't call SessionManager methods in non-main thread.
It's not safe to call SessionManger::projects() or
any method of Project class in a non-main thread,
as in meantime the Project object may get deleted
or the Project object may change in main thread in
a not thread-safe way.
Instead, prepare the data needed for the parser's
thread before, when scheduling a call in a main thread,
and pass this data in a safe way.
This fixes possible crash in class view, e.g. on session
switch.
Task-number: QTCREATORBUG-25317
Fixes: QTCREATORBUG-25312
Change-Id: I114aae788aec649d1de3b3d3afdd049ed1e9b2c6
Reviewed-by: Eike Ziller <eike.ziller@qt.io>
Diffstat (limited to 'src/plugins/classview/classviewmanager.cpp')
-rw-r--r-- | src/plugins/classview/classviewmanager.cpp | 49 |
1 files changed, 28 insertions, 21 deletions
diff --git a/src/plugins/classview/classviewmanager.cpp b/src/plugins/classview/classviewmanager.cpp index 7724fbc493..b067ad1cc5 100644 --- a/src/plugins/classview/classviewmanager.cpp +++ b/src/plugins/classview/classviewmanager.cpp @@ -44,6 +44,7 @@ #include <QTimer> using namespace Core; +using namespace ProjectExplorer; using namespace Utils; namespace ClassView { @@ -94,7 +95,7 @@ public: ParserTreeItem::ConstPtr m_root; QTimer m_timer; - QHash<QString, CPlusPlus::Document::Ptr> m_awaitingDocuments; + QSet<FilePath> m_awaitingDocuments; //! Internal manager state. \sa Manager::state bool state = false; @@ -116,7 +117,15 @@ void ManagerPrivate::cancelScheduledUpdate() void ManagerPrivate::resetParser() { cancelScheduledUpdate(); - QMetaObject::invokeMethod(m_parser, &Parser::resetDataToCurrentState, Qt::QueuedConnection); + + QHash<FilePath, QPair<QString, FilePaths>> projectData; + for (const Project *project : SessionManager::projects()) { + projectData.insert(project->projectFilePath(), + qMakePair(project->displayName(), project->files(Project::SourceFiles))); + } + QMetaObject::invokeMethod(m_parser, [this, projectData]() { + m_parser->resetData(projectData); + }, Qt::QueuedConnection); } /*! @@ -218,15 +227,26 @@ bool Manager::hasChildren(QStandardItem *item) const void Manager::initialize() { - using ProjectExplorer::SessionManager; d->m_timer.setSingleShot(true); // connections to enable/disable navi widget factory SessionManager *sessionManager = SessionManager::instance(); connect(sessionManager, &SessionManager::projectAdded, - this, &Manager::onProjectListChanged); + this, [this](Project *project) { + const FilePath projectPath = project->projectFilePath(); + const QString projectName = project->displayName(); + const FilePaths projectFiles = project->files(Project::SourceFiles); + QMetaObject::invokeMethod(d->m_parser, [this, projectPath, projectName, projectFiles]() { + d->m_parser->addProject(projectPath, projectName, projectFiles); + }, Qt::QueuedConnection); + }); connect(sessionManager, &SessionManager::projectRemoved, - this, &Manager::onProjectListChanged); + this, [this](Project *project) { + const FilePath projectPath = project->projectFilePath(); + QMetaObject::invokeMethod(d->m_parser, [this, projectPath]() { + d->m_parser->removeProject(projectPath); + }, Qt::QueuedConnection); + }); // connect to the progress manager for signals about Parsing tasks connect(ProgressManager::instance(), &ProgressManager::taskStarted, @@ -283,12 +303,12 @@ void Manager::initialize() if (doc.data() == nullptr) return; - d->m_awaitingDocuments.insert(doc->fileName(), doc); + d->m_awaitingDocuments.insert(FilePath::fromString(doc->fileName())); d->m_timer.start(400); // Accumulate multiple requests into one, restarts the timer }); connect(&d->m_timer, &QTimer::timeout, this, [this]() { - const QList<CPlusPlus::Document::Ptr> docsToBeUpdated = d->m_awaitingDocuments.values(); + const QSet<FilePath> docsToBeUpdated = d->m_awaitingDocuments; d->cancelScheduledUpdate(); if (!state() || d->disableCodeParser) // enabling any of them will trigger the total update return; @@ -346,20 +366,7 @@ void Manager::onWidgetVisibilityIsChanged(bool visibility) if (!visibility) return; setState(true); - onProjectListChanged(); -} - -/*! - Reacts to the project list being changed by updating the navigation pane - visibility if necessary. -*/ - -void Manager::onProjectListChanged() -{ - // do nothing if Manager is disabled - if (!state()) - return; - + // TODO: this one may change into getter (when a new class view widget is being shown) QMetaObject::invokeMethod(d->m_parser, &Parser::requestCurrentState, Qt::QueuedConnection); } |