Commit ff834fc9 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

BUG: 422111
FIXED-IN: 5.24.5
parent 1faeaf2e
Pipeline #165866 passed with stage
in 12 minutes and 31 seconds
......@@ -14,7 +14,6 @@
#include <QDBusConnection>
#include <QDBusConnectionInterface>
#include <QDBusPendingReply>
#include <QDBusServiceWatcher>
DBusServiceObserver::DBusServiceObserver(const 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) {
m_dbusSessionServiceNamesFetched = true;
QDBusPendingCall systemAsync = QDBusConnection::systemBus().interface()->asyncCall(QStringLiteral("ListNames"));
QDBusPendingCallWatcher *systemCallWatcher = new QDBusPendingCallWatcher(systemAsync, this);
connect(systemCallWatcher, &QDBusPendingCallWatcher::finished, [=](QDBusPendingCallWatcher *callWatcher) {
m_dbusSystemServiceNamesFetched = true;
void DBusServiceObserver::sessionBusNameFetchFinished(const QStringList &list)
for (const QString &serviceName : list) {
m_dbusSessionServiceNamesFetched = true;
void DBusServiceObserver::serviceNameFetchFinished(QDBusPendingCallWatcher *watcher)
void DBusServiceObserver::sessionBusNameFetchError(const QDBusError &error)
QDBusPendingReply<QStringList> propsReply = *watcher;
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) {
qCWarning(SYSTEM_TRAY) << "Could not get list of available D-Bus services on the session bus:"
<< << ":" << error.message();
void DBusServiceObserver::systemBusNameFetchFinished(const QStringList &list)
for (const QString &serviceName : list) {
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.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);
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