forked from qt-creator/qt-creator
Don't call DocumentManager::addDocument() from non-main thread
Detect that constructor of ResourceTopLevelNode is being
run from non-main thread and omit creation of ResourceFileWatcher
in this case. The construction of ResourceFileWatcher
will be postponed until the node tree returns back
to the main thread. This happens later inside
Project::setRootProjectNode() when ProjectTree::applyTreeManager()
is called for the second time - this time it's done from the main
thread. In order to setup the lacking resource file watchers
we install an additional folder node visitor only in case when
the handler is called from main thread. The visitor
sets up the lacking resource watchers if that's still needed.
Amends: 0bcab32657
Fixes: QTCREATORBUG-26417
Change-Id: Ia1bfb7f284afb833b6b4291accc4d0a91bd0d6c5
Reviewed-by: Eike Ziller <eike.ziller@qt.io>
This commit is contained in:
@@ -168,6 +168,7 @@ add_qtc_library(Utils
|
|||||||
textfileformat.cpp textfileformat.h
|
textfileformat.cpp textfileformat.h
|
||||||
textutils.cpp textutils.h
|
textutils.cpp textutils.h
|
||||||
theme/theme.cpp theme/theme.h theme/theme_p.h
|
theme/theme.cpp theme/theme.h theme/theme_p.h
|
||||||
|
threadutils.cpp threadutils.h
|
||||||
tooltip/effects.h
|
tooltip/effects.h
|
||||||
tooltip/tips.cpp tooltip/tips.h
|
tooltip/tips.cpp tooltip/tips.h
|
||||||
tooltip/tooltip.cpp tooltip/tooltip.h
|
tooltip/tooltip.cpp tooltip/tooltip.h
|
||||||
|
38
src/libs/utils/threadutils.cpp
Normal file
38
src/libs/utils/threadutils.cpp
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
/****************************************************************************
|
||||||
|
**
|
||||||
|
** Copyright (C) 2021 The Qt Company Ltd.
|
||||||
|
** Contact: https://www.qt.io/licensing/
|
||||||
|
**
|
||||||
|
** This file is part of Qt Creator.
|
||||||
|
**
|
||||||
|
** Commercial License Usage
|
||||||
|
** Licensees holding valid commercial Qt licenses may use this file in
|
||||||
|
** accordance with the commercial license agreement provided with the
|
||||||
|
** Software or, alternatively, in accordance with the terms contained in
|
||||||
|
** a written agreement between you and The Qt Company. For licensing terms
|
||||||
|
** and conditions see https://www.qt.io/terms-conditions. For further
|
||||||
|
** information use the contact form at https://www.qt.io/contact-us.
|
||||||
|
**
|
||||||
|
** GNU General Public License Usage
|
||||||
|
** Alternatively, this file may be used under the terms of the GNU
|
||||||
|
** General Public License version 3 as published by the Free Software
|
||||||
|
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
|
||||||
|
** included in the packaging of this file. Please review the following
|
||||||
|
** information to ensure the GNU General Public License requirements will
|
||||||
|
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
|
||||||
|
**
|
||||||
|
****************************************************************************/
|
||||||
|
|
||||||
|
#include "threadutils.h"
|
||||||
|
|
||||||
|
#include <QCoreApplication>
|
||||||
|
#include <QThread>
|
||||||
|
|
||||||
|
namespace Utils {
|
||||||
|
|
||||||
|
bool isMainThread()
|
||||||
|
{
|
||||||
|
return QThread::currentThread() == qApp->thread();
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace Utils
|
35
src/libs/utils/threadutils.h
Normal file
35
src/libs/utils/threadutils.h
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
/****************************************************************************
|
||||||
|
**
|
||||||
|
** Copyright (C) 2021 The Qt Company Ltd.
|
||||||
|
** Contact: https://www.qt.io/licensing/
|
||||||
|
**
|
||||||
|
** This file is part of Qt Creator.
|
||||||
|
**
|
||||||
|
** Commercial License Usage
|
||||||
|
** Licensees holding valid commercial Qt licenses may use this file in
|
||||||
|
** accordance with the commercial license agreement provided with the
|
||||||
|
** Software or, alternatively, in accordance with the terms contained in
|
||||||
|
** a written agreement between you and The Qt Company. For licensing terms
|
||||||
|
** and conditions see https://www.qt.io/terms-conditions. For further
|
||||||
|
** information use the contact form at https://www.qt.io/contact-us.
|
||||||
|
**
|
||||||
|
** GNU General Public License Usage
|
||||||
|
** Alternatively, this file may be used under the terms of the GNU
|
||||||
|
** General Public License version 3 as published by the Free Software
|
||||||
|
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
|
||||||
|
** included in the packaging of this file. Please review the following
|
||||||
|
** information to ensure the GNU General Public License requirements will
|
||||||
|
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
|
||||||
|
**
|
||||||
|
****************************************************************************/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include "utils_global.h"
|
||||||
|
|
||||||
|
namespace Utils {
|
||||||
|
|
||||||
|
QTCREATOR_UTILS_EXPORT bool isMainThread();
|
||||||
|
|
||||||
|
} // namespace Utils
|
||||||
|
|
@@ -143,6 +143,7 @@ SOURCES += \
|
|||||||
$$PWD/link.cpp \
|
$$PWD/link.cpp \
|
||||||
$$PWD/linecolumn.cpp \
|
$$PWD/linecolumn.cpp \
|
||||||
$$PWD/multitextcursor.cpp \
|
$$PWD/multitextcursor.cpp \
|
||||||
|
$$PWD/threadutils.cpp \
|
||||||
$$PWD/singleton.cpp
|
$$PWD/singleton.cpp
|
||||||
|
|
||||||
HEADERS += \
|
HEADERS += \
|
||||||
@@ -310,6 +311,7 @@ HEADERS += \
|
|||||||
$$PWD/launchersocket.h \
|
$$PWD/launchersocket.h \
|
||||||
$$PWD/qtcsettings.h \
|
$$PWD/qtcsettings.h \
|
||||||
$$PWD/multitextcursor.h \
|
$$PWD/multitextcursor.h \
|
||||||
|
$$PWD/threadutils.h \
|
||||||
$$PWD/singleton.h
|
$$PWD/singleton.h
|
||||||
|
|
||||||
FORMS += $$PWD/filewizardpage.ui \
|
FORMS += $$PWD/filewizardpage.ui \
|
||||||
|
@@ -298,6 +298,8 @@ Project {
|
|||||||
"textfileformat.h",
|
"textfileformat.h",
|
||||||
"textutils.cpp",
|
"textutils.cpp",
|
||||||
"textutils.h",
|
"textutils.h",
|
||||||
|
"threadutils.cpp",
|
||||||
|
"threadutils.h",
|
||||||
"treemodel.cpp",
|
"treemodel.cpp",
|
||||||
"treemodel.h",
|
"treemodel.h",
|
||||||
"treeviewcombobox.cpp",
|
"treeviewcombobox.cpp",
|
||||||
|
@@ -757,7 +757,7 @@ FileApiQtcData extractData(FileApiData &input,
|
|||||||
result.projectParts = generateRawProjectParts(data, sourceDirectory, buildDirectory);
|
result.projectParts = generateRawProjectParts(data, sourceDirectory, buildDirectory);
|
||||||
|
|
||||||
auto rootProjectNode = generateRootProjectNode(data, sourceDirectory, buildDirectory);
|
auto rootProjectNode = generateRootProjectNode(data, sourceDirectory, buildDirectory);
|
||||||
ProjectTree::applyTreeManager(rootProjectNode.get()); // QRC nodes
|
ProjectTree::applyTreeManager(rootProjectNode.get(), ProjectTree::AsyncPhase); // QRC nodes
|
||||||
result.rootProjectNode = std::move(rootProjectNode);
|
result.rootProjectNode = std::move(rootProjectNode);
|
||||||
|
|
||||||
setupLocationInfoForTargets(result.rootProjectNode.get(), result.buildTargets);
|
setupLocationInfoForTargets(result.rootProjectNode.get(), result.buildTargets);
|
||||||
|
@@ -55,6 +55,7 @@
|
|||||||
#include <utils/pathchooser.h>
|
#include <utils/pathchooser.h>
|
||||||
#include <utils/qtcassert.h>
|
#include <utils/qtcassert.h>
|
||||||
#include <utils/reloadpromptutils.h>
|
#include <utils/reloadpromptutils.h>
|
||||||
|
#include <utils/threadutils.h>
|
||||||
|
|
||||||
#include <QStringList>
|
#include <QStringList>
|
||||||
#include <QDateTime>
|
#include <QDateTime>
|
||||||
@@ -326,6 +327,7 @@ static void addFileInfo(IDocument *document, const FilePath &filePath, const Fil
|
|||||||
(The added file names are guaranteed to be absolute and cleaned.) */
|
(The added file names are guaranteed to be absolute and cleaned.) */
|
||||||
static void addFileInfos(const QList<IDocument *> &documents)
|
static void addFileInfos(const QList<IDocument *> &documents)
|
||||||
{
|
{
|
||||||
|
QTC_ASSERT(isMainThread(), return);
|
||||||
FilePaths pathsToWatch;
|
FilePaths pathsToWatch;
|
||||||
FilePaths linkPathsToWatch;
|
FilePaths linkPathsToWatch;
|
||||||
for (IDocument *document : documents) {
|
for (IDocument *document : documents) {
|
||||||
@@ -400,6 +402,7 @@ void DocumentManager::addDocuments(const QList<IDocument *> &documents, bool add
|
|||||||
*/
|
*/
|
||||||
static void removeFileInfo(IDocument *document)
|
static void removeFileInfo(IDocument *document)
|
||||||
{
|
{
|
||||||
|
QTC_ASSERT(isMainThread(), return);
|
||||||
if (!d->m_documentsWithWatch.contains(document))
|
if (!d->m_documentsWithWatch.contains(document))
|
||||||
return;
|
return;
|
||||||
foreach (const FilePath &filePath, d->m_documentsWithWatch.value(document)) {
|
foreach (const FilePath &filePath, d->m_documentsWithWatch.value(document)) {
|
||||||
|
@@ -586,7 +586,8 @@ void Project::setRootProjectNode(std::unique_ptr<ProjectNode> &&root)
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (root) {
|
if (root) {
|
||||||
ProjectTree::applyTreeManager(root.get());
|
ProjectTree::applyTreeManager(root.get(), ProjectTree::AsyncPhase);
|
||||||
|
ProjectTree::applyTreeManager(root.get(), ProjectTree::FinalPhase);
|
||||||
root->setParentFolderNode(d->m_containerNode.get());
|
root->setParentFolderNode(d->m_containerNode.get());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -402,13 +402,13 @@ void ProjectTree::registerTreeManager(const TreeManagerFunction &treeChange)
|
|||||||
s_instance->m_treeManagers.append(treeChange);
|
s_instance->m_treeManagers.append(treeChange);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ProjectTree::applyTreeManager(FolderNode *folder)
|
void ProjectTree::applyTreeManager(FolderNode *folder, ConstructionPhase phase)
|
||||||
{
|
{
|
||||||
if (!folder)
|
if (!folder)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
for (TreeManagerFunction &f : s_instance->m_treeManagers)
|
for (TreeManagerFunction &f : s_instance->m_treeManagers)
|
||||||
f(folder);
|
f(folder, phase);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ProjectTree::hasNode(const Node *node)
|
bool ProjectTree::hasNode(const Node *node)
|
||||||
|
@@ -68,6 +68,11 @@ public:
|
|||||||
const bool m_active = false;
|
const bool m_active = false;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
enum ConstructionPhase {
|
||||||
|
AsyncPhase,
|
||||||
|
FinalPhase
|
||||||
|
};
|
||||||
|
|
||||||
// Integration with ProjectTreeWidget
|
// Integration with ProjectTreeWidget
|
||||||
static void registerWidget(Internal::ProjectTreeWidget *widget);
|
static void registerWidget(Internal::ProjectTreeWidget *widget);
|
||||||
static void unregisterWidget(Internal::ProjectTreeWidget *widget);
|
static void unregisterWidget(Internal::ProjectTreeWidget *widget);
|
||||||
@@ -79,9 +84,9 @@ public:
|
|||||||
|
|
||||||
static void highlightProject(Project *project, const QString &message);
|
static void highlightProject(Project *project, const QString &message);
|
||||||
|
|
||||||
using TreeManagerFunction = std::function<void(FolderNode *)>;
|
using TreeManagerFunction = std::function<void(FolderNode *, ConstructionPhase)>;
|
||||||
static void registerTreeManager(const TreeManagerFunction &treeChange);
|
static void registerTreeManager(const TreeManagerFunction &treeChange);
|
||||||
static void applyTreeManager(FolderNode *folder);
|
static void applyTreeManager(FolderNode *folder, ConstructionPhase phase);
|
||||||
|
|
||||||
// Nodes:
|
// Nodes:
|
||||||
static bool hasNode(const Node *node);
|
static bool hasNode(const Node *node);
|
||||||
|
@@ -159,7 +159,7 @@ static std::unique_ptr<FolderNode> createFolderNode(const Utils::FilePath &direc
|
|||||||
std::unique_ptr<FileNode> node(fn->clone());
|
std::unique_ptr<FileNode> node(fn->clone());
|
||||||
fileSystemNode->addNestedNode(std::move(node));
|
fileSystemNode->addNestedNode(std::move(node));
|
||||||
}
|
}
|
||||||
ProjectTree::applyTreeManager(fileSystemNode.get()); // QRC nodes
|
ProjectTree::applyTreeManager(fileSystemNode.get(), ProjectTree::AsyncPhase); // QRC nodes
|
||||||
return fileSystemNode;
|
return fileSystemNode;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -232,7 +232,7 @@ QbsProjectNode *QbsNodeTreeBuilder::buildTree(const QString &projectName,
|
|||||||
}
|
}
|
||||||
buildSystemFiles->compress();
|
buildSystemFiles->compress();
|
||||||
root->addNode(std::move(buildSystemFiles));
|
root->addNode(std::move(buildSystemFiles));
|
||||||
ProjectTree::applyTreeManager(root.get()); // QRC nodes
|
ProjectTree::applyTreeManager(root.get(), ProjectTree::AsyncPhase); // QRC nodes
|
||||||
return root.release();
|
return root.release();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -46,6 +46,7 @@
|
|||||||
#include <utils/algorithm.h>
|
#include <utils/algorithm.h>
|
||||||
#include <utils/parameteraction.h>
|
#include <utils/parameteraction.h>
|
||||||
#include <utils/qtcassert.h>
|
#include <utils/qtcassert.h>
|
||||||
|
#include <utils/threadutils.h>
|
||||||
|
|
||||||
#include <QCoreApplication>
|
#include <QCoreApplication>
|
||||||
#include <QAction>
|
#include <QAction>
|
||||||
@@ -247,21 +248,33 @@ ResourceEditorPluginPrivate::ResourceEditorPluginPrivate(ResourceEditorPlugin *q
|
|||||||
|
|
||||||
void ResourceEditorPlugin::extensionsInitialized()
|
void ResourceEditorPlugin::extensionsInitialized()
|
||||||
{
|
{
|
||||||
ProjectTree::registerTreeManager([](FolderNode *folder) {
|
ProjectTree::registerTreeManager([](FolderNode *folder, ProjectTree::ConstructionPhase phase) {
|
||||||
QList<FileNode *> toReplace;
|
switch (phase) {
|
||||||
folder->forEachNode([&toReplace](FileNode *fn) {
|
case ProjectTree::AsyncPhase: {
|
||||||
if (fn->fileType() == FileType::Resource)
|
QList<FileNode *> toReplace;
|
||||||
toReplace.append(fn);
|
folder->forEachNode([&toReplace](FileNode *fn) {
|
||||||
});
|
if (fn->fileType() == FileType::Resource)
|
||||||
|
toReplace.append(fn);
|
||||||
for (FileNode *file : qAsConst(toReplace)) {
|
});
|
||||||
FolderNode *const pn = file->parentFolderNode();
|
for (FileNode *file : qAsConst(toReplace)) {
|
||||||
QTC_ASSERT(pn, continue);
|
FolderNode *const pn = file->parentFolderNode();
|
||||||
const Utils::FilePath path = file->filePath();
|
QTC_ASSERT(pn, continue);
|
||||||
auto topLevel = std::make_unique<ResourceTopLevelNode>(path, pn->filePath());
|
const Utils::FilePath path = file->filePath();
|
||||||
topLevel->setEnabled(file->isEnabled());
|
auto topLevel = std::make_unique<ResourceTopLevelNode>(path, pn->filePath());
|
||||||
topLevel->setIsGenerated(file->isGenerated());
|
topLevel->setEnabled(file->isEnabled());
|
||||||
pn->replaceSubtree(file, std::move(topLevel));
|
topLevel->setIsGenerated(file->isGenerated());
|
||||||
|
pn->replaceSubtree(file, std::move(topLevel));
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case ProjectTree::FinalPhase: {
|
||||||
|
folder->forEachNode({}, [](FolderNode *fn) {
|
||||||
|
auto *topLevel = dynamic_cast<ResourceTopLevelNode *>(fn);
|
||||||
|
if (topLevel)
|
||||||
|
topLevel->setupWatcherIfNeeded();
|
||||||
|
});
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@@ -36,6 +36,7 @@
|
|||||||
#include <utils/fileutils.h>
|
#include <utils/fileutils.h>
|
||||||
#include <utils/mimetypes/mimedatabase.h>
|
#include <utils/mimetypes/mimedatabase.h>
|
||||||
#include <utils/qtcassert.h>
|
#include <utils/qtcassert.h>
|
||||||
|
#include <utils/threadutils.h>
|
||||||
|
|
||||||
#include <QCoreApplication>
|
#include <QCoreApplication>
|
||||||
#include <QDir>
|
#include <QDir>
|
||||||
@@ -251,10 +252,8 @@ ResourceTopLevelNode::ResourceTopLevelNode(const FilePath &filePath,
|
|||||||
setShowWhenEmpty(true);
|
setShowWhenEmpty(true);
|
||||||
|
|
||||||
if (!filePath.isEmpty()) {
|
if (!filePath.isEmpty()) {
|
||||||
if (filePath.isReadableFile()) {
|
if (filePath.isReadableFile())
|
||||||
m_document = new ResourceFileWatcher(this);
|
setupWatcherIfNeeded();
|
||||||
DocumentManager::addDocument(m_document);
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
m_contents = contents;
|
m_contents = contents;
|
||||||
}
|
}
|
||||||
@@ -267,6 +266,15 @@ ResourceTopLevelNode::ResourceTopLevelNode(const FilePath &filePath,
|
|||||||
addInternalNodes();
|
addInternalNodes();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void ResourceTopLevelNode::setupWatcherIfNeeded()
|
||||||
|
{
|
||||||
|
if (m_document || !isMainThread())
|
||||||
|
return;
|
||||||
|
|
||||||
|
m_document = new ResourceFileWatcher(this);
|
||||||
|
DocumentManager::addDocument(m_document);
|
||||||
|
}
|
||||||
|
|
||||||
ResourceTopLevelNode::~ResourceTopLevelNode()
|
ResourceTopLevelNode::~ResourceTopLevelNode()
|
||||||
{
|
{
|
||||||
if (m_document)
|
if (m_document)
|
||||||
|
@@ -39,6 +39,7 @@ public:
|
|||||||
const QString &contents = {});
|
const QString &contents = {});
|
||||||
~ResourceTopLevelNode() override;
|
~ResourceTopLevelNode() override;
|
||||||
|
|
||||||
|
void setupWatcherIfNeeded();
|
||||||
void addInternalNodes();
|
void addInternalNodes();
|
||||||
|
|
||||||
bool supportsAction(ProjectExplorer::ProjectAction action, const Node *node) const override;
|
bool supportsAction(ProjectExplorer::ProjectAction action, const Node *node) const override;
|
||||||
|
Reference in New Issue
Block a user