From 187050144369ecfda58a567dd29715bcae5c9f92 Mon Sep 17 00:00:00 2001 From: Eike Ziller Date: Tue, 5 Sep 2023 10:10:44 +0200 Subject: [PATCH] Delay modifications to MIME database until first use Initialization of the MIME database takes time, and it is good to avoid this during startup. We nevertheless want to apply some modifications to the MIME database, so we need to delay these modifications until first use. Allow registration of additional initialization functions. Since the initialization functions access the MIME database, which does locking for thread- safety, we need to separate the initialization to outside these locking functions. Change-Id: I2b1706347768bcf081644e078ccfa45302d61641 Reviewed-by: Reviewed-by: hjk --- src/libs/extensionsystem/pluginmanager.cpp | 4 +- src/libs/utils/mimetypes2/mimedatabase.cpp | 51 +++++++++++-------- src/libs/utils/mimetypes2/mimedatabase_p.h | 10 +++- src/libs/utils/mimetypes2/mimeutils.cpp | 24 ++++++--- src/libs/utils/mimeutils.h | 1 + src/plugins/coreplugin/mimetypesettings.cpp | 19 ++++--- src/plugins/cppeditor/cppeditorplugin.cpp | 4 +- src/plugins/cppeditor/cppfilesettingspage.cpp | 15 +++++- src/plugins/cppeditor/cppfilesettingspage.h | 1 + 9 files changed, 88 insertions(+), 41 deletions(-) diff --git a/src/libs/extensionsystem/pluginmanager.cpp b/src/libs/extensionsystem/pluginmanager.cpp index c072ea67fb4..116243ede39 100644 --- a/src/libs/extensionsystem/pluginmanager.cpp +++ b/src/libs/extensionsystem/pluginmanager.cpp @@ -957,6 +957,7 @@ void PluginManagerPrivate::nextDelayedInitialize() break; // do next delayedInitialize after a delay } if (delayedInitializeQueue.empty()) { + Utils::setMimeStartupPhase(MimeStartupPhase::UpAndRunning); m_isInitializationDone = true; delete delayedInitializeTimer; delayedInitializeTimer = nullptr; @@ -1370,7 +1371,6 @@ void PluginManagerPrivate::loadPlugins() loadPlugin(spec, PluginSpec::Initialized); } - Utils::setMimeStartupPhase(MimeStartupPhase::PluginsDelayedInitializing); { NANOTRACE_SCOPE("ExtensionSystem", "ExtensionsInitialized"); Utils::reverseForeach(queue, [this](PluginSpec *spec) { @@ -1384,8 +1384,8 @@ void PluginManagerPrivate::loadPlugins() }); } emit q->pluginsChanged(); - Utils::setMimeStartupPhase(MimeStartupPhase::UpAndRunning); + Utils::setMimeStartupPhase(MimeStartupPhase::PluginsDelayedInitializing); delayedInitializeTimer = new QTimer; delayedInitializeTimer->setInterval(DELAYED_INITIALIZE_INTERVAL); delayedInitializeTimer->setSingleShot(true); diff --git a/src/libs/utils/mimetypes2/mimedatabase.cpp b/src/libs/utils/mimetypes2/mimedatabase.cpp index b4966a952dd..60f3fbf0db5 100644 --- a/src/libs/utils/mimetypes2/mimedatabase.cpp +++ b/src/libs/utils/mimetypes2/mimedatabase.cpp @@ -562,12 +562,9 @@ MimeDatabase::~MimeDatabase() */ MimeType MimeDatabase::mimeTypeForName(const QString &nameOrAlias) const { + d->checkInitPhase(nameOrAlias); QMutexLocker locker(&d->mutex); - if (d->m_startupPhase <= int(MimeStartupPhase::PluginsInitializing)) - qWarning("Accessing MimeDatabase for %s before plugins are initialized", - qPrintable(nameOrAlias)); - return d->mimeTypeForName(nameOrAlias); } @@ -601,12 +598,9 @@ MimeType MimeDatabase::mimeTypeForName(const QString &nameOrAlias) const */ MimeType MimeDatabase::mimeTypeForFile(const QFileInfo &fileInfo, MatchMode mode) const { + d->checkInitPhase(fileInfo.filePath()); QMutexLocker locker(&d->mutex); - if (d->m_startupPhase <= int(MimeStartupPhase::PluginsInitializing)) - qWarning("Accessing MimeDatabase for %s before plugins are initialized", - qPrintable(fileInfo.filePath())); - if (fileInfo.isDir()) return d->mimeTypeForName(QLatin1String("inode/directory")); @@ -659,12 +653,9 @@ MimeType MimeDatabase::mimeTypeForFile(const QFileInfo &fileInfo, MatchMode mode MimeType MimeDatabase::mimeTypeForFile(const QString &fileName, MatchMode mode) const { if (mode == MatchExtension) { + d->checkInitPhase(fileName); QMutexLocker locker(&d->mutex); - if (d->m_startupPhase <= int(MimeStartupPhase::PluginsInitializing)) - qWarning("Accessing MimeDatabase for %s before plugins are initialized", - qPrintable(fileName)); - const QStringList matches = d->mimeTypeForFileName(fileName); const int matchCount = matches.count(); if (matchCount == 0) { @@ -696,12 +687,9 @@ MimeType MimeDatabase::mimeTypeForFile(const QString &fileName, MatchMode mode) */ QList MimeDatabase::mimeTypesForFileName(const QString &fileName) const { + d->checkInitPhase(fileName); QMutexLocker locker(&d->mutex); - if (d->m_startupPhase <= int(MimeStartupPhase::PluginsInitializing)) - qWarning("Accessing MimeDatabase for %s before plugins are initialized", - qPrintable(fileName)); - const QStringList matches = d->mimeTypeForFileName(fileName); QList mimes; mimes.reserve(matches.count()); @@ -731,11 +719,9 @@ QString MimeDatabase::suffixForFileName(const QString &fileName) const */ MimeType MimeDatabase::mimeTypeForData(const QByteArray &data) const { + d->checkInitPhase("data"); QMutexLocker locker(&d->mutex); - if (d->m_startupPhase <= int(MimeStartupPhase::PluginsInitializing)) - qWarning("Accessing MimeDatabase for data before plugins are initialized"); - int accuracy = 0; return d->findByData(data, &accuracy); } @@ -863,11 +849,9 @@ MimeType MimeDatabase::mimeTypeForFileNameAndData(const QString &fileName, const */ QList MimeDatabase::allMimeTypes() const { + d->checkInitPhase("all mime types"); QMutexLocker locker(&d->mutex); - if (d->m_startupPhase <= int(MimeStartupPhase::PluginsInitializing)) - qWarning("Accessing MimeDatabase for all mime types before plugins are initialized"); - return d->allMimeTypes(); } @@ -924,4 +908,27 @@ void MimeDatabasePrivate::setGlobPatternsForMimeType(const MimeType &mimeType, } } +void MimeDatabasePrivate::checkInitPhase(const QString &info) +{ + QReadLocker locker(&m_initMutex); + if (m_startupPhase <= int(MimeStartupPhase::PluginsInitializing)) { + qWarning("Accessing MimeDatabase for %s before plugins are initialized", qPrintable(info)); + return; + } + // run initialization functions and ensure providers are loaded + // the initializers will call other MIME database functions which "checkInitPhase" again, + // so make sure not to recurse + if (!m_initialized.exchange(true)) { + for (const std::function &f : m_initializers) + f(); + QMutexLocker locker(&mutex); + providers(); + } +} + +void MimeDatabasePrivate::addInitializer(const std::function &init) +{ + m_initializers.append(init); +} + } // namespace Utils diff --git a/src/libs/utils/mimetypes2/mimedatabase_p.h b/src/libs/utils/mimetypes2/mimedatabase_p.h index 8709b2918eb..c816a5c0e5b 100644 --- a/src/libs/utils/mimetypes2/mimedatabase_p.h +++ b/src/libs/utils/mimetypes2/mimedatabase_p.h @@ -25,8 +25,11 @@ #include #include -#include +#include + +#include #include +#include QT_BEGIN_NAMESPACE class QIODevice; @@ -75,6 +78,8 @@ public: void setMagicRulesForMimeType(const MimeType &mimeType, const QMap> &rules); void setGlobPatternsForMimeType(const MimeType &mimeType, const QStringList &patterns); + void checkInitPhase(const QString &info); + void addInitializer(const std::function &init); private: using Providers = std::vector>; @@ -86,6 +91,7 @@ private: QElapsedTimer m_lastCheck; // added for Qt Creator + QList> m_initializers; QHash m_additionalData; // id -> data bool m_forceLoad = true; @@ -94,6 +100,8 @@ public: QMutex mutex; // added for Qt Creator + QReadWriteLock m_initMutex; + std::atomic_bool m_initialized = false; int m_startupPhase = 0; }; diff --git a/src/libs/utils/mimetypes2/mimeutils.cpp b/src/libs/utils/mimetypes2/mimeutils.cpp index 92aeed56f0c..711dc97187a 100644 --- a/src/libs/utils/mimetypes2/mimeutils.cpp +++ b/src/libs/utils/mimetypes2/mimeutils.cpp @@ -57,7 +57,7 @@ QList allMimeTypes() void setMimeStartupPhase(MimeStartupPhase phase) { auto d = MimeDatabasePrivate::instance(); - QMutexLocker locker(&d->mutex); + QWriteLocker locker(&d->m_initMutex); if (int(phase) != d->m_startupPhase + 1) { qWarning("Unexpected jump in MimedDatabase lifetime from %d to %d", d->m_startupPhase, @@ -69,12 +69,14 @@ void setMimeStartupPhase(MimeStartupPhase phase) void addMimeTypes(const QString &id, const QByteArray &data) { auto d = MimeDatabasePrivate::instance(); - QMutexLocker locker(&d->mutex); - - if (d->m_startupPhase >= int(MimeStartupPhase::PluginsDelayedInitializing)) { - qWarning("Adding items for ID \"%s\" to MimeDatabase after initialization time", - qPrintable(id)); + { + QReadLocker locker(&d->m_initMutex); + if (d->m_startupPhase >= int(MimeStartupPhase::PluginsDelayedInitializing)) { + qWarning("Adding items for ID \"%s\" to MimeDatabase after initialization time", + qPrintable(id)); + } } + QMutexLocker locker(&d->mutex); d->addMimeData(id, data); } @@ -130,4 +132,14 @@ void visitMimeParents(const MimeType &mimeType, } } +/*! + The \a init function will be executed once after the MIME database is first initialized. + It must be thread safe. +*/ +void addMimeInitializer(const std::function &init) +{ + auto d = MimeDatabasePrivate::instance(); + d->addInitializer(init); +} + } // namespace Utils diff --git a/src/libs/utils/mimeutils.h b/src/libs/utils/mimeutils.h index 3fa0fc4ebb9..c5f8b819456 100644 --- a/src/libs/utils/mimeutils.h +++ b/src/libs/utils/mimeutils.h @@ -43,6 +43,7 @@ enum class MimeStartupPhase { }; QTCREATOR_UTILS_EXPORT void setMimeStartupPhase(MimeStartupPhase); +QTCREATOR_UTILS_EXPORT void addMimeInitializer(const std::function &init); QTCREATOR_UTILS_EXPORT void addMimeTypes(const QString &id, const QByteArray &data); QTCREATOR_UTILS_EXPORT QMap> magicRulesForMimeType( const MimeType &mimeType); // priority -> rules diff --git a/src/plugins/coreplugin/mimetypesettings.cpp b/src/plugins/coreplugin/mimetypesettings.cpp index 0e39eaeb3e4..c2bf8202ea4 100644 --- a/src/plugins/coreplugin/mimetypesettings.cpp +++ b/src/plugins/coreplugin/mimetypesettings.cpp @@ -746,19 +746,25 @@ MimeTypeSettingsPrivate::UserMimeTypeHash MimeTypeSettingsPrivate::readUserModif return userMimeTypes; } -void MimeTypeSettingsPrivate::applyUserModifiedMimeTypes(const UserMimeTypeHash &mimeTypes) +static void registerUserModifiedMimeTypes(const MimeTypeSettingsPrivate::UserMimeTypeHash &mimeTypes) { - // register in mime data base, and remember for later for (auto it = mimeTypes.constBegin(); it != mimeTypes.constEnd(); ++it) { Utils::MimeType mt = Utils::mimeTypeForName(it.key()); - if (!mt.isValid()) // loaded from settings + if (!mt.isValid()) continue; - m_userModifiedMimeTypes.insert(it.key(), it.value()); Utils::setGlobPatternsForMimeType(mt, it.value().globPatterns); Utils::setMagicRulesForMimeType(mt, it.value().rules); } } +void MimeTypeSettingsPrivate::applyUserModifiedMimeTypes(const UserMimeTypeHash &mimeTypes) +{ + // register in mime data base, and remember for later + for (auto it = mimeTypes.constBegin(); it != mimeTypes.constEnd(); ++it) + m_userModifiedMimeTypes.insert(it.key(), it.value()); + registerUserModifiedMimeTypes(mimeTypes); +} + // MimeTypeSettingsPage MimeTypeSettings::MimeTypeSettings() @@ -792,8 +798,9 @@ QStringList MimeTypeSettings::keywords() const void MimeTypeSettings::restoreSettings() { MimeTypeSettingsPrivate::UserMimeTypeHash mimetypes - = MimeTypeSettingsPrivate::readUserModifiedMimeTypes(); - MimeTypeSettingsPrivate::applyUserModifiedMimeTypes(mimetypes); + = MimeTypeSettingsPrivate::readUserModifiedMimeTypes(); + MimeTypeSettingsPrivate::m_userModifiedMimeTypes = mimetypes; + Utils::addMimeInitializer([mimetypes] { registerUserModifiedMimeTypes(mimetypes); }); } QWidget *MimeEditorDelegate::createEditor(QWidget *parent, diff --git a/src/plugins/cppeditor/cppeditorplugin.cpp b/src/plugins/cppeditor/cppeditorplugin.cpp index 51bd004d1a7..58822a5f71b 100644 --- a/src/plugins/cppeditor/cppeditorplugin.cpp +++ b/src/plugins/cppeditor/cppeditorplugin.cpp @@ -516,9 +516,7 @@ void CppEditorPlugin::initialize() void CppEditorPlugin::extensionsInitialized() { d->m_fileSettings.fromSettings(ICore::settings()); - if (!d->m_fileSettings.applySuffixesToMimeDB()) - qWarning("Unable to apply cpp suffixes to mime database (cpp mime types not found).\n"); - + d->m_fileSettings.addMimeInitializer(); const auto fileNamesPanelFactory = new ProjectPanelFactory; fileNamesPanelFactory->setPriority(99); fileNamesPanelFactory->setDisplayName(Tr::tr("C++ File Naming")); diff --git a/src/plugins/cppeditor/cppfilesettingspage.cpp b/src/plugins/cppeditor/cppfilesettingspage.cpp index a506dff8b8a..3d3014f86b1 100644 --- a/src/plugins/cppeditor/cppfilesettingspage.cpp +++ b/src/plugins/cppeditor/cppfilesettingspage.cpp @@ -93,7 +93,7 @@ void CppFileSettings::fromSettings(QSettings *s) s->endGroup(); } -bool CppFileSettings::applySuffixesToMimeDB() +static bool applySuffixes(const QString &sourceSuffix, const QString &headerSuffix) { Utils::MimeType mt; mt = Utils::mimeTypeForName(QLatin1String(Constants::CPP_SOURCE_MIMETYPE)); @@ -107,6 +107,19 @@ bool CppFileSettings::applySuffixesToMimeDB() return true; } +void CppFileSettings::addMimeInitializer() const +{ + Utils::addMimeInitializer([sourceSuffix = sourceSuffix, headerSuffix = headerSuffix] { + if (!applySuffixes(sourceSuffix, headerSuffix)) + qWarning("Unable to apply cpp suffixes to mime database (cpp mime types not found).\n"); + }); +} + +bool CppFileSettings::applySuffixesToMimeDB() +{ + return applySuffixes(sourceSuffix, headerSuffix); +} + bool CppFileSettings::equals(const CppFileSettings &rhs) const { return lowerCaseFiles == rhs.lowerCaseFiles diff --git a/src/plugins/cppeditor/cppfilesettingspage.h b/src/plugins/cppeditor/cppfilesettingspage.h index f4c65fbbc5c..3232ba4d4d5 100644 --- a/src/plugins/cppeditor/cppfilesettingspage.h +++ b/src/plugins/cppeditor/cppfilesettingspage.h @@ -38,6 +38,7 @@ public: void toSettings(QSettings *) const; void fromSettings(QSettings *); + void addMimeInitializer() const; bool applySuffixesToMimeDB(); // Convenience to return a license template completely formatted.