Improve plugin caching
Summary: Extend the plasmoid startup cache to DataEngines and ContaimentActions. Include a cache for script engines. In practice it's always the same, we don't need to check the file system every time. Reviewers: #plasma, davidedmundson, mart Reviewed By: #plasma, davidedmundson, mart Subscribers: davidedmundson, mart, broulik, kde-frameworks-devel Tags: #frameworks Differential Revision: https://phabricator.kde.org/D22258
This commit is contained in:
parent
6d53dfad57
commit
21227e0f98
@ -69,15 +69,23 @@ public:
|
|||||||
static QString s_servicesPluginDir;
|
static QString s_servicesPluginDir;
|
||||||
static QString s_containmentActionsPluginDir;
|
static QString s_containmentActionsPluginDir;
|
||||||
|
|
||||||
// We only use this cache during start of the process to speed up many consecutive calls
|
class Cache {
|
||||||
// After that, we're too afraid to produce race conditions and it's not that time-critical anyway
|
// We only use this cache during start of the process to speed up many consecutive calls
|
||||||
// the 20 seconds here means that the cache is only used within 20sec during startup, after that,
|
// After that, we're too afraid to produce race conditions and it's not that time-critical anyway
|
||||||
// complexity goes up and we'd have to update the cache in order to avoid subtle bugs
|
// the 20 seconds here means that the cache is only used within 20sec during startup, after that,
|
||||||
// just not using the cache is way easier then, since it doesn't make *that* much of a difference,
|
// complexity goes up and we'd have to update the cache in order to avoid subtle bugs
|
||||||
// anyway
|
// just not using the cache is way easier then, since it doesn't make *that* much of a difference,
|
||||||
int maxCacheAge = 20;
|
// anyway
|
||||||
qint64 pluginCacheAge = 0;
|
int maxCacheAge = 20;
|
||||||
QHash<QString, QVector<KPluginMetaData>> pluginCache;
|
qint64 pluginCacheAge = 0;
|
||||||
|
QHash<QString, QVector<KPluginMetaData>> plugins;
|
||||||
|
|
||||||
|
public:
|
||||||
|
QVector<KPluginMetaData> findPluginsById(const QString& name, const QStringList &dirs);
|
||||||
|
};
|
||||||
|
Cache plasmoidCache;
|
||||||
|
Cache dataengineCache;
|
||||||
|
Cache containmentactionCache;
|
||||||
};
|
};
|
||||||
|
|
||||||
QSet<QString> PluginLoaderPrivate::s_customCategories;
|
QSet<QString> PluginLoaderPrivate::s_customCategories;
|
||||||
@ -171,49 +179,8 @@ Applet *PluginLoader::loadApplet(const QString &name, uint appletId, const QVari
|
|||||||
appletId = ++AppletPrivate::s_maxAppletId;
|
appletId = ++AppletPrivate::s_maxAppletId;
|
||||||
}
|
}
|
||||||
|
|
||||||
const qint64 now = qRound64(QDateTime::currentMSecsSinceEpoch() / 1000.0);
|
// Need to pass the empty directory because it's where plasmoids used to be
|
||||||
bool useRuntimeCache = true;
|
const auto plugins = d->plasmoidCache.findPluginsById(name, { PluginLoaderPrivate::s_plasmoidsPluginDir, {} });
|
||||||
if (now - d->pluginCacheAge > d->maxCacheAge && d->pluginCacheAge != 0) {
|
|
||||||
// cache is old and we're not within a few seconds of startup anymore
|
|
||||||
useRuntimeCache = false;
|
|
||||||
d->pluginCache.clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (d->pluginCacheAge == 0) {
|
|
||||||
// Find all the plugins now, but only once
|
|
||||||
d->pluginCacheAge = now;
|
|
||||||
|
|
||||||
auto insertIntoCache = [this](const QString &pluginPath) {
|
|
||||||
KPluginMetaData metadata(pluginPath);
|
|
||||||
if (!metadata.isValid()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
d->pluginCache[metadata.pluginId()].append(metadata);
|
|
||||||
};
|
|
||||||
|
|
||||||
KPluginLoader::forEachPlugin(PluginLoaderPrivate::s_plasmoidsPluginDir, insertIntoCache);
|
|
||||||
// COMPAT CODE for applets installed into the toplevel plugins dir by mistake.
|
|
||||||
KPluginLoader::forEachPlugin(QString(), insertIntoCache);
|
|
||||||
}
|
|
||||||
|
|
||||||
//if name wasn't a path, pluginName == name
|
|
||||||
const QString pluginName = name.section(QLatin1Char('/'), -1);
|
|
||||||
|
|
||||||
QVector<KPluginMetaData> plugins;
|
|
||||||
|
|
||||||
if (useRuntimeCache) {
|
|
||||||
auto it = d->pluginCache.constFind(pluginName);
|
|
||||||
if (it != d->pluginCache.constEnd()) {
|
|
||||||
plugins = *it;
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
plugins = KPluginLoader::findPluginsById(PluginLoaderPrivate::s_plasmoidsPluginDir, pluginName);
|
|
||||||
// COMPAT CODE for applets installed into the toplevel plugins dir by mistake.
|
|
||||||
if (plugins.isEmpty()) {
|
|
||||||
plugins = KPluginLoader::findPluginsById(QString(), pluginName);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const KPackage::Package p = KPackage::PackageLoader::self()->loadPackage(QStringLiteral("Plasma/Applet"), name);
|
const KPackage::Package p = KPackage::PackageLoader::self()->loadPackage(QStringLiteral("Plasma/Applet"), name);
|
||||||
|
|
||||||
@ -263,19 +230,12 @@ DataEngine *PluginLoader::loadDataEngine(const QString &name)
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Look for C++ plugins first
|
// Look for C++ plugins first
|
||||||
auto filter = [&name](const KPluginMetaData &md) -> bool
|
const QVector<KPluginMetaData> plugins = d->dataengineCache.findPluginsById(name, {PluginLoaderPrivate::s_dataEnginePluginDir});
|
||||||
{
|
|
||||||
return md.pluginId() == name;
|
|
||||||
};
|
|
||||||
QVector<KPluginMetaData> plugins = KPluginLoader::findPlugins(PluginLoaderPrivate::s_dataEnginePluginDir, filter);
|
|
||||||
|
|
||||||
if (!plugins.isEmpty()) {
|
if (!plugins.isEmpty()) {
|
||||||
KPluginLoader loader(plugins.first().fileName());
|
KPluginLoader loader(plugins.constFirst().fileName());
|
||||||
const QVariantList argsWithMetaData = QVariantList() << loader.metaData().toVariantMap();
|
const QVariantList argsWithMetaData = QVariantList() << loader.metaData().toVariantMap();
|
||||||
KPluginFactory *factory = loader.factory();
|
KPluginFactory *factory = loader.factory();
|
||||||
if (factory) {
|
return factory ? factory->create<Plasma::DataEngine>(nullptr, argsWithMetaData) : nullptr;
|
||||||
engine = factory->create<Plasma::DataEngine>(nullptr, argsWithMetaData);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
if (engine) {
|
if (engine) {
|
||||||
return engine;
|
return engine;
|
||||||
@ -406,13 +366,7 @@ ContainmentActions *PluginLoader::loadContainmentActions(Containment *parent, co
|
|||||||
return actions;
|
return actions;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const QVector<KPluginMetaData> plugins = d->containmentactionCache.findPluginsById(name, {PluginLoaderPrivate::s_containmentActionsPluginDir});
|
||||||
// Look for C++ plugins first
|
|
||||||
auto filter = [&name](const KPluginMetaData &md) -> bool
|
|
||||||
{
|
|
||||||
return md.pluginId() == name;
|
|
||||||
};
|
|
||||||
QVector<KPluginMetaData> plugins = KPluginLoader::findPlugins(PluginLoaderPrivate::s_containmentActionsPluginDir, filter);
|
|
||||||
|
|
||||||
if (!plugins.isEmpty()) {
|
if (!plugins.isEmpty()) {
|
||||||
KPluginLoader loader(plugins.first().fileName());
|
KPluginLoader loader(plugins.first().fileName());
|
||||||
@ -891,5 +845,53 @@ bool PluginLoader::isPluginVersionCompatible(KPluginLoader &loader)
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
} // Plasma Namespace
|
QVector<KPluginMetaData> PluginLoaderPrivate::Cache::findPluginsById(const QString& name, const QStringList &dirs)
|
||||||
|
{
|
||||||
|
const qint64 now = qRound64(QDateTime::currentMSecsSinceEpoch() / 1000.0);
|
||||||
|
bool useRuntimeCache = true;
|
||||||
|
|
||||||
|
if (pluginCacheAge == 0) {
|
||||||
|
// Find all the plugins now, but only once
|
||||||
|
pluginCacheAge = now;
|
||||||
|
|
||||||
|
auto insertIntoCache = [this](const QString &pluginPath) {
|
||||||
|
KPluginMetaData metadata(pluginPath);
|
||||||
|
if (!metadata.isValid()) {
|
||||||
|
qWarning() << "invalid metadata" << pluginPath;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
plugins[metadata.pluginId()].append(metadata);
|
||||||
|
};
|
||||||
|
|
||||||
|
for (const QString &dir : dirs)
|
||||||
|
KPluginLoader::forEachPlugin(dir, insertIntoCache);
|
||||||
|
} else if (now - pluginCacheAge > maxCacheAge) {
|
||||||
|
// cache is old and we're not within a few seconds of startup anymore
|
||||||
|
useRuntimeCache = false;
|
||||||
|
plugins.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
//if name wasn't a path, pluginName == name
|
||||||
|
const QString pluginName = name.section(QLatin1Char('/'), -1);
|
||||||
|
|
||||||
|
QVector<KPluginMetaData> ret;
|
||||||
|
|
||||||
|
if (useRuntimeCache) {
|
||||||
|
auto it = plugins.constFind(pluginName);
|
||||||
|
if (it != plugins.constEnd()) {
|
||||||
|
ret = *it;
|
||||||
|
}
|
||||||
|
qCDebug(LOG_PLASMA) << "loading applet by name" << name << useRuntimeCache << ret.size();
|
||||||
|
} else {
|
||||||
|
|
||||||
|
for (const auto& dir : dirs) {
|
||||||
|
ret = KPluginLoader::findPluginsById(dir, pluginName);
|
||||||
|
if (!ret.isEmpty())
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // Plasma Namespace
|
||||||
|
@ -20,7 +20,7 @@
|
|||||||
#include "scripting/scriptengine.h"
|
#include "scripting/scriptengine.h"
|
||||||
|
|
||||||
#include <QDebug>
|
#include <QDebug>
|
||||||
#include <kservice.h>
|
#include <QGlobalStatic>
|
||||||
|
|
||||||
#include "applet.h"
|
#include "applet.h"
|
||||||
#include "dataengine.h"
|
#include "dataengine.h"
|
||||||
@ -32,6 +32,23 @@
|
|||||||
namespace Plasma
|
namespace Plasma
|
||||||
{
|
{
|
||||||
|
|
||||||
|
static QVector<KPluginMetaData> listEngines(Types::ComponentTypes types, std::function<bool(const KPluginMetaData &)> filter)
|
||||||
|
{
|
||||||
|
QVector<KPluginMetaData> ret;
|
||||||
|
const QVector<KPluginMetaData> plugins = KPluginLoader::findPlugins(QStringLiteral("plasma/scriptengines"));
|
||||||
|
ret.reserve(plugins.size());
|
||||||
|
for (const auto &plugin : plugins) {
|
||||||
|
if (!filter(plugin))
|
||||||
|
continue;
|
||||||
|
const QStringList componentTypes = KPluginMetaData::readStringList(plugins.first().rawData(), QStringLiteral("X-Plasma-ComponentTypes"));
|
||||||
|
if (((types & Types::AppletComponent) && componentTypes.contains(QStringLiteral("Applet")))
|
||||||
|
||((types & Types::DataEngineComponent) && componentTypes.contains(QStringLiteral("DataEngine")))) {
|
||||||
|
ret << plugin;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
ScriptEngine::ScriptEngine(QObject *parent)
|
ScriptEngine::ScriptEngine(QObject *parent)
|
||||||
: QObject(parent),
|
: QObject(parent),
|
||||||
d(nullptr)
|
d(nullptr)
|
||||||
@ -61,44 +78,42 @@ QString ScriptEngine::mainScript() const
|
|||||||
QStringList knownLanguages(Types::ComponentTypes types)
|
QStringList knownLanguages(Types::ComponentTypes types)
|
||||||
{
|
{
|
||||||
QStringList languages;
|
QStringList languages;
|
||||||
const QVector<KPluginMetaData> plugins = KPluginLoader::findPlugins(QStringLiteral("plasma/scriptengines"));
|
const QVector<KPluginMetaData> plugins = listEngines(types, [] (const KPluginMetaData &) -> bool { return true;});
|
||||||
|
|
||||||
foreach (const auto &plugin, plugins) {
|
for (const auto &plugin : plugins)
|
||||||
const QStringList componentTypes = KPluginMetaData::readStringList(plugins.first().rawData(), QStringLiteral("X-Plasma-ComponentTypes"));
|
languages << plugin.value(QStringLiteral("X-Plasma-API"));
|
||||||
if (((types & Types::AppletComponent) && componentTypes.contains(QStringLiteral("Applet")))
|
|
||||||
||((types & Types::DataEngineComponent) && componentTypes.contains(QStringLiteral("DataEngine")))) {
|
|
||||||
languages << plugin.value(QStringLiteral("X-Plasma-API"));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return languages;
|
return languages;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
typedef QHash<QString, QSharedPointer<KPluginLoader>> EngineCache;
|
||||||
|
Q_GLOBAL_STATIC(EngineCache, engines)
|
||||||
|
|
||||||
ScriptEngine *loadEngine(const QString &language, Types::ComponentType type, QObject *parent,
|
ScriptEngine *loadEngine(const QString &language, Types::ComponentType type, QObject *parent,
|
||||||
const QVariantList &args = QVariantList())
|
const QVariantList &args = QVariantList())
|
||||||
{
|
{
|
||||||
Q_UNUSED(parent);
|
Q_UNUSED(parent);
|
||||||
|
|
||||||
ScriptEngine *engine = nullptr;
|
{
|
||||||
|
auto it = engines->constFind(language);
|
||||||
|
if (it != engines->constEnd()) {
|
||||||
|
return (*it)->factory()->create<Plasma::ScriptEngine>(nullptr, args);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
ScriptEngine *engine = nullptr;
|
||||||
auto filter = [&language](const KPluginMetaData &md) -> bool
|
auto filter = [&language](const KPluginMetaData &md) -> bool
|
||||||
{
|
{
|
||||||
return md.value(QStringLiteral("X-Plasma-API")) == language;
|
return md.value(QStringLiteral("X-Plasma-API")) == language;
|
||||||
};
|
};
|
||||||
QVector<KPluginMetaData> plugins = KPluginLoader::findPlugins(QStringLiteral("plasma/scriptengines"), filter);
|
|
||||||
|
|
||||||
|
const QVector<KPluginMetaData> plugins = listEngines(type, filter);
|
||||||
if (!plugins.isEmpty()) {
|
if (!plugins.isEmpty()) {
|
||||||
const QStringList componentTypes = KPluginMetaData::readStringList(plugins.first().rawData(), QStringLiteral("X-Plasma-ComponentTypes"));
|
QSharedPointer<KPluginLoader> loader(new KPluginLoader(plugins.first().fileName()));
|
||||||
if (((type & Types::AppletComponent) && !componentTypes.contains(QStringLiteral("Applet")))
|
KPluginFactory *factory = loader->factory();
|
||||||
|| ((type & Types::DataEngineComponent) && !componentTypes.contains(QStringLiteral("DataEngine")))) {
|
|
||||||
|
|
||||||
qCWarning(LOG_PLASMA) << "ScriptEngine" << plugins.first().name() << "does not provide Applet or DataEngine components, returning empty.";
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
KPluginLoader loader(plugins.first().fileName());
|
|
||||||
KPluginFactory *factory = loader.factory();
|
|
||||||
if (factory) {
|
if (factory) {
|
||||||
engine = factory->create<Plasma::ScriptEngine>(nullptr, args);
|
engine = factory->create<Plasma::ScriptEngine>(nullptr, args);
|
||||||
|
engines->insert(language, loader);
|
||||||
} else {
|
} else {
|
||||||
qCWarning(LOG_PLASMA) << "Unable to load" << plugins.first().name() << "ScriptEngine";
|
qCWarning(LOG_PLASMA) << "Unable to load" << plugins.first().name() << "ScriptEngine";
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user