QtDBus: finish all pending call with error if disconnected
libdbus will send a local signal if connection gets disconnected. When this happens, end all pending calls with QDBusError::Disconnected. Task-number: QTBUG-51649 Change-Id: I5c7d2a468bb5da746d0c0e53e458c1e376f186a9 Reviewed-by: Thiago Macieira <thiago.macieira@intel.com>bb10
parent
8a7311532b
commit
9be4ee5202
|
|
@ -99,9 +99,11 @@ typedef dbus_uint32_t dbus_bool_t;
|
|||
/* dbus-shared.h */
|
||||
#define DBUS_SERVICE_DBUS "org.freedesktop.DBus"
|
||||
#define DBUS_PATH_DBUS "/org/freedesktop/DBus"
|
||||
#define DBUS_PATH_LOCAL "/org/freedesktop/DBus/Local"
|
||||
#define DBUS_INTERFACE_DBUS "org.freedesktop.DBus"
|
||||
#define DBUS_INTERFACE_INTROSPECTABLE "org.freedesktop.DBus.Introspectable"
|
||||
#define DBUS_INTERFACE_PROPERTIES "org.freedesktop.DBus.Properties"
|
||||
#define DBUS_INTERFACE_LOCAL "org.freedesktop.DBus.Local"
|
||||
|
||||
#define DBUS_NAME_FLAG_ALLOW_REPLACEMENT 0x1 /**< Allow another service to become the primary owner if requested */
|
||||
#define DBUS_NAME_FLAG_REPLACE_EXISTING 0x2 /**< Request to replace the current primary owner */
|
||||
|
|
|
|||
|
|
@ -260,6 +260,8 @@ private:
|
|||
|
||||
QString getNameOwnerNoCache(const QString &service);
|
||||
|
||||
void watchForDBusDisconnection();
|
||||
|
||||
void _q_newConnection(QDBusConnectionPrivate *newConnection);
|
||||
|
||||
protected:
|
||||
|
|
@ -279,6 +281,7 @@ private slots:
|
|||
void serviceOwnerChangedNoLock(const QString &name, const QString &oldOwner, const QString &newOwner);
|
||||
void registerServiceNoLock(const QString &serviceName);
|
||||
void unregisterServiceNoLock(const QString &serviceName);
|
||||
void handleDBusDisconnection();
|
||||
|
||||
signals:
|
||||
void dispatchStatusChanged();
|
||||
|
|
|
|||
|
|
@ -1121,6 +1121,12 @@ void QDBusConnectionPrivate::closeConnection()
|
|||
rootNode.children.clear(); // free resources
|
||||
}
|
||||
|
||||
void QDBusConnectionPrivate::handleDBusDisconnection()
|
||||
{
|
||||
while (!pendingCalls.isEmpty())
|
||||
processFinishedCall(pendingCalls.first());
|
||||
}
|
||||
|
||||
void QDBusConnectionPrivate::checkThread()
|
||||
{
|
||||
Q_ASSERT(thread() == QDBusConnectionManager::instance());
|
||||
|
|
@ -1646,6 +1652,19 @@ void QDBusConnectionPrivate::handleSignal(const QDBusMessage& msg)
|
|||
handleSignal(key, msg); // third try
|
||||
}
|
||||
|
||||
void QDBusConnectionPrivate::watchForDBusDisconnection()
|
||||
{
|
||||
SignalHook hook;
|
||||
// Initialize the hook for Disconnected signal
|
||||
hook.service.clear(); // org.freedesktop.DBus.Local.Disconnected uses empty service name
|
||||
hook.path = QDBusUtil::dbusPathLocal();
|
||||
hook.obj = this;
|
||||
hook.params << QMetaType::Void;
|
||||
hook.midx = staticMetaObject.indexOfSlot("handleDBusDisconnection()");
|
||||
Q_ASSERT(hook.midx != -1);
|
||||
signalHooks.insert(QLatin1String("Disconnected:" DBUS_INTERFACE_LOCAL), hook);
|
||||
}
|
||||
|
||||
void QDBusConnectionPrivate::setServer(QDBusServer *object, DBusServer *s, const QDBusErrorInternal &error)
|
||||
{
|
||||
mode = ServerMode;
|
||||
|
|
@ -1711,6 +1730,8 @@ void QDBusConnectionPrivate::setPeer(DBusConnection *c, const QDBusErrorInternal
|
|||
qDBusSignalFilter,
|
||||
this, 0);
|
||||
|
||||
watchForDBusDisconnection();
|
||||
|
||||
QMetaObject::invokeMethod(this, "doDispatch", Qt::QueuedConnection);
|
||||
}
|
||||
|
||||
|
|
@ -1787,6 +1808,8 @@ void QDBusConnectionPrivate::setConnection(DBusConnection *dbc, const QDBusError
|
|||
Q_ASSERT(hook.midx != -1);
|
||||
signalHooks.insert(QLatin1String("NameOwnerChanged:" DBUS_INTERFACE_DBUS), hook);
|
||||
|
||||
watchForDBusDisconnection();
|
||||
|
||||
qDBusDebug() << this << ": connected successfully";
|
||||
|
||||
// schedule a dispatch:
|
||||
|
|
@ -1813,10 +1836,16 @@ void QDBusConnectionPrivate::processFinishedCall(QDBusPendingCallPrivate *call)
|
|||
|
||||
QDBusMessage &msg = call->replyMessage;
|
||||
if (call->pending) {
|
||||
// decode the message
|
||||
DBusMessage *reply = q_dbus_pending_call_steal_reply(call->pending);
|
||||
msg = QDBusMessagePrivate::fromDBusMessage(reply, connection->capabilities);
|
||||
q_dbus_message_unref(reply);
|
||||
// when processFinishedCall is called and pending call is not completed,
|
||||
// it means we received disconnected signal from libdbus
|
||||
if (q_dbus_pending_call_get_completed(call->pending)) {
|
||||
// decode the message
|
||||
DBusMessage *reply = q_dbus_pending_call_steal_reply(call->pending);
|
||||
msg = QDBusMessagePrivate::fromDBusMessage(reply, connection->capabilities);
|
||||
q_dbus_message_unref(reply);
|
||||
} else {
|
||||
msg = QDBusMessage::createError(QDBusError::Disconnected, QDBusUtil::disconnectedErrorMessage());
|
||||
}
|
||||
}
|
||||
qDBusDebug() << connection << "got message reply:" << msg;
|
||||
|
||||
|
|
@ -2116,8 +2145,8 @@ void QDBusConnectionPrivate::sendInternal(QDBusPendingCallPrivate *pcall, void *
|
|||
pcall->pending = pending;
|
||||
q_dbus_pending_call_set_notify(pending, qDBusResultReceived, pcall, 0);
|
||||
|
||||
// DBus won't notify us when a peer disconnects so we need to track these ourselves
|
||||
if (mode == QDBusConnectionPrivate::PeerMode)
|
||||
// DBus won't notify us when a peer disconnects or server terminates so we need to track these ourselves
|
||||
if (mode == QDBusConnectionPrivate::PeerMode || mode == QDBusConnectionPrivate::ClientMode)
|
||||
pendingCalls.append(pcall);
|
||||
|
||||
return;
|
||||
|
|
|
|||
|
|
@ -155,6 +155,8 @@ namespace QDBusUtil
|
|||
{ return QStringLiteral(DBUS_SERVICE_DBUS); }
|
||||
inline QString dbusPath()
|
||||
{ return QStringLiteral(DBUS_PATH_DBUS); }
|
||||
inline QString dbusPathLocal()
|
||||
{ return QStringLiteral(DBUS_PATH_LOCAL); }
|
||||
inline QString dbusInterface()
|
||||
{
|
||||
// it's the same string, but just be sure
|
||||
|
|
|
|||
|
|
@ -1218,6 +1218,27 @@ void tst_QDBusConnection::callVirtualObjectLocal()
|
|||
QCOMPARE(obj.replyArguments, subPathReply.arguments());
|
||||
}
|
||||
|
||||
void tst_QDBusConnection::pendingCallWhenDisconnected()
|
||||
{
|
||||
if (!QCoreApplication::instance())
|
||||
QSKIP("Test requires a QCoreApplication");
|
||||
|
||||
QDBusServer *server = new QDBusServer;
|
||||
QDBusConnection con = QDBusConnection::connectToPeer(server->address(), "disconnect");
|
||||
QTestEventLoop::instance().enterLoop(2);
|
||||
QVERIFY(con.isConnected());
|
||||
QDBusMessage message = QDBusMessage::createMethodCall("", "/", QString(), "method");
|
||||
QDBusPendingCall reply = con.asyncCall(message);
|
||||
|
||||
delete server;
|
||||
|
||||
QTestEventLoop::instance().enterLoop(2);
|
||||
QVERIFY(!con.isConnected());
|
||||
QVERIFY(reply.isFinished());
|
||||
QVERIFY(reply.isError());
|
||||
QVERIFY(reply.error().type() == QDBusError::Disconnected);
|
||||
}
|
||||
|
||||
QString MyObject::path;
|
||||
QString MyObjectWithoutInterface::path;
|
||||
QString MyObjectWithoutInterface::interface;
|
||||
|
|
|
|||
|
|
@ -121,6 +121,7 @@ private slots:
|
|||
void registerVirtualObject();
|
||||
void callVirtualObject();
|
||||
void callVirtualObjectLocal();
|
||||
void pendingCallWhenDisconnected();
|
||||
|
||||
public:
|
||||
QString serviceName() const { return "org.qtproject.Qt.Autotests.QDBusConnection"; }
|
||||
|
|
|
|||
Loading…
Reference in New Issue