Commit 757b5e8c authored by Jolene K's avatar Jolene K Committed by Nate Graham
Browse files

systemtray: Fix race in DBusServiceObserver

When a service appears right after the ListNames() call finished, but
before the QDBusPendingCallWatcher is set up, the ::serviceRegistered
signal will be queued before the ListNames() response. As a result,
the service will be ignored by DBusServiceObserver.

Switching to QDBusInterface::callWithCallback() ensures that the
result slot is queued as soon as the response arrives, thus avoiding
the race.

Credits go to Sam Edwards for identifying the problem in
https://bugs.kde.org/show_bug.cgi?id=422111#c69

BUG: 422111


(cherry picked from commit 3751f49c)
parent f4b7cbb6
Pipeline #165874 passed with stage
in 10 minutes and 58 seconds
......@@ -14,7 +14,6 @@
#include <QDBusConnection>
#include <QDBusConnectionInterface>
#include <QDBusPendingReply>
#include <QDBusServiceWatcher>
DBusServiceObserver::DBusServiceObserver(QPointer<SystemTraySettings> settings, QObject *parent)
......@@ -90,8 +89,8 @@ bool DBusServiceObserver::isDBusActivable(const QString &pluginId)
* This works as follows:
* - we collect a list of plugins and related services in m_dbusActivatableTasks
* - we query DBus for the list of services, async (initDBusActivatables())
* - we go over that list, adding tasks when a service and plugin match (serviceNameFetchFinished())
* - we start watching for new services, and do the same (serviceNameFetchFinished())
* - we go over that list, adding tasks when a service and plugin match ({session,system}BusNameFetchFinished())
* - we start watching for new services, and do the same (serviceRegistered())
* - whenever a service is gone, we check whether to unload a Plasmoid (serviceUnregistered())
*
* Order of events has to be:
......@@ -103,34 +102,47 @@ bool DBusServiceObserver::isDBusActivable(const QString &pluginId)
void DBusServiceObserver::initDBusActivatables()
{
// fetch list of existing services
QDBusPendingCall async = QDBusConnection::sessionBus().interface()->asyncCall(QStringLiteral("ListNames"));
QDBusPendingCallWatcher *callWatcher = new QDBusPendingCallWatcher(async, this);
connect(callWatcher, &QDBusPendingCallWatcher::finished, [=](QDBusPendingCallWatcher *callWatcher) {
serviceNameFetchFinished(callWatcher);
m_dbusSessionServiceNamesFetched = true;
});
QDBusConnection::sessionBus().interface()->callWithCallback(QStringLiteral("ListNames"),
QList<QVariant>(),
this,
SLOT(sessionBusNameFetchFinished(QStringList)),
SLOT(sessionBusNameFetchError(QDBusError)));
QDBusConnection::systemBus().interface()->callWithCallback(QStringLiteral("ListNames"),
QList<QVariant>(),
this,
SLOT(systemBusNameFetchFinished(QStringList)),
SLOT(systemBusNameFetchError(QDBusError)));
}
QDBusPendingCall systemAsync = QDBusConnection::systemBus().interface()->asyncCall(QStringLiteral("ListNames"));
QDBusPendingCallWatcher *systemCallWatcher = new QDBusPendingCallWatcher(systemAsync, this);
connect(systemCallWatcher, &QDBusPendingCallWatcher::finished, [=](QDBusPendingCallWatcher *callWatcher) {
serviceNameFetchFinished(callWatcher);
m_dbusSystemServiceNamesFetched = true;
});
void DBusServiceObserver::sessionBusNameFetchFinished(const QStringList &list)
{
for (const QString &serviceName : list) {
serviceRegistered(serviceName);
}
m_dbusSessionServiceNamesFetched = true;
}
void DBusServiceObserver::serviceNameFetchFinished(QDBusPendingCallWatcher *watcher)
void DBusServiceObserver::sessionBusNameFetchError(const QDBusError &error)
{
QDBusPendingReply<QStringList> propsReply = *watcher;
watcher->deleteLater();
if (propsReply.isError()) {
qCWarning(SYSTEM_TRAY) << "Could not get list of available D-Bus services";
} else {
const auto propsReplyValue = propsReply.value();
for (const QString &serviceName : propsReplyValue) {
serviceRegistered(serviceName);
}
qCWarning(SYSTEM_TRAY) << "Could not get list of available D-Bus services on the session bus:"
<< error.name() << ":" << error.message();
}
void DBusServiceObserver::systemBusNameFetchFinished(const QStringList &list)
{
for (const QString &serviceName : list) {
serviceRegistered(serviceName);
}
m_dbusSystemServiceNamesFetched = true;
}
void DBusServiceObserver::systemBusNameFetchError(const QDBusError &error)
{
qCWarning(SYSTEM_TRAY) << "Could not get list of available D-Bus services on the system bus:"
<< error.name() << ":" << error.message();
}
void DBusServiceObserver::serviceRegistered(const QString &service)
......
......@@ -11,10 +11,10 @@
#include <QObject>
#include <QPointer>
#include <QRegExp>
#include <QDBusError>
class KPluginMetaData;
class SystemTraySettings;
class QDBusPendingCallWatcher;
class QDBusServiceWatcher;
/**
......@@ -37,8 +37,13 @@ Q_SIGNALS:
public Q_SLOTS:
void initDBusActivatables();
private Q_SLOTS:
void sessionBusNameFetchFinished(const QStringList &list);
void systemBusNameFetchFinished(const QStringList &list);
void sessionBusNameFetchError(const QDBusError &error);
void systemBusNameFetchError(const QDBusError &error);
private:
void serviceNameFetchFinished(QDBusPendingCallWatcher *watcher);
void serviceRegistered(const QString &service);
void serviceUnregistered(const QString &service);
......
Supports Markdown
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