Fix deadlock if the last reference is dropped during delivery

We increase the reference count of the connection during delivery of an
incoming message, so it's possible that the corresponding deref will
drop the last reference to the connection: another thread may have
called disconnectFromBus/Peer. However, during destruction we try to
drain the incoming socket queue, so we need to acquire the dispatch lock
again.

The solution is to always use deleteLater(), which means the
deleteYourself() function is unnecessary.

Change-Id: I27eaacb532114dd188c4ffff13d507039fcf7b6a
Reviewed-by: Albert Astals Cid <aacid@kde.org>
Reviewed-by: Alex Blasche <alexander.blasche@theqtcompany.com>
This commit is contained in:
Thiago Macieira 2015-04-14 17:02:48 -07:00
parent 8132cb655a
commit 2e12f6e63d
3 changed files with 3 additions and 18 deletions

View File

@ -68,7 +68,7 @@ void QDBusConnectionManager::removeConnection(const QString &name)
QDBusConnectionPrivate *d = 0;
d = connectionHash.take(name);
if (d && !d->ref.deref())
d->deleteYourself();
d->deleteLater();
// Static objects may be keeping the connection open.
// However, it is harmless to have outstanding references to a connection that is
@ -268,7 +268,7 @@ QDBusConnection::QDBusConnection(QDBusConnectionPrivate *dd)
QDBusConnection::~QDBusConnection()
{
if (d && !d->ref.deref())
d->deleteYourself();
d->deleteLater();
}
/*!
@ -283,7 +283,7 @@ QDBusConnection &QDBusConnection::operator=(const QDBusConnection &other)
if (other.d)
other.d->ref.ref();
if (d && !d->ref.deref())
d->deleteYourself();
d->deleteLater();
d = other.d;
return *this;
}

View File

@ -187,7 +187,6 @@ public:
// public methods are entry points from other objects
explicit QDBusConnectionPrivate(QObject *parent = 0);
~QDBusConnectionPrivate();
void deleteYourself();
void setBusService(const QDBusConnection &connection);
void setPeer(DBusConnection *connection, const QDBusErrorInternal &error);

View File

@ -1065,20 +1065,6 @@ QDBusConnectionPrivate::~QDBusConnectionPrivate()
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;
}
}
void QDBusConnectionPrivate::closeConnection()
{
QDBusWriteLocker locker(CloseConnectionAction, this);