summaryrefslogtreecommitdiffstats
path: root/src/corelib/kernel/qobject.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/corelib/kernel/qobject.cpp')
-rw-r--r--src/corelib/kernel/qobject.cpp47
1 files changed, 29 insertions, 18 deletions
diff --git a/src/corelib/kernel/qobject.cpp b/src/corelib/kernel/qobject.cpp
index 4c2a787173..b38f39e2c8 100644
--- a/src/corelib/kernel/qobject.cpp
+++ b/src/corelib/kernel/qobject.cpp
@@ -389,8 +389,14 @@ void QObjectPrivate::ConnectionData::removeConnection(QObjectPrivate::Connection
Q_ASSERT(c != orphaned.loadRelaxed());
// add c to orphanedConnections
- c->nextInOrphanList = orphaned.loadRelaxed();
- orphaned.storeRelaxed(c);
+ Connection *o = nullptr;
+ /* No ABA issue here: When adding a node, we only care about the list head, it doesn't
+ * matter if the tail changes.
+ */
+ do {
+ o = orphaned.loadRelaxed();
+ c->nextInOrphanList = o;
+ } while (!orphaned.testAndSetRelease(o, c));
#ifndef QT_NO_DEBUG
found = false;
@@ -419,8 +425,7 @@ void QObjectPrivate::ConnectionData::cleanOrphanedConnectionsImpl(QObject *sende
// Since ref == 1, no activate() is in process since we locked the mutex. That implies,
// that nothing can reference the orphaned connection objects anymore and they can
// be safely deleted
- c = orphaned.loadRelaxed();
- orphaned.storeRelaxed(nullptr);
+ c = orphaned.fetchAndStoreRelaxed(nullptr);
}
if (c) {
// Deleting c might run arbitrary user code, so we must not hold the lock
@@ -1035,7 +1040,7 @@ QObject::~QObject()
QBasicMutex *m = signalSlotLock(c->receiver.loadRelaxed());
bool needToUnlock = QOrderedMutexLocker::relock(signalSlotMutex, m);
- if (c->receiver.loadAcquire()) {
+ if (c == connectionList.first.loadAcquire() && c->receiver.loadAcquire()) {
cd->removeConnection(c);
Q_ASSERT(connectionList.first.loadRelaxed() != c);
}
@@ -1076,11 +1081,11 @@ QObject::~QObject()
if (needToUnlock)
m->unlock();
- if (slotObj) {
- locker.unlock();
+ locker.unlock();
+ if (slotObj)
slotObj->destroyIfLastRef();
- locker.relock();
- }
+ senderData->cleanOrphanedConnections(sender);
+ locker.relock();
}
// invalidate all connections on the object and make sure
@@ -5024,11 +5029,11 @@ QMetaObject::Connection QObjectPrivate::connectImpl(const QObject *sender, int s
bool QObject::disconnect(const QMetaObject::Connection &connection)
{
QObjectPrivate::Connection *c = static_cast<QObjectPrivate::Connection *>(connection.d_ptr);
+ if (!c)
+ return false;
const bool disconnected = QObjectPrivate::disconnect(c);
- if (disconnected) {
- const_cast<QMetaObject::Connection &>(connection).d_ptr = nullptr;
- c->deref(); // has been removed from the QMetaObject::Connection object
- }
+ const_cast<QMetaObject::Connection &>(connection).d_ptr = nullptr;
+ c->deref(); // has been removed from the QMetaObject::Connection object
return disconnected;
}
@@ -5222,12 +5227,18 @@ bool QObjectPrivate::disconnect(QObjectPrivate::Connection *c)
connections = QObjectPrivate::get(c->sender)->connections.loadRelaxed();
Q_ASSERT(connections);
connections->removeConnection(c);
- }
-
- connections->cleanOrphanedConnections(c->sender);
- c->sender->disconnectNotify(QMetaObjectPrivate::signal(c->sender->metaObject(),
- c->signal_index));
+ c->sender->disconnectNotify(QMetaObjectPrivate::signal(c->sender->metaObject(), c->signal_index));
+ // We must not hold the receiver mutex, else we risk dead-locking; we also only need the sender mutex
+ // It is however vital to hold the senderMutex before calling cleanOrphanedConnections, as otherwise
+ // another thread might modify/delete the connection
+ if (receiverMutex != senderMutex) {
+ receiverMutex->unlock();
+ }
+ connections->cleanOrphanedConnections(c->sender, ConnectionData::AlreadyLockedAndTemporarilyReleasingLock);
+ senderMutex->unlock(); // now both sender and receiver mutex have been manually unlocked
+ locker.dismiss(); // so we dismiss the QOrderedMutexLocker
+ }
return true;
}