Remove QDBusPendingCallPrivate::autoDelete logic.

First step to fix race condition about deleting QDBusPendingCallPrivate.

In a multithreaded application on a slow/single core cpu the following
race (and segmentation fault) can occur:

First thread A is running:
A:  QDBusPendingReply<> reply = pi->asyncCallWithArgumentList(method, argumentList);

Then when the dbus answer arrives thread B will call:

B:  QDBusConnectionPrivate::processFinishedCall()
B:      ...
B:      locker.unlock()

and runs until here, go on with thread A:

A:  reply.waitForFinished();
A:    QDBusPendingCallPrivate::waitForFinished()
A:    {
A:        QMutexLocker locker(&mutex);
A:        if (replyMessage.type() != QDBusMessage::InvalidMessage)
A:            return;

which returns immediately (mutex acquired, replyMessage alread set), now
reply goes out of scope (destructor called) and QDBusPendingCall::d's
destructor of type QExplicitlySharedDataPointer<QDBusPendingCallPrivate>
deletes the reference counted object QDBusPendingCallPrivate.

Now thread B continues, still in processFinishedCall()

B:      if (call->watcherHelper)
B:          call->watcherHelper->emitSignals(msg, call->sentMessage);
B:
B:      if (msg.type() == QDBusMessage::ErrorMessage)
B:          emit connection->callWithCallbackFailed(QDBusError(msg),
B:              call->sentMessage);

accessing alread deleted object QDBusPendingCallPrivate via call->...

Fixed QDBusPendingCallPrivate deletion by proper reference counting
will be done in the next commit.

Task-number: QTBUG-27809
Change-Id: I15b3f0242471b62eaafadc763fb6a33339ff2fe1
Reviewed-by: Thiago Macieira <thiago.macieira@intel.com>
bb10
Peter Seiderer 2013-06-17 20:56:08 +02:00 committed by The Qt Project
parent 64e3bd481e
commit 72ecf5a7ec
2 changed files with 1 additions and 16 deletions

View File

@ -1828,7 +1828,6 @@ static void qDBusResultReceived(DBusPendingCall *pending, void *user_data)
void QDBusConnectionPrivate::waitForFinished(QDBusPendingCallPrivate *pcall)
{
Q_ASSERT(pcall->pending);
Q_ASSERT(!pcall->autoDelete);
//Q_ASSERT(pcall->mutex.isLocked()); // there's no such function
if (pcall->waitingForFinished) {
@ -1854,13 +1853,6 @@ void QDBusConnectionPrivate::waitForFinished(QDBusPendingCallPrivate *pcall)
}
}
// this function is called only in a Q_ASSERT
static inline Q_DECL_UNUSED bool waitingForFinishedIsSet(QDBusPendingCallPrivate *call)
{
const QMutexLocker locker(&call->mutex);
return call->waitingForFinished;
}
void QDBusConnectionPrivate::processFinishedCall(QDBusPendingCallPrivate *call)
{
QDBusConnectionPrivate *connection = const_cast<QDBusConnectionPrivate *>(call->connection);
@ -1909,11 +1901,6 @@ void QDBusConnectionPrivate::processFinishedCall(QDBusPendingCallPrivate *call)
if (msg.type() == QDBusMessage::ErrorMessage)
emit connection->callWithCallbackFailed(QDBusError(msg), call->sentMessage);
if (call->autoDelete) {
Q_ASSERT(!waitingForFinishedIsSet(call)); // can't wait on a call with autoDelete!
delete call;
}
}
int QDBusConnectionPrivate::send(const QDBusMessage& message)
@ -2132,7 +2119,6 @@ int QDBusConnectionPrivate::sendWithReplyAsync(const QDBusMessage &message, QObj
return 1;
}
pcall->autoDelete = true;
pcall->ref.ref();
pcall->setReplyCallback(receiver, returnMethod);

View File

@ -85,7 +85,6 @@ public:
QVector<int> metaTypes;
int methodIdx;
bool autoDelete;
// }
mutable QMutex mutex;
@ -102,7 +101,7 @@ public:
// }
QDBusPendingCallPrivate(const QDBusMessage &sent, QDBusConnectionPrivate *connection)
: sentMessage(sent), connection(connection), autoDelete(false), watcherHelper(0), pending(0), waitingForFinished(false)
: sentMessage(sent), connection(connection), watcherHelper(0), pending(0), waitingForFinished(false)
{ }
~QDBusPendingCallPrivate();
bool setReplyCallback(QObject *target, const char *member);