diff options
Diffstat (limited to 'src/dbus/qdbusintegrator.cpp')
-rw-r--r-- | src/dbus/qdbusintegrator.cpp | 672 |
1 files changed, 285 insertions, 387 deletions
diff --git a/src/dbus/qdbusintegrator.cpp b/src/dbus/qdbusintegrator.cpp index cce8b9c28d..f6221d51b6 100644 --- a/src/dbus/qdbusintegrator.cpp +++ b/src/dbus/qdbusintegrator.cpp @@ -1,6 +1,7 @@ /**************************************************************************** ** ** Copyright (C) 2015 The Qt Company Ltd. +** Copyright (C) 2015 Intel Corporation. ** Contact: http://www.qt.io/licensing/ ** ** This file is part of the QtDBus module of the Qt Toolkit. @@ -52,6 +53,7 @@ #include "qdbusmetatype_p.h" #include "qdbusabstractadaptor.h" #include "qdbusabstractadaptor_p.h" +#include "qdbusserver.h" #include "qdbusutil_p.h" #include "qdbusvirtualobject.h" #include "qdbusmessage_p.h" @@ -92,12 +94,7 @@ static inline QDebug operator<<(QDebug dbg, const QDBusConnectionPrivate *conn) << "ptr=" << (const void*)conn << ", name=" << conn->name << ", baseService=" << conn->baseService - << ", thread="; - if (conn->thread() == QThread::currentThread()) - dbg.nospace() << "same thread"; - else - dbg.nospace() << conn->thread(); - dbg.nospace() << ')'; + << ')'; return dbg; } @@ -131,7 +128,6 @@ extern "C" { // libdbus-1 callbacks -static bool qDBusRealAddTimeout(QDBusConnectionPrivate *d, DBusTimeout *timeout, int ms); static dbus_bool_t qDBusAddTimeout(DBusTimeout *timeout, void *data) { Q_ASSERT(timeout); @@ -140,29 +136,17 @@ static dbus_bool_t qDBusAddTimeout(DBusTimeout *timeout, void *data) // qDebug("addTimeout %d", q_dbus_timeout_get_interval(timeout)); QDBusConnectionPrivate *d = static_cast<QDBusConnectionPrivate *>(data); + Q_ASSERT(QThread::currentThread() == d->thread()); - if (!q_dbus_timeout_get_enabled(timeout)) - return true; + // we may get called from qDBusToggleTimeout + if (Q_UNLIKELY(!q_dbus_timeout_get_enabled(timeout))) + return false; QDBusDispatchLocker locker(AddTimeoutAction, d); - if (QCoreApplication::instance() && QThread::currentThread() == d->thread()) { - // correct thread - return qDBusRealAddTimeout(d, timeout, q_dbus_timeout_get_interval(timeout)); - } else { - // wrong thread: sync back - QDBusConnectionCallbackEvent *ev = new QDBusConnectionCallbackEvent; - ev->subtype = QDBusConnectionCallbackEvent::AddTimeout; - d->timeoutsPendingAdd.append(qMakePair(timeout, q_dbus_timeout_get_interval(timeout))); - d->postEventToThread(AddTimeoutAction, d, ev); - return true; - } -} - -static bool qDBusRealAddTimeout(QDBusConnectionPrivate *d, DBusTimeout *timeout, int ms) -{ - Q_ASSERT(d->timeouts.keys(timeout).isEmpty()); + Q_ASSERT(d->timeouts.key(timeout, 0) == 0); - int timerId = d->startTimer(ms); + int timerId = d->startTimer(q_dbus_timeout_get_interval(timeout)); + Q_ASSERT_X(timerId, "QDBusConnection", "Failed to start a timer"); if (!timerId) return false; @@ -178,33 +162,14 @@ static void qDBusRemoveTimeout(DBusTimeout *timeout, void *data) // qDebug("removeTimeout"); QDBusConnectionPrivate *d = static_cast<QDBusConnectionPrivate *>(data); + Q_ASSERT(QThread::currentThread() == d->thread()); QDBusDispatchLocker locker(RemoveTimeoutAction, d); - // is it pending addition? - QDBusConnectionPrivate::PendingTimeoutList::iterator pit = d->timeoutsPendingAdd.begin(); - while (pit != d->timeoutsPendingAdd.end()) { - if (pit->first == timeout) - pit = d->timeoutsPendingAdd.erase(pit); - else - ++pit; - } - - // is it a running timer? - bool correctThread = QCoreApplication::instance() && QThread::currentThread() == d->thread(); QDBusConnectionPrivate::TimeoutHash::iterator it = d->timeouts.begin(); while (it != d->timeouts.end()) { if (it.value() == timeout) { - if (correctThread) { - // correct thread - d->killTimer(it.key()); - } else { - // incorrect thread or no application, post an event for later - QDBusConnectionCallbackEvent *ev = new QDBusConnectionCallbackEvent; - ev->subtype = QDBusConnectionCallbackEvent::KillTimer; - ev->timerId = it.key(); - d->postEventToThread(KillTimerAction, d, ev); - } + d->killTimer(it.key()); it = d->timeouts.erase(it); break; } else { @@ -224,52 +189,33 @@ static void qDBusToggleTimeout(DBusTimeout *timeout, void *data) qDBusAddTimeout(timeout, data); } -static bool qDBusRealAddWatch(QDBusConnectionPrivate *d, DBusWatch *watch, int flags, int fd); static dbus_bool_t qDBusAddWatch(DBusWatch *watch, void *data) { Q_ASSERT(watch); Q_ASSERT(data); QDBusConnectionPrivate *d = static_cast<QDBusConnectionPrivate *>(data); + Q_ASSERT(QThread::currentThread() == d->thread()); int flags = q_dbus_watch_get_flags(watch); int fd = q_dbus_watch_get_unix_fd(watch); - if (QCoreApplication::instance() && QThread::currentThread() == d->thread()) { - return qDBusRealAddWatch(d, watch, flags, fd); - } else { - QDBusConnectionCallbackEvent *ev = new QDBusConnectionCallbackEvent; - ev->subtype = QDBusConnectionCallbackEvent::AddWatch; - ev->watch = watch; - ev->fd = fd; - ev->extra = flags; - d->postEventToThread(AddWatchAction, d, ev); - return true; - } -} - -static bool qDBusRealAddWatch(QDBusConnectionPrivate *d, DBusWatch *watch, int flags, int fd) -{ QDBusConnectionPrivate::Watcher watcher; QDBusDispatchLocker locker(AddWatchAction, d); if (flags & DBUS_WATCH_READABLE) { //qDebug("addReadWatch %d", fd); watcher.watch = watch; - if (QCoreApplication::instance()) { - watcher.read = new QSocketNotifier(fd, QSocketNotifier::Read, d); - watcher.read->setEnabled(q_dbus_watch_get_enabled(watch)); - d->connect(watcher.read, SIGNAL(activated(int)), SLOT(socketRead(int))); - } + watcher.read = new QSocketNotifier(fd, QSocketNotifier::Read, d); + watcher.read->setEnabled(q_dbus_watch_get_enabled(watch)); + d->connect(watcher.read, SIGNAL(activated(int)), SLOT(socketRead(int))); } if (flags & DBUS_WATCH_WRITABLE) { //qDebug("addWriteWatch %d", fd); watcher.watch = watch; - if (QCoreApplication::instance()) { - watcher.write = new QSocketNotifier(fd, QSocketNotifier::Write, d); - watcher.write->setEnabled(q_dbus_watch_get_enabled(watch)); - d->connect(watcher.write, SIGNAL(activated(int)), SLOT(socketWrite(int))); - } + watcher.write = new QSocketNotifier(fd, QSocketNotifier::Write, d); + watcher.write->setEnabled(q_dbus_watch_get_enabled(watch)); + d->connect(watcher.write, SIGNAL(activated(int)), SLOT(socketWrite(int))); } d->watchers.insertMulti(fd, watcher); @@ -284,23 +230,15 @@ static void qDBusRemoveWatch(DBusWatch *watch, void *data) //qDebug("remove watch"); QDBusConnectionPrivate *d = static_cast<QDBusConnectionPrivate *>(data); + Q_ASSERT(QThread::currentThread() == d->thread()); int fd = q_dbus_watch_get_unix_fd(watch); QDBusDispatchLocker locker(RemoveWatchAction, d); QDBusConnectionPrivate::WatcherHash::iterator i = d->watchers.find(fd); while (i != d->watchers.end() && i.key() == fd) { if (i.value().watch == watch) { - if (QCoreApplication::instance() && QThread::currentThread() == d->thread()) { - // correct thread, delete the socket notifiers - delete i.value().read; - delete i.value().write; - } else { - // incorrect thread or no application, use delete later - if (i->read) - i->read->deleteLater(); - if (i->write) - i->write->deleteLater(); - } + delete i.value().read; + delete i.value().write; i = d->watchers.erase(i); } else { ++i; @@ -308,28 +246,15 @@ static void qDBusRemoveWatch(DBusWatch *watch, void *data) } } -static void qDBusRealToggleWatch(QDBusConnectionPrivate *d, DBusWatch *watch, int fd); static void qDBusToggleWatch(DBusWatch *watch, void *data) { Q_ASSERT(watch); Q_ASSERT(data); QDBusConnectionPrivate *d = static_cast<QDBusConnectionPrivate *>(data); + Q_ASSERT(QThread::currentThread() == d->thread()); int fd = q_dbus_watch_get_unix_fd(watch); - if (QCoreApplication::instance() && QThread::currentThread() == d->thread()) { - qDBusRealToggleWatch(d, watch, fd); - } else { - QDBusConnectionCallbackEvent *ev = new QDBusConnectionCallbackEvent; - ev->subtype = QDBusConnectionCallbackEvent::ToggleWatch; - ev->watch = watch; - ev->fd = fd; - d->postEventToThread(ToggleWatchAction, d, ev); - } -} - -static void qDBusRealToggleWatch(QDBusConnectionPrivate *d, DBusWatch *watch, int fd) -{ QDBusDispatchLocker locker(ToggleWatchAction, d); QDBusConnectionPrivate::WatcherHash::iterator i = d->watchers.find(fd); @@ -355,18 +280,8 @@ static void qDBusUpdateDispatchStatus(DBusConnection *connection, DBusDispatchSt Q_ASSERT(connection); Q_UNUSED(connection); QDBusConnectionPrivate *d = static_cast<QDBusConnectionPrivate *>(data); - - static int slotId; // 0 is QObject::deleteLater() - if (!slotId) { - // it's ok to do this: there's no race condition because the store is atomic - // and we always set to the same value - slotId = QDBusConnectionPrivate::staticMetaObject.indexOfSlot("doDispatch()"); - } - - //qDBusDebug() << "Updating dispatcher status" << slotId; if (new_status == DBUS_DISPATCH_DATA_REMAINS) - QDBusConnectionPrivate::staticMetaObject.method(slotId). - invoke(d, Qt::QueuedConnection); + emit d->dispatchStatusChanged(); } static void qDBusNewConnection(DBusServer *server, DBusConnection *connection, void *data) @@ -393,10 +308,14 @@ static void qDBusNewConnection(DBusServer *server, DBusConnection *connection, v QDBusErrorInternal error; newConnection->setPeer(connection, error); - QDBusConnection retval = QDBusConnectionPrivate::q(newConnection); + // this is a queued connection and will resume in the QDBusServer's thread + emit serverConnection->newServerConnection(newConnection); +} - // make QDBusServer emit the newConnection signal - serverConnection->serverConnection(retval); +void QDBusConnectionPrivate::_q_newConnection(QDBusConnectionPrivate *newConnection) +{ + Q_ASSERT(mode == ServerMode); + emit serverObject->newConnection(QDBusConnectionPrivate::q(newConnection)); } } // extern "C" @@ -521,11 +440,27 @@ static QObject *findChildObject(const QDBusConnectionPrivate::ObjectTreeNode *ro return 0; } -static bool shouldWatchService(const QString &service) +static QStringList matchArgsForService(const QString &service, QDBusServiceWatcher::WatchMode mode) { - return !service.isEmpty() && !service.startsWith(QLatin1Char(':')); + QStringList matchArgs; + matchArgs << service; + + switch (mode) { + case QDBusServiceWatcher::WatchForOwnerChange: + break; + + case QDBusServiceWatcher::WatchForRegistration: + matchArgs << QString::fromLatin1("", 0); + break; + + case QDBusServiceWatcher::WatchForUnregistration: + matchArgs << QString() << QString::fromLatin1("", 0); + break; + } + return matchArgs; } + extern Q_DBUS_EXPORT void qDBusAddSpyHook(QDBusSpyHook); void qDBusAddSpyHook(QDBusSpyHook hook) { @@ -561,6 +496,11 @@ bool QDBusConnectionPrivate::handleMessage(const QDBusMessage &amsg) if (!ref.load()) return false; + if (!dispatchEnabled && !QDBusMessagePrivate::isLocal(amsg)) { + // queue messages only, we'll handle them later + pendingMessages << amsg; + return amsg.type() == QDBusMessage::MethodCallMessage; + } switch (amsg.type()) { case QDBusMessage::SignalMessage: @@ -755,6 +695,20 @@ static int findSlot(const QMetaObject *mo, const QByteArray &name, int flags, return -1; } +/*! + \internal + Enables or disables the delivery of incoming method calls and signals. If + \a enable is true, this will also cause any queued, pending messages to be + delivered. + */ +void QDBusConnectionPrivate::setDispatchEnabled(bool enable) +{ + QDBusDispatchLocker locker(SetDispatchEnabledAction, this); + dispatchEnabled = enable; + if (enable) + emit dispatchStatusChanged(); +} + static QDBusCallDeliveryEvent * const DIRECT_DELIVERY = (QDBusCallDeliveryEvent *)1; QDBusCallDeliveryEvent* QDBusConnectionPrivate::prepareReply(QDBusConnectionPrivate *target, @@ -952,14 +906,19 @@ void QDBusConnectionPrivate::deliverCall(QObject *object, int /*flags*/, const Q } // output arguments + const int numMetaTypes = metaTypes.count(); QVariantList outputArgs; void *null = 0; if (metaTypes[0] != QMetaType::Void && metaTypes[0] != QMetaType::UnknownType) { + outputArgs.reserve(numMetaTypes - i + 1); QVariant arg(metaTypes[0], null); outputArgs.append( arg ); params[0] = const_cast<void*>(outputArgs.at( outputArgs.count() - 1 ).constData()); + } else { + outputArgs.reserve(numMetaTypes - i); } - for ( ; i < metaTypes.count(); ++i) { + + for ( ; i < numMetaTypes; ++i) { QVariant arg(metaTypes[i], null); outputArgs.append( arg ); params.append(const_cast<void*>(outputArgs.at( outputArgs.count() - 1 ).constData())); @@ -1004,9 +963,10 @@ extern bool qDBusInitThreads(); QDBusConnectionPrivate::QDBusConnectionPrivate(QObject *p) : QObject(p), ref(1), capabilities(0), mode(InvalidMode), busService(0), - dispatchLock(QMutex::Recursive), connection(0), server(0), + dispatchLock(QMutex::Recursive), connection(0), rootNode(QString(QLatin1Char('/'))), - anonymousAuthenticationAllowed(false) + anonymousAuthenticationAllowed(false), + dispatchEnabled(true) { static const bool threads = q_dbus_threads_init_default(); if (::isDebugging == -1) @@ -1019,6 +979,14 @@ QDBusConnectionPrivate::QDBusConnectionPrivate(QObject *p) #endif QDBusMetaTypeId::init(); + connect(this, &QDBusConnectionPrivate::dispatchStatusChanged, + this, &QDBusConnectionPrivate::doDispatch, Qt::QueuedConnection); + connect(this, &QDBusConnectionPrivate::messageNeedsSending, + this, &QDBusConnectionPrivate::sendInternal); + connect(this, &QDBusConnectionPrivate::signalNeedsConnecting, + this, &QDBusConnectionPrivate::addSignalHook, Qt::BlockingQueuedConnection); + connect(this, &QDBusConnectionPrivate::signalNeedsDisconnecting, + this, &QDBusConnectionPrivate::removeSignalHook, Qt::BlockingQueuedConnection); rootNode.flags = 0; @@ -1038,50 +1006,38 @@ QDBusConnectionPrivate::~QDBusConnectionPrivate() "Timer and socket errors will follow and the program will probably crash", qPrintable(name)); - if (mode == ClientMode) { - // the bus service object holds a reference back to us; - // we need to destroy it before we finish destroying ourselves - Q_ASSERT(ref.load() == 0); - QObject *obj = (QObject *)busService; - disconnect(obj, Q_NULLPTR, this, Q_NULLPTR); - delete obj; - } - closeConnection(); rootNode.children.clear(); // free resources qDeleteAll(cachedMetaObjects); - if (server) - q_dbus_server_unref(server); - if (connection) - q_dbus_connection_unref(connection); - - connection = 0; - server = 0; -} - -void QDBusConnectionPrivate::deleteYourself() -{ - if (thread() && thread() != QThread::currentThread()) { - // last reference dropped while not in the correct thread - // ask the correct thread to delete - - // note: since we're posting an event to another thread, we - // must consider deleteLater() to take effect immediately - deleteLater(); - } else { - delete this; + if (mode == ClientMode || mode == PeerMode) { + // the bus service object holds a reference back to us; + // we need to destroy it before we finish destroying ourselves + Q_ASSERT(ref.load() == 0); + QObject *obj = (QObject *)busService; + if (obj) { + disconnect(obj, Q_NULLPTR, this, Q_NULLPTR); + delete obj; + } + if (connection) + q_dbus_connection_unref(connection); + connection = 0; + } else if (mode == ServerMode) { + if (server) + q_dbus_server_unref(server); + server = 0; } } void QDBusConnectionPrivate::closeConnection() { QDBusWriteLocker locker(CloseConnectionAction, this); + qDBusDebug() << this << "Disconnected"; ConnectionMode oldMode = mode; mode = InvalidMode; // prevent reentrancy baseService.clear(); - if (server) { + if (oldMode == ServerMode && server) { q_dbus_server_disconnect(server); q_dbus_server_free_data_slot(&server_slot); } @@ -1096,19 +1052,12 @@ void QDBusConnectionPrivate::closeConnection() } qDeleteAll(pendingCalls); - - qDBusDebug() << this << "Disconnected"; } void QDBusConnectionPrivate::checkThread() { - if (!thread()) { - if (QCoreApplication::instance()) - moveToThread(QCoreApplication::instance()->thread()); - else - qWarning("The thread that had QDBusConnection('%s') has died and there is no main thread", - qPrintable(name)); - } + Q_ASSERT(thread() == QDBusConnectionManager::instance()); + Q_ASSERT(QThread::currentThread() == thread()); } bool QDBusConnectionPrivate::handleError(const QDBusErrorInternal &error) @@ -1134,45 +1083,20 @@ void QDBusConnectionPrivate::timerEvent(QTimerEvent *e) doDispatch(); } -void QDBusConnectionPrivate::customEvent(QEvent *e) -{ - Q_ASSERT(e->type() == QEvent::User); - - QDBusConnectionCallbackEvent *ev = static_cast<QDBusConnectionCallbackEvent *>(e); - QDBusLockerBase::reportThreadAction(int(AddTimeoutAction) + int(ev->subtype), - QDBusLockerBase::BeforeDeliver, this); - switch (ev->subtype) - { - case QDBusConnectionCallbackEvent::AddTimeout: { - QDBusDispatchLocker locker(RealAddTimeoutAction, this); - while (!timeoutsPendingAdd.isEmpty()) { - QPair<DBusTimeout *, int> entry = timeoutsPendingAdd.takeFirst(); - qDBusRealAddTimeout(this, entry.first, entry.second); - } - break; - } - - case QDBusConnectionCallbackEvent::KillTimer: - killTimer(ev->timerId); - break; - - case QDBusConnectionCallbackEvent::AddWatch: - qDBusRealAddWatch(this, ev->watch, ev->extra, ev->fd); - break; - - case QDBusConnectionCallbackEvent::ToggleWatch: - qDBusRealToggleWatch(this, ev->watch, ev->fd); - break; - } - QDBusLockerBase::reportThreadAction(int(AddTimeoutAction) + int(ev->subtype), - QDBusLockerBase::AfterDeliver, this); -} - void QDBusConnectionPrivate::doDispatch() { QDBusDispatchLocker locker(DoDispatchAction, this); - if (mode == ClientMode || mode == PeerMode) + if (mode == ClientMode || mode == PeerMode) { while (q_dbus_connection_dispatch(connection) == DBUS_DISPATCH_DATA_REMAINS) ; + if (dispatchEnabled && !pendingMessages.isEmpty()) { + // dispatch previously queued messages + PendingMessageList::Iterator it = pendingMessages.begin(); + PendingMessageList::Iterator end = pendingMessages.end(); + for ( ; it != end; ++it) + handleMessage(qMove(*it)); + pendingMessages.clear(); + } + } } void QDBusConnectionPrivate::socketRead(int fd) @@ -1212,7 +1136,7 @@ void QDBusConnectionPrivate::objectDestroyed(QObject *obj) SignalHookHash::iterator sit = signalHooks.begin(); while (sit != signalHooks.end()) { if (static_cast<QObject *>(sit.value().obj) == obj) - sit = disconnectSignal(sit); + sit = removeSignalHookNoLock(sit); else ++sit; } @@ -1593,10 +1517,7 @@ void QDBusConnectionPrivate::handleSignal(const QString &key, const QDBusMessage for ( ; it != end && it.key() == key; ++it) { const SignalHook &hook = it.value(); if (!hook.service.isEmpty()) { - const QString owner = - shouldWatchService(hook.service) ? - watchedServices.value(hook.service).owner : - hook.service; + QString owner = watchedServices.value(hook.service, WatchedServiceData(hook.service)).owner; if (owner != msg.service()) continue; } @@ -1654,9 +1575,11 @@ void QDBusConnectionPrivate::handleSignal(const QDBusMessage& msg) handleSignal(key, msg); // third try } -void QDBusConnectionPrivate::setServer(DBusServer *s, const QDBusErrorInternal &error) +void QDBusConnectionPrivate::setServer(QDBusServer *object, DBusServer *s, const QDBusErrorInternal &error) { mode = ServerMode; + serverObject = object; + object->d = this; if (!s) { handleError(error); return; @@ -1730,7 +1653,7 @@ static QDBusConnection::ConnectionCapabilities connectionCapabilies(DBusConnecti # if DBUS_VERSION-0 >= 0x010400 can_send_type = dbus_connection_can_send_type; # endif -#else +#elif !defined(QT_NO_LIBRARY) // run-time check if the next functions are available can_send_type = (can_send_type_t)qdbus_resolve_conditionally("dbus_connection_can_send_type"); #endif @@ -1784,6 +1707,15 @@ void QDBusConnectionPrivate::setConnection(DBusConnection *dbc, const QDBusError Q_ASSERT(hook.midx != -1); signalHooks.insert(QLatin1String("NameLost:" DBUS_INTERFACE_DBUS), hook); + // And initialize the hook for the NameOwnerChanged signal; + // we don't use connectSignal here because the rules are added by connectSignal on a per-need basis + hook.params.clear(); + hook.params.reserve(4); + hook.params << QMetaType::Void << QVariant::String << QVariant::String << QVariant::String; + hook.midx = staticMetaObject.indexOfSlot("serviceOwnerChangedNoLock(QString,QString,QString)"); + Q_ASSERT(hook.midx != -1); + signalHooks.insert(QLatin1String("NameOwnerChanged:" DBUS_INTERFACE_DBUS), hook); + qDBusDebug() << this << ": connected successfully"; // schedule a dispatch: @@ -1800,34 +1732,6 @@ static void qDBusResultReceived(DBusPendingCall *pending, void *user_data) } } -void QDBusConnectionPrivate::waitForFinished(QDBusPendingCallPrivate *pcall) -{ - Q_ASSERT(pcall->pending); - //Q_ASSERT(pcall->mutex.isLocked()); // there's no such function - - if (pcall->waitingForFinished) { - // another thread is already waiting - pcall->waitForFinishedCondition.wait(&pcall->mutex); - } else { - pcall->waitingForFinished = true; - pcall->mutex.unlock(); - - { - QDBusDispatchLocker locker(PendingCallBlockAction, this); - q_dbus_pending_call_block(pcall->pending); - // QDBusConnectionPrivate::processFinishedCall() is called automatically - } - pcall->mutex.lock(); - - if (pcall->pending) { - q_dbus_pending_call_unref(pcall->pending); - pcall->pending = 0; - } - - pcall->waitForFinishedCondition.wakeAll(); - } -} - void QDBusConnectionPrivate::processFinishedCall(QDBusPendingCallPrivate *call) { QDBusConnectionPrivate *connection = const_cast<QDBusConnectionPrivate *>(call->connection); @@ -1843,7 +1747,7 @@ void QDBusConnectionPrivate::processFinishedCall(QDBusPendingCallPrivate *call) msg = QDBusMessagePrivate::fromDBusMessage(reply, connection->capabilities); q_dbus_message_unref(reply); } - qDBusDebug() << connection << "got message reply (async):" << msg; + qDBusDebug() << connection << "got message reply:" << msg; // Check if the reply has the expected signature call->checkReceivedSignature(); @@ -1865,17 +1769,18 @@ void QDBusConnectionPrivate::processFinishedCall(QDBusPendingCallPrivate *call) qDBusDebug() << "Deliver failed!"; } - if (call->pending && !call->waitingForFinished) { + if (call->pending) { q_dbus_pending_call_unref(call->pending); call->pending = 0; } - locker.unlock(); - // Are there any watchers? if (call->watcherHelper) call->watcherHelper->emitSignals(msg, call->sentMessage); + call->waitForFinishedCondition.wakeAll(); + locker.unlock(); + if (msg.type() == QDBusMessage::ErrorMessage) emit connection->callWithCallbackFailed(QDBusError(msg), call->sentMessage); @@ -1883,10 +1788,10 @@ void QDBusConnectionPrivate::processFinishedCall(QDBusPendingCallPrivate *call) delete call; } -int QDBusConnectionPrivate::send(const QDBusMessage& message) +bool QDBusConnectionPrivate::send(const QDBusMessage& message) { if (QDBusMessagePrivate::isLocal(message)) - return -1; // don't send; the reply will be retrieved by the caller + return true; // don't send; the reply will be retrieved by the caller // through the d_ptr->localReply link QDBusError error; @@ -1898,7 +1803,8 @@ int QDBusConnectionPrivate::send(const QDBusMessage& message) qPrintable(message.interface()), qPrintable(message.member()), qPrintable(error.message())); else if (message.type() == QDBusMessage::SignalMessage) - qWarning("QDBusConnection: error: could not send signal path \"%s\" interface \"%s\" member \"%s\": %s", + qWarning("QDBusConnection: error: could not send signal to service \"%s\" path \"%s\" interface \"%s\" member \"%s\": %s", + qPrintable(message.service()), qPrintable(message.path()), qPrintable(message.interface()), qPrintable(message.member()), qPrintable(error.message())); @@ -1909,24 +1815,13 @@ int QDBusConnectionPrivate::send(const QDBusMessage& message) "invalid", qPrintable(message.service()), qPrintable(error.message())); lastError = error; - return 0; + return false; } q_dbus_message_set_no_reply(msg, true); // the reply would not be delivered to anything - qDBusDebug() << this << "sending message (no reply):" << message; - checkThread(); - bool isOk; - { - QDBusDispatchLocker locker(SendMessageAction, this); - isOk = q_dbus_connection_send(connection, msg, 0); - } - int serial = 0; - if (isOk) - serial = q_dbus_message_get_serial(msg); - - q_dbus_message_unref(msg); - return serial; + emit messageNeedsSending(Q_NULLPTR, msg); + return true; } // small helper to note long running blocking dbus calls. @@ -2018,49 +1913,12 @@ QDBusMessage QDBusConnectionPrivate::sendWithReply(const QDBusMessage &message, { QDBusBlockingCallWatcher watcher(message); - checkThread(); - if ((sendMode == QDBus::BlockWithGui || sendMode == QDBus::Block) - && isServiceRegisteredByThread(message.service())) - // special case for synchronous local calls - return sendWithReplyLocal(message); - - if (!QCoreApplication::instance() || sendMode == QDBus::Block) { - QDBusError err; - DBusMessage *msg = QDBusMessagePrivate::toDBusMessage(message, capabilities, &err); - if (!msg) { - qWarning("QDBusConnection: error: could not send message to service \"%s\" path \"%s\" interface \"%s\" member \"%s\": %s", - qPrintable(message.service()), qPrintable(message.path()), - qPrintable(message.interface()), qPrintable(message.member()), - qPrintable(err.message())); - lastError = err; - return QDBusMessage::createError(err); - } + QDBusPendingCallPrivate *pcall = sendWithReplyAsync(message, 0, 0, 0, timeout); + Q_ASSERT(pcall); - qDBusDebug() << this << "sending message (blocking):" << message; - QDBusErrorInternal error; - DBusMessage *reply; - { - QDBusDispatchLocker locker(SendWithReplyAndBlockAction, this); - reply = q_dbus_connection_send_with_reply_and_block(connection, msg, timeout, error); - } - - q_dbus_message_unref(msg); - - if (!!error) { - lastError = err = error; - return QDBusMessage::createError(err); - } - - QDBusMessage amsg = QDBusMessagePrivate::fromDBusMessage(reply, capabilities); - q_dbus_message_unref(reply); - qDBusDebug() << this << "got message reply (blocking):" << amsg; - - return amsg; - } else { // use the event loop - QDBusPendingCallPrivate *pcall = sendWithReplyAsync(message, 0, 0, 0, timeout); - Q_ASSERT(pcall); - - if (pcall->replyMessage.type() == QDBusMessage::InvalidMessage) { + if (pcall->replyMessage.type() == QDBusMessage::InvalidMessage) { + // need to wait for the reply + if (sendMode == QDBus::BlockWithGui) { pcall->watcherHelper = new QDBusPendingCallWatcherHelper; QEventLoop loop; loop.connect(pcall->watcherHelper, SIGNAL(reply(QDBusMessage)), SLOT(quit())); @@ -2068,18 +1926,17 @@ QDBusMessage QDBusConnectionPrivate::sendWithReply(const QDBusMessage &message, // enter the event loop and wait for a reply loop.exec(QEventLoop::ExcludeUserInputEvents | QEventLoop::WaitForMoreEvents); + } else { + pcall->waitForFinished(); } + } - QDBusMessage reply = pcall->replyMessage; - lastError = QDBusError(reply); // set or clear error - - bool r = pcall->ref.deref(); - Q_ASSERT(!r); - Q_UNUSED(r); + QDBusMessage reply = pcall->replyMessage; + lastError = QDBusError(reply); // set or clear error + if (!pcall->ref.deref()) delete pcall; - return reply; - } + return reply; } QDBusMessage QDBusConnectionPrivate::sendWithReplyLocal(const QDBusMessage &message) @@ -2119,34 +1976,13 @@ QDBusPendingCallPrivate *QDBusConnectionPrivate::sendWithReplyAsync(const QDBusM QObject *receiver, const char *returnMethod, const char *errorMethod, int timeout) { - if (isServiceRegisteredByThread(message.service())) { + QDBusPendingCallPrivate *pcall = new QDBusPendingCallPrivate(message, this); + bool isLoopback; + if ((isLoopback = isServiceRegisteredByThread(message.service()))) { // special case for local calls - QDBusPendingCallPrivate *pcall = new QDBusPendingCallPrivate(message, this); pcall->replyMessage = sendWithReplyLocal(message); - if (receiver && returnMethod) - pcall->setReplyCallback(receiver, returnMethod); - - if (errorMethod) { - pcall->watcherHelper = new QDBusPendingCallWatcherHelper; - connect(pcall->watcherHelper, SIGNAL(error(QDBusError,QDBusMessage)), receiver, errorMethod, - Qt::QueuedConnection); - pcall->watcherHelper->moveToThread(thread()); - } - - if ((receiver && returnMethod) || errorMethod) { - // no one waiting, will delete pcall in processFinishedCall() - pcall->ref.store(1); - } else { - // set double ref to prevent race between processFinishedCall() and ref counting - // by QDBusPendingCall::QExplicitlySharedDataPointer<QDBusPendingCallPrivate> - pcall->ref.store(2); - } - processFinishedCall(pcall); - return pcall; } - checkThread(); - QDBusPendingCallPrivate *pcall = new QDBusPendingCallPrivate(message, this); if (receiver && returnMethod) pcall->setReplyCallback(receiver, returnMethod); @@ -2166,6 +2002,12 @@ QDBusPendingCallPrivate *QDBusConnectionPrivate::sendWithReplyAsync(const QDBusM pcall->ref.store(2); } + if (isLoopback) { + // a loopback call + processFinishedCall(pcall); + return pcall; + } + QDBusError error; DBusMessage *msg = QDBusMessagePrivate::toDBusMessage(message, capabilities, &error); if (!msg) { @@ -2176,14 +2018,27 @@ QDBusPendingCallPrivate *QDBusConnectionPrivate::sendWithReplyAsync(const QDBusM pcall->replyMessage = QDBusMessage::createError(error); lastError = error; processFinishedCall(pcall); - return pcall; + } else { + qDBusDebug() << this << "sending message:" << message; + emit messageNeedsSending(pcall, msg, timeout); } + return pcall; +} - qDBusDebug() << this << "sending message (async):" << message; +void QDBusConnectionPrivate::sendInternal(QDBusPendingCallPrivate *pcall, void *message, int timeout) +{ + QDBusError error; DBusPendingCall *pending = 0; + DBusMessage *msg = static_cast<DBusMessage *>(message); + bool isNoReply = !pcall; + Q_ASSERT(isNoReply == !!q_dbus_message_get_no_reply(msg)); - QDBusDispatchLocker locker(SendWithReplyAsyncAction, this); - if (q_dbus_connection_send_with_reply(connection, msg, &pending, timeout)) { + checkThread(); + QDBusDispatchLocker locker(SendMessageAction, this); + + if (isNoReply && q_dbus_connection_send(connection, msg, Q_NULLPTR)) { + // success + } else if (!isNoReply && q_dbus_connection_send_with_reply(connection, msg, &pending, timeout)) { if (pending) { q_dbus_message_unref(msg); @@ -2194,7 +2049,7 @@ QDBusPendingCallPrivate *QDBusConnectionPrivate::sendWithReplyAsync(const QDBusM if (mode == QDBusConnectionPrivate::PeerMode) pendingCalls.append(pcall); - return pcall; + return; } else { // we're probably disconnected at this point lastError = error = QDBusError(QDBusError::Disconnected, QDBusUtil::disconnectedErrorMessage()); @@ -2204,9 +2059,10 @@ QDBusPendingCallPrivate *QDBusConnectionPrivate::sendWithReplyAsync(const QDBusM } q_dbus_message_unref(msg); - pcall->replyMessage = QDBusMessage::createError(error); - processFinishedCall(pcall); - return pcall; + if (pcall) { + pcall->replyMessage = QDBusMessage::createError(error); + processFinishedCall(pcall); + } } bool QDBusConnectionPrivate::connectSignal(const QString &service, @@ -2225,6 +2081,15 @@ bool QDBusConnectionPrivate::connectSignal(const QString &service, if (!prepareHook(hook, key, service, path, interface, name, argumentMatch, receiver, slot, 0, false)) return false; // don't connect + Q_ASSERT(thread() != QThread::currentThread()); + emit signalNeedsConnecting(key, hook); + return true; +} + +void QDBusConnectionPrivate::addSignalHook(const QString &key, const SignalHook &hook) +{ + QDBusWriteLocker locker(ConnectAction, this); + // avoid duplicating: QDBusConnectionPrivate::SignalHookHash::ConstIterator it = signalHooks.constFind(key); QDBusConnectionPrivate::SignalHookHash::ConstIterator end = signalHooks.constEnd(); @@ -2237,24 +2102,18 @@ bool QDBusConnectionPrivate::connectSignal(const QString &service, entry.midx == hook.midx && entry.argumentMatch == hook.argumentMatch) { // no need to compare the parameters if it's the same slot - return true; // already there + return; // already there } } - connectSignal(key, hook); - return true; -} - -void QDBusConnectionPrivate::connectSignal(const QString &key, const SignalHook &hook) -{ signalHooks.insertMulti(key, hook); connect(hook.obj, SIGNAL(destroyed(QObject*)), SLOT(objectDestroyed(QObject*)), - Qt::ConnectionType(Qt::DirectConnection | Qt::UniqueConnection)); + Qt::ConnectionType(Qt::BlockingQueuedConnection | Qt::UniqueConnection)); - MatchRefCountHash::iterator it = matchRefCounts.find(hook.matchRule); + MatchRefCountHash::iterator mit = matchRefCounts.find(hook.matchRule); - if (it != matchRefCounts.end()) { // Match already present - it.value() = it.value() + 1; + if (mit != matchRefCounts.end()) { // Match already present + mit.value() = mit.value() + 1; return; } @@ -2262,7 +2121,7 @@ void QDBusConnectionPrivate::connectSignal(const QString &key, const SignalHook if (connection) { if (mode != QDBusConnectionPrivate::PeerMode) { - qDBusDebug("Adding rule: %s", hook.matchRule.constData()); + qDBusDebug() << this << "Adding rule:" << hook.matchRule; q_dbus_bus_add_match(connection, hook.matchRule, NULL); // Successfully connected the signal @@ -2271,9 +2130,10 @@ void QDBusConnectionPrivate::connectSignal(const QString &key, const SignalHook WatchedServicesHash::mapped_type &data = watchedServices[hook.service]; if (++data.refcount == 1) { // we need to watch for this service changing - connectSignal(QDBusUtil::dbusService(), QString(), QDBusUtil::dbusInterface(), - QStringLiteral("NameOwnerChanged"), QStringList() << hook.service, QString(), - this, SLOT(serviceOwnerChangedNoLock(QString,QString,QString))); + q_dbus_bus_add_match(connection, + buildMatchRule(QDBusUtil::dbusService(), QString(), QDBusUtil::dbusInterface(), + QDBusUtil::nameOwnerChanged(), QStringList() << hook.service, QString()), + NULL); data.owner = getNameOwnerNoCache(hook.service); qDBusDebug() << this << "Watching service" << hook.service << "for owner changes (current owner:" << data.owner << ")"; @@ -2299,7 +2159,14 @@ bool QDBusConnectionPrivate::disconnectSignal(const QString &service, if (!prepareHook(hook, key, service, path, interface, name, argumentMatch, receiver, slot, 0, false)) return false; // don't disconnect - // avoid duplicating: + Q_ASSERT(thread() != QThread::currentThread()); + return emit signalNeedsDisconnecting(key, hook); +} + +bool QDBusConnectionPrivate::removeSignalHook(const QString &key, const SignalHook &hook) +{ + // remove it from our list: + QDBusWriteLocker locker(ConnectAction, this); QDBusConnectionPrivate::SignalHookHash::Iterator it = signalHooks.find(key); QDBusConnectionPrivate::SignalHookHash::Iterator end = signalHooks.end(); for ( ; it != end && it.key() == key; ++it) { @@ -2311,7 +2178,7 @@ bool QDBusConnectionPrivate::disconnectSignal(const QString &service, entry.midx == hook.midx && entry.argumentMatch == hook.argumentMatch) { // no need to compare the parameters if it's the same slot - disconnectSignal(it); + removeSignalHookNoLock(it); return true; // it was there } } @@ -2321,7 +2188,7 @@ bool QDBusConnectionPrivate::disconnectSignal(const QString &service, } QDBusConnectionPrivate::SignalHookHash::Iterator -QDBusConnectionPrivate::disconnectSignal(SignalHookHash::Iterator &it) +QDBusConnectionPrivate::removeSignalHookNoLock(SignalHookHash::Iterator it) { const SignalHook &hook = it.value(); @@ -2342,7 +2209,7 @@ QDBusConnectionPrivate::disconnectSignal(SignalHookHash::Iterator &it) // we don't care about errors here if (connection && erase) { if (mode != QDBusConnectionPrivate::PeerMode) { - qDBusDebug("Removing rule: %s", hook.matchRule.constData()); + qDBusDebug() << this << "Removing rule:" << hook.matchRule; q_dbus_bus_remove_match(connection, hook.matchRule, NULL); // Successfully disconnected the signal @@ -2351,9 +2218,10 @@ QDBusConnectionPrivate::disconnectSignal(SignalHookHash::Iterator &it) if (sit != watchedServices.end()) { if (--sit.value().refcount == 0) { watchedServices.erase(sit); - disconnectSignal(QDBusUtil::dbusService(), QString(), QDBusUtil::dbusInterface(), - QStringLiteral("NameOwnerChanged"), QStringList() << hook.service, QString(), - this, SLOT(serviceOwnerChangedNoLock(QString,QString,QString))); + q_dbus_bus_remove_match(connection, + buildMatchRule(QDBusUtil::dbusService(), QString(), QDBusUtil::dbusInterface(), + QDBusUtil::nameOwnerChanged(), QStringList() << hook.service, QString()), + NULL); } } } @@ -2366,7 +2234,7 @@ QDBusConnectionPrivate::disconnectSignal(SignalHookHash::Iterator &it) void QDBusConnectionPrivate::registerObject(const ObjectTreeNode *node) { connect(node->obj, SIGNAL(destroyed(QObject*)), SLOT(objectDestroyed(QObject*)), - Qt::DirectConnection); + Qt::ConnectionType(Qt::BlockingQueuedConnection | Qt::UniqueConnection)); if (node->flags & (QDBusConnection::ExportAdaptors | QDBusConnection::ExportScriptableSignals @@ -2420,21 +2288,8 @@ void QDBusConnectionPrivate::connectRelay(const QString &service, QDBusAbstractInterface::staticMetaObject.methodCount(), true)) return; // don't connect - // add it to our list: - QDBusWriteLocker locker(ConnectRelayAction, this); - SignalHookHash::ConstIterator it = signalHooks.constFind(key); - SignalHookHash::ConstIterator end = signalHooks.constEnd(); - for ( ; it != end && it.key() == key; ++it) { - const SignalHook &entry = it.value(); - if (entry.service == hook.service && - entry.path == hook.path && - entry.signature == hook.signature && - entry.obj == hook.obj && - entry.midx == hook.midx) - return; // already there, no need to re-add - } - - connectSignal(key, hook); + Q_ASSERT(thread() != QThread::currentThread()); + emit signalNeedsConnecting(key, hook); } void QDBusConnectionPrivate::disconnectRelay(const QString &service, @@ -2452,24 +2307,55 @@ void QDBusConnectionPrivate::disconnectRelay(const QString &service, sig.append(signal.methodSignature()); if (!prepareHook(hook, key, service, path, interface, QString(), QStringList(), receiver, sig, QDBusAbstractInterface::staticMetaObject.methodCount(), true)) - return; // don't connect + return; // don't disconnect - // remove it from our list: - QDBusWriteLocker locker(DisconnectRelayAction, this); - SignalHookHash::Iterator it = signalHooks.find(key); - SignalHookHash::Iterator end = signalHooks.end(); - for ( ; it != end && it.key() == key; ++it) { - const SignalHook &entry = it.value(); - if (entry.service == hook.service && - entry.path == hook.path && - entry.signature == hook.signature && - entry.obj == hook.obj && - entry.midx == hook.midx) { - // found it - disconnectSignal(it); - return; - } - } + Q_ASSERT(thread() != QThread::currentThread()); + emit signalNeedsDisconnecting(key, hook); +} + +bool QDBusConnectionPrivate::shouldWatchService(const QString &service) +{ + // we don't have to watch anything in peer mode + if (mode != ClientMode) + return false; + // we don't have to watch wildcard services (empty strings) + if (service.isEmpty()) + return false; + // we don't have to watch the bus driver + if (service == QDBusUtil::dbusService()) + return false; + return true; +} + +/*! + Sets up a watch rule for service \a service for the change described by + mode \a mode. When the change happens, slot \a member in object \a obj will + be called. + + The caller should call QDBusConnectionPrivate::shouldWatchService() before + calling this function to check whether the service needs to be watched at + all. Failing to do so may add rules that are never activated. +*/ +void QDBusConnectionPrivate::watchService(const QString &service, QDBusServiceWatcher::WatchMode mode, QObject *obj, const char *member) +{ + QStringList matchArgs = matchArgsForService(service, mode); + connectSignal(QDBusUtil::dbusService(), QString(), QDBusUtil::dbusInterface(), QDBusUtil::nameOwnerChanged(), + matchArgs, QString(), obj, member); +} + +/*! + Removes a watch rule set up by QDBusConnectionPrivate::watchService(). The + arguments to this function must be the same as the ones for that function. + + Sets up a watch rule for service \a service for the change described by + mode \a mode. When the change happens, slot \a member in object \a obj will + be called. +*/ +void QDBusConnectionPrivate::unwatchService(const QString &service, QDBusServiceWatcher::WatchMode mode, QObject *obj, const char *member) +{ + QStringList matchArgs = matchArgsForService(service, mode); + disconnectSignal(QDBusUtil::dbusService(), QString(), QDBusUtil::dbusInterface(), QDBusUtil::nameOwnerChanged(), + matchArgs, QString(), obj, member); } QString QDBusConnectionPrivate::getNameOwner(const QString& serviceName) @@ -2498,9 +2384,21 @@ QString QDBusConnectionPrivate::getNameOwnerNoCache(const QString &serviceName) QStringLiteral("GetNameOwner")); QDBusMessagePrivate::setParametersValidated(msg, true); msg << serviceName; - QDBusMessage reply = sendWithReply(msg, QDBus::Block); - if (reply.type() == QDBusMessage::ReplyMessage) - return reply.arguments().at(0).toString(); + + QDBusPendingCallPrivate *pcall = sendWithReplyAsync(msg, Q_NULLPTR, Q_NULLPTR, Q_NULLPTR); + if (thread() == QThread::currentThread()) { + // this function may be called in our own thread and + // QDBusPendingCallPrivate::waitForFinished() would deadlock there + q_dbus_pending_call_block(pcall->pending); + } + pcall->waitForFinished(); + msg = pcall->replyMessage; + + if (!pcall->ref.deref()) + delete pcall; + + if (msg.type() == QDBusMessage::ReplyMessage) + return msg.arguments().at(0).toString(); return QString(); } |