Commit ab5ad67b authored by Arjen Hiemstra's avatar Arjen Hiemstra
Browse files

Don't store Processes of a group in CGroup, just the pids

Instead, store the processes in the cgroup model, so we keep those
together. This avoids needing to keep track of which processes get
removed, instead we can just drop the cached processes when updating.
parent 1c1c6dd1
......@@ -42,12 +42,13 @@ public:
}
const QString processGroupId;
const KService::Ptr service;
QVector<Process *> processes;
QVector<pid_t> pids;
std::mutex pidsLock;
static KService::Ptr serviceFromAppId(const QString &appId);
static QRegularExpression s_appIdFromProcessGroupPattern;
static QString unescapeName(const QString &cgroupId);
QVector<Process *> procs;
};
class CGroupSystemInformation
......@@ -86,20 +87,22 @@ KService::Ptr KSysGuard::CGroup::service() const
return d->service;
}
QVector<Process *> CGroup::processes() const
QVector<pid_t> CGroup::pids() const
{
return d->procs;
std::lock_guard<std::mutex> lock{d->pidsLock};
return d->pids;
}
void CGroup::setProcesses(QVector<Process *> procs)
void CGroup::setPids(const QVector<pid_t>& pids)
{
d->procs = procs;
std::lock_guard<std::mutex> lock{d->pidsLock};
d->pids = pids;
}
void KSysGuard::CGroup::requestPids(std::function<void (const QVector<pid_t>&)> callback)
void CGroup::requestPids(QPointer<QObject> context, std::function<void()> callback)
{
QString path = cgroupSysBasePath() + d->processGroupId + QLatin1String("/cgroup.procs");
auto runnable = [path, callback]() {
auto runnable = [this, path, callback, context]() {
QFile pidFile(path);
pidFile.open(QFile::ReadOnly | QIODevice::Text);
QTextStream stream(&pidFile);
......@@ -110,8 +113,12 @@ void KSysGuard::CGroup::requestPids(std::function<void (const QVector<pid_t>&)>
pids.append(line.toLong());
line = stream.readLine();
}
setPids(pids);
callback(pids);
// Ensure we call the callback on the thread the context object lives on.
if (context) {
QMetaObject::invokeMethod(context, callback);
}
};
QThreadPool::globalInstance()->start(runnable);
}
......
......@@ -54,11 +54,10 @@ public:
KService::Ptr service() const;
/**
* @brief updates and fetches the list of processes associated with the process
* @brief The list of pids contained in this group.
* @return A Vector of pids
* @note This reloads the data on every fetch
*/
QVector<Process*> processes() const;
QVector<pid_t> pids() const;
/**
* Request fetching the list of processes associated with this cgroup.
......@@ -66,10 +65,11 @@ public:
* This is done in a separate thread. Once it has completed, \p callback is
* called with the list of pids of this cgroup.
*
* \param context An object that is used to track if the caller still exists.
* \param callback A callback that gets called once the list of pids has
* been retrieved.
*/
void requestPids(std::function<void(const QVector<pid_t>&)> callback);
void requestPids(QPointer<QObject> context, std::function<void()> callback);
/**
* Returns the base path to exposed cgroup information. Either /sys/fs/cgroup or /sys/fs/cgroup/unified as applicable
......@@ -86,10 +86,10 @@ private:
CGroup(const QString &id);
/**
* Set the updated processes of this cgroup object.
* Managed by CgroupDataModel exclusively
* Set the list of PIDs of this cgroup object.
*/
void setProcesses(QVector<Process*> procs);
void setPids(const QVector<pid_t> &pids);
QScopedPointer<CGroupPrivate> d;
friend class CGroupDataModel;
friend class CGroupDataModelPrivate;
......
......@@ -39,6 +39,8 @@ using namespace KSysGuard;
class KSysGuard::CGroupDataModelPrivate
{
public:
QVector<KSysGuard::Process*> processesFor(CGroup *app);
ExtendedProcesses *m_processes;
QTimer *m_updateTimer;
ProcessAttributeModel *m_attributeModel = nullptr;
......@@ -51,6 +53,7 @@ public:
QVector<CGroup *> m_cGroups; // an ordered list of unfiltered cgroups from our root
QHash<QString, CGroup *> m_cgroupMap; // all known cgroups from our root
QHash<QString, CGroup *> m_oldGroups;
QHash<CGroup*, QVector<Process*>> m_processMap; // cached mapping of cgroup to list of processes of that group
};
class GroupNameAttribute : public ProcessAttribute
......@@ -59,7 +62,8 @@ public:
GroupNameAttribute(QObject *parent) :
KSysGuard::ProcessAttribute(QStringLiteral("menuId"), i18nc("@title", "Desktop ID"), parent) {
}
QVariant cgroupData(CGroup *app) const override {
QVariant cgroupData(CGroup *app, const QVector<KSysGuard::Process*> &processes) const override {
Q_UNUSED(processes)
return app->service()->menuId();
}
};
......@@ -70,7 +74,8 @@ public:
AppIconAttribute(QObject *parent) :
KSysGuard::ProcessAttribute(QStringLiteral("iconName"), i18nc("@title", "Icon"), parent) {
}
QVariant cgroupData(CGroup *app) const override {
QVariant cgroupData(CGroup *app, const QVector<KSysGuard::Process*> &processes) const override {
Q_UNUSED(processes)
return app->service()->icon();
}
};
......@@ -81,7 +86,8 @@ public:
AppNameAttribute(QObject *parent) :
KSysGuard::ProcessAttribute(QStringLiteral("appName"), i18nc("@title", "Name"), parent) {
}
QVariant cgroupData(CGroup *app) const override {
QVariant cgroupData(CGroup *app, const QVector<KSysGuard::Process*> &processes) const override {
Q_UNUSED(processes)
return app->service()->name();
}
};
......@@ -235,12 +241,12 @@ QVariant CGroupDataModel::data(const QModelIndex &index, int role) const
case Qt::DisplayRole:
case ProcessDataModel::FormattedValue: {
KSysGuard::CGroup *app = reinterpret_cast< KSysGuard::CGroup* > (index.internalPointer());
const QVariant value = attribute->cgroupData(app);
const QVariant value = attribute->cgroupData(app, d->processesFor(app));
return KSysGuard::Formatter::formatValue(value, attribute->unit());
}
case ProcessDataModel::Value: {
KSysGuard::CGroup *app = reinterpret_cast< KSysGuard::CGroup* > (index.internalPointer());
const QVariant value = attribute->cgroupData(app);
const QVariant value = attribute->cgroupData(app, d->processesFor(app));
return value;
}
case ProcessDataModel::Attribute: {
......@@ -266,11 +272,7 @@ QVariant CGroupDataModel::data(const QModelIndex &index, int role) const
}
case ProcessDataModel::PIDs: {
KSysGuard::CGroup *app = reinterpret_cast< KSysGuard::CGroup* > (index.internalPointer());
QVariantList pidList;
std::transform(app->processes().constBegin(), app->processes().constEnd(), std::back_inserter(pidList), [](Process* process) -> QVariant {
return QVariant::fromValue(process->pid());
});
return pidList;
return QVariant::fromValue(app->pids());
}
}
return QVariant();
......@@ -366,6 +368,11 @@ void CGroupDataModel::update()
d->m_oldGroups = d->m_cgroupMap;
// updateAllProcesses will delete processes that no longer exist, so clear
// out our cache of the processes before that happens so we have no dangling
// processes.
d->m_processMap.clear();
// In an ideal world we would only the relevant process
// but Ksysguard::Processes doesn't handle that very well
d->m_processes->updateAllProcesses();
......@@ -399,23 +406,9 @@ void CGroupDataModel::update(CGroup *node)
// Update our own stat info
// This may trigger some dataChanged
node->requestPids([this, node](const QVector<pid_t> pids) {
// The callback is called from a different thread, to avoid needing to
// make the entire thing thread safe, we do the actual data update on the
// main thread.
QMetaObject::invokeMethod(this, [this, node, pids]() {
QVector<Process*> processes;
for (const pid_t pid : pids) {
auto proc = d->m_processes->getProcess(pid);
if (proc) { // as potentially this is racey with when kprocess fetched data
processes << proc;
}
}
node->setProcesses(processes);
auto row = d->m_cGroups.indexOf(node);
Q_EMIT dataChanged(index(row, 0, QModelIndex()), index(row, 0, QModelIndex()),{ ProcessDataModel::PIDs });
}, Qt::QueuedConnection);
node->requestPids(this, [this, node]() {
auto row = d->m_cGroups.indexOf(node);
Q_EMIT dataChanged(index(row, 0, QModelIndex()), index(row, 0, QModelIndex()));
});
const auto entries = dir.entryList(QDir::Dirs | QDir::NoDotAndDotDot);
......@@ -437,3 +430,23 @@ void CGroupDataModel::update(CGroup *node)
d->m_oldGroups.remove(childId);
}
}
QVector<Process*> CGroupDataModelPrivate::processesFor(CGroup *app)
{
if (m_processMap.contains(app)) {
return m_processMap.value(app);
}
QVector<Process*> result;
const auto pids = app->pids();
std::for_each(pids.begin(), pids.end(), [this, &result](pid_t pid) {
auto process = m_processes->getProcess(pid);
if (process) {
result.append(process);
}
});
m_processMap.insert(app, result);
return result;
}
......@@ -164,9 +164,15 @@ void ProcessAttribute::clearData(KSysGuard::Process *process)
emit dataChanged(process);
}
QVariant ProcessAttribute::cgroupData(KSysGuard::CGroup *cgroup) const
QVariant ProcessAttribute::cgroupData(KSysGuard::CGroup *cgroup, const QVector<KSysGuard::Process*> &groupProcesses) const
{
qreal total = std::accumulate(cgroup->processes().constBegin(), cgroup->processes().constEnd(), 0.0, [this](qreal total, KSysGuard::Process *process) {
Q_UNUSED(cgroup)
if (groupProcesses.isEmpty()) {
return QVariant{};
}
qreal total = std::accumulate(groupProcesses.constBegin(), groupProcesses.constEnd(), 0.0, [this](qreal total, KSysGuard::Process *process) {
return total + data(process).toDouble();
});
return QVariant(total);
......
......@@ -109,7 +109,7 @@ public:
*/
void clearData(KSysGuard::Process *process);
virtual QVariant cgroupData(KSysGuard::CGroup *cgroup) const;
virtual QVariant cgroupData(KSysGuard::CGroup *cgroup, const QVector<KSysGuard::Process*> &groupProcesses = {}) const;
Q_SIGNALS:
void dataChanged(KSysGuard::Process *process);
......
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