Change QDBusPendingCallPrivate to full reference counting for deletion.

Fixes race between QDBusConnectionPrivate::processFinishedCall()
releasing the mutex before emitting signals (using various members of
QDBusPendingCallPrivate) and deletion of the QDBusPendingCallPrivate
object through QDBusPendingCall::d's destructor (a member of type
QExplicitlySharedDataPointer<QDBusPendingCallPrivate>) leeds to
segmentation fault with CrashTest example on slow/single core
arm cpu).

Task-number: QTBUG-27809
Change-Id: I3590d74d1cfa5816ede764b50b83a7008ec780ff
Reviewed-by: Thiago Macieira <thiago.macieira@intel.com>
This commit is contained in:
Peter Seiderer 2013-06-17 22:44:30 +02:00 committed by The Qt Project
parent 72ecf5a7ec
commit 6c21f42657
4 changed files with 58 additions and 43 deletions

View File

@ -54,6 +54,7 @@
#include "qdbusinterface_p.h" #include "qdbusinterface_p.h"
#include "qdbusutil_p.h" #include "qdbusutil_p.h"
#include "qdbusconnectionmanager_p.h" #include "qdbusconnectionmanager_p.h"
#include "qdbuspendingcall_p.h"
#include "qdbusthreaddebug_p.h" #include "qdbusthreaddebug_p.h"
@ -611,7 +612,7 @@ QDBusPendingCall QDBusConnection::asyncCall(const QDBusMessage &message, int tim
return QDBusPendingCall(0); // null pointer -> disconnected return QDBusPendingCall(0); // null pointer -> disconnected
} }
QDBusPendingCallPrivate *priv = d->sendWithReplyAsync(message, timeout); QDBusPendingCallPrivate *priv = d->sendWithReplyAsync(message, 0, 0, 0, timeout);
return QDBusPendingCall(priv); return QDBusPendingCall(priv);
} }

View File

@ -199,9 +199,8 @@ public:
int send(const QDBusMessage &message); int send(const QDBusMessage &message);
QDBusMessage sendWithReply(const QDBusMessage &message, int mode, int timeout = -1); QDBusMessage sendWithReply(const QDBusMessage &message, int mode, int timeout = -1);
QDBusMessage sendWithReplyLocal(const QDBusMessage &message); QDBusMessage sendWithReplyLocal(const QDBusMessage &message);
QDBusPendingCallPrivate *sendWithReplyAsync(const QDBusMessage &message, int timeout = -1); QDBusPendingCallPrivate *sendWithReplyAsync(const QDBusMessage &message, QObject *receiver,
int sendWithReplyAsync(const QDBusMessage &message, QObject *receiver, const char *returnMethod, const char *errorMethod,int timeout = -1);
const char *returnMethod, const char *errorMethod, int timeout = -1);
bool connectSignal(const QString &service, const QString &path, const QString& interface, bool connectSignal(const QString &service, const QString &path, const QString& interface,
const QString &name, const QStringList &argumentMatch, const QString &signature, const QString &name, const QStringList &argumentMatch, const QString &signature,
QObject *receiver, const char *slot); QObject *receiver, const char *slot);

View File

@ -1901,6 +1901,9 @@ void QDBusConnectionPrivate::processFinishedCall(QDBusPendingCallPrivate *call)
if (msg.type() == QDBusMessage::ErrorMessage) if (msg.type() == QDBusMessage::ErrorMessage)
emit connection->callWithCallbackFailed(QDBusError(msg), call->sentMessage); emit connection->callWithCallbackFailed(QDBusError(msg), call->sentMessage);
if (!call->ref.deref())
delete call;
} }
int QDBusConnectionPrivate::send(const QDBusMessage& message) int QDBusConnectionPrivate::send(const QDBusMessage& message)
@ -1983,7 +1986,7 @@ QDBusMessage QDBusConnectionPrivate::sendWithReply(const QDBusMessage &message,
return amsg; return amsg;
} else { // use the event loop } else { // use the event loop
QDBusPendingCallPrivate *pcall = sendWithReplyAsync(message, timeout); QDBusPendingCallPrivate *pcall = sendWithReplyAsync(message, 0, 0, 0, timeout);
Q_ASSERT(pcall); Q_ASSERT(pcall);
if (pcall->replyMessage.type() == QDBusMessage::InvalidMessage) { if (pcall->replyMessage.type() == QDBusMessage::InvalidMessage) {
@ -1999,6 +2002,10 @@ QDBusMessage QDBusConnectionPrivate::sendWithReply(const QDBusMessage &message,
QDBusMessage reply = pcall->replyMessage; QDBusMessage reply = pcall->replyMessage;
lastError = QDBusError(reply); // set or clear error lastError = QDBusError(reply); // set or clear error
bool r = pcall->ref.deref();
Q_ASSERT(!r);
Q_UNUSED(r);
delete pcall; delete pcall;
return reply; return reply;
} }
@ -2038,19 +2045,55 @@ QDBusMessage QDBusConnectionPrivate::sendWithReplyLocal(const QDBusMessage &mess
} }
QDBusPendingCallPrivate *QDBusConnectionPrivate::sendWithReplyAsync(const QDBusMessage &message, QDBusPendingCallPrivate *QDBusConnectionPrivate::sendWithReplyAsync(const QDBusMessage &message,
int timeout) QObject *receiver, const char *returnMethod,
const char *errorMethod, int timeout)
{ {
if (isServiceRegisteredByThread(message.service())) { if (isServiceRegisteredByThread(message.service())) {
// special case for local calls // special case for local calls
QDBusPendingCallPrivate *pcall = new QDBusPendingCallPrivate(message, this); QDBusPendingCallPrivate *pcall = new QDBusPendingCallPrivate(message, this);
pcall->replyMessage = sendWithReplyLocal(message); 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; return pcall;
} }
checkThread(); checkThread();
QDBusPendingCallPrivate *pcall = new QDBusPendingCallPrivate(message, this); QDBusPendingCallPrivate *pcall = new QDBusPendingCallPrivate(message, this);
pcall->ref.store(0); 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);
}
QDBusError error; QDBusError error;
DBusMessage *msg = QDBusMessagePrivate::toDBusMessage(message, capabilities, &error); DBusMessage *msg = QDBusMessagePrivate::toDBusMessage(message, capabilities, &error);
@ -2061,6 +2104,7 @@ QDBusPendingCallPrivate *QDBusConnectionPrivate::sendWithReplyAsync(const QDBusM
qPrintable(error.message())); qPrintable(error.message()));
pcall->replyMessage = QDBusMessage::createError(error); pcall->replyMessage = QDBusMessage::createError(error);
lastError = error; lastError = error;
processFinishedCall(pcall);
return pcall; return pcall;
} }
@ -2086,45 +2130,10 @@ QDBusPendingCallPrivate *QDBusConnectionPrivate::sendWithReplyAsync(const QDBusM
q_dbus_message_unref(msg); q_dbus_message_unref(msg);
pcall->replyMessage = QDBusMessage::createError(error); pcall->replyMessage = QDBusMessage::createError(error);
processFinishedCall(pcall);
return pcall; return pcall;
} }
int QDBusConnectionPrivate::sendWithReplyAsync(const QDBusMessage &message, QObject *receiver,
const char *returnMethod, const char *errorMethod,
int timeout)
{
QDBusPendingCallPrivate *pcall = sendWithReplyAsync(message, timeout);
Q_ASSERT(pcall);
// has it already finished with success (dispatched locally)?
if (pcall->replyMessage.type() == QDBusMessage::ReplyMessage) {
pcall->setReplyCallback(receiver, returnMethod);
processFinishedCall(pcall);
delete pcall;
return 1;
}
// either it hasn't finished or it has finished with error
if (errorMethod) {
pcall->watcherHelper = new QDBusPendingCallWatcherHelper;
connect(pcall->watcherHelper, SIGNAL(error(QDBusError,QDBusMessage)), receiver, errorMethod,
Qt::QueuedConnection);
pcall->watcherHelper->moveToThread(thread());
}
// has it already finished and is an error reply message?
if (pcall->replyMessage.type() == QDBusMessage::ErrorMessage) {
processFinishedCall(pcall);
delete pcall;
return 1;
}
pcall->ref.ref();
pcall->setReplyCallback(receiver, returnMethod);
return 1;
}
bool QDBusConnectionPrivate::connectSignal(const QString &service, bool QDBusConnectionPrivate::connectSignal(const QString &service,
const QString &path, const QString &interface, const QString &name, const QString &path, const QString &interface, const QString &name,
const QStringList &argumentMatch, const QString &signature, const QStringList &argumentMatch, const QString &signature,

View File

@ -256,6 +256,11 @@ QDBusPendingCall::QDBusPendingCall(const QDBusPendingCall &other)
QDBusPendingCall::QDBusPendingCall(QDBusPendingCallPrivate *dd) QDBusPendingCall::QDBusPendingCall(QDBusPendingCallPrivate *dd)
: d(dd) : d(dd)
{ {
if (dd) {
bool r = dd->ref.deref();
Q_ASSERT(r);
Q_UNUSED(r);
}
} }
/*! /*!
@ -469,6 +474,7 @@ QDBusPendingCall QDBusPendingCall::fromCompletedCall(const QDBusMessage &msg)
msg.type() == QDBusMessage::ReplyMessage) { msg.type() == QDBusMessage::ReplyMessage) {
d = new QDBusPendingCallPrivate(QDBusMessage(), 0); d = new QDBusPendingCallPrivate(QDBusMessage(), 0);
d->replyMessage = msg; d->replyMessage = msg;
d->ref.store(1);
} }
return QDBusPendingCall(d); return QDBusPendingCall(d);