summaryrefslogtreecommitdiffstats
path: root/src/corelib/kernel
diff options
context:
space:
mode:
Diffstat (limited to 'src/corelib/kernel')
-rw-r--r--src/corelib/kernel/qobject.cpp315
-rw-r--r--src/corelib/kernel/qobject_p.h116
2 files changed, 236 insertions, 195 deletions
diff --git a/src/corelib/kernel/qobject.cpp b/src/corelib/kernel/qobject.cpp
index 158d92799c..45e88a2082 100644
--- a/src/corelib/kernel/qobject.cpp
+++ b/src/corelib/kernel/qobject.cpp
@@ -266,12 +266,12 @@ bool QObjectPrivate::isSender(const QObject *receiver, const char *signal) const
return false;
QBasicMutexLocker locker(signalSlotLock(q));
if (signal_index < cd->signalVectorCount()) {
- const QObjectPrivate::Connection *c = cd->signalVector->at(signal_index).first;
+ const QObjectPrivate::Connection *c = cd->signalVector.load()->at(signal_index).first.load();
while (c) {
- if (c->receiver == receiver)
+ if (c->receiver.load() == receiver)
return true;
- c = c->nextConnectionList;
+ c = c->nextConnectionList.load();
}
}
return false;
@@ -280,20 +280,19 @@ bool QObjectPrivate::isSender(const QObject *receiver, const char *signal) const
// Used by QAccessibleWidget
QObjectList QObjectPrivate::receiverList(const char *signal) const
{
- Q_Q(const QObject);
QObjectList returnValue;
int signal_index = signalIndex(signal);
ConnectionData *cd = connections.load();
if (signal_index < 0 || !cd)
return returnValue;
- QBasicMutexLocker locker(signalSlotLock(q));
if (signal_index < cd->signalVectorCount()) {
- const QObjectPrivate::Connection *c = cd->signalVector->at(signal_index).first;
+ const QObjectPrivate::Connection *c = cd->signalVector.load()->at(signal_index).first.load();
while (c) {
- if (c->receiver)
- returnValue << c->receiver;
- c = c->nextConnectionList;
+ QObject *r = c->receiver.load();
+ if (r)
+ returnValue << r;
+ c = c->nextConnectionList.load();
}
}
return returnValue;
@@ -330,17 +329,17 @@ void QObjectPrivate::addConnection(int signal, Connection *c)
cd->resizeSignalVector(signal + 1);
ConnectionList &connectionList = cd->connectionsForSignal(signal);
- if (connectionList.last) {
- Q_ASSERT(connectionList.last->receiver);
- connectionList.last->nextConnectionList = c;
+ if (connectionList.last.load()) {
+ Q_ASSERT(connectionList.last.load()->receiver.load());
+ connectionList.last.load()->nextConnectionList.store(c);
} else {
- connectionList.first = c;
+ connectionList.first.store(c);
}
c->id = ++cd->currentConnectionId;
- c->prevConnectionList = connectionList.last;
- connectionList.last = c;
+ c->prevConnectionList = connectionList.last.load();
+ connectionList.last.store(c);
- QObjectPrivate *rd = QObjectPrivate::get(c->receiver);
+ QObjectPrivate *rd = QObjectPrivate::get(c->receiver.load());
rd->ensureConnectionData();
c->prev = &(rd->connections.load()->senders);
@@ -350,6 +349,66 @@ void QObjectPrivate::addConnection(int signal, Connection *c)
c->next->prev = &c->next;
}
+void QObjectPrivate::ConnectionData::removeConnection(QObjectPrivate::Connection *c)
+{
+ Q_ASSERT(c->receiver.load());
+ ConnectionList &connections = signalVector.load()->at(c->signal_index);
+ c->receiver.store(nullptr);
+ QThreadData *td = c->receiverThreadData.load();
+ if (td)
+ td->deref();
+ c->receiverThreadData.store(nullptr);
+
+#ifndef QT_NO_DEBUG
+ bool found = false;
+ for (Connection *cc = connections.first.load(); cc; cc = cc->nextConnectionList.load()) {
+ if (cc == c) {
+ found = true;
+ break;
+ }
+ }
+ Q_ASSERT(found);
+#endif
+
+ // remove from the senders linked list
+ *c->prev = c->next;
+ if (c->next)
+ c->next->prev = c->prev;
+ c->prev = nullptr;
+
+ if (connections.first.load() == c)
+ connections.first.store(c->nextConnectionList.load());
+ if (connections.last.load() == c)
+ connections.last.store(c->prevConnectionList);
+ Q_ASSERT(signalVector.load()->at(c->signal_index).first.load() != c);
+ Q_ASSERT(signalVector.load()->at(c->signal_index).last.load() != c);
+
+ // keep c->nextConnectionList intact, as it might still get accessed by activate
+ Connection *n = c->nextConnectionList.load();
+ if (n)
+ n->prevConnectionList = c->prevConnectionList;
+ if (c->prevConnectionList)
+ c->prevConnectionList->nextConnectionList.store(n);
+ c->prevConnectionList = nullptr;
+
+ Q_ASSERT(c != orphaned.load());
+ // add c to orphanedConnections
+ c->nextInOrphanList = orphaned.load();
+ orphaned.store(c);
+
+#ifndef QT_NO_DEBUG
+ found = false;
+ for (Connection *cc = connections.first.load(); cc; cc = cc->nextConnectionList.load()) {
+ if (cc == c) {
+ found = true;
+ break;
+ }
+ }
+ Q_ASSERT(!found);
+#endif
+
+}
+
void QObjectPrivate::ConnectionData::cleanOrphanedConnectionsImpl(QObject *sender)
{
ConnectionOrSignalVector *c = nullptr;
@@ -377,7 +436,7 @@ void QObjectPrivate::ConnectionData::deleteOrphaned(QObjectPrivate::ConnectionOr
} else {
QObjectPrivate::Connection *c = static_cast<Connection *>(o);
next = c->nextInOrphanList;
- Q_ASSERT(!c->receiver);
+ Q_ASSERT(!c->receiver.load());
Q_ASSERT(!c->prev);
c->freeSlotObject();
c->deref();
@@ -398,18 +457,21 @@ bool QObjectPrivate::isSignalConnected(uint signalIndex, bool checkDeclarative)
return true;
ConnectionData *cd = connections.load();
- if (!cd || !cd->signalVector)
+ if (!cd)
+ return false;
+ SignalVector *signalVector = cd->signalVector.load();
+ if (!signalVector)
return false;
- if (cd->signalVector->at(-1).first)
+ if (signalVector->at(-1).first.load())
return true;
if (signalIndex < uint(cd->signalVectorCount())) {
- const QObjectPrivate::Connection *c = cd->signalVector->at(signalIndex).first;
+ const QObjectPrivate::Connection *c = signalVector->at(signalIndex).first.load();
while (c) {
- if (c->receiver)
+ if (c->receiver.load())
return true;
- c = c->nextConnectionList;
+ c = c->nextConnectionList.load();
}
}
return false;
@@ -418,14 +480,17 @@ bool QObjectPrivate::isSignalConnected(uint signalIndex, bool checkDeclarative)
bool QObjectPrivate::maybeSignalConnected(uint signalIndex) const
{
ConnectionData *cd = connections.load();
- if (!cd || !cd->signalVector)
+ if (!cd)
+ return false;
+ SignalVector *signalVector = cd->signalVector.load();
+ if (!signalVector)
return false;
- if (cd->signalVector->at(-1).first)
+ if (signalVector->at(-1).first)
return true;
if (signalIndex < uint(cd->signalVectorCount())) {
- const QObjectPrivate::Connection *c = cd->signalVector->at(signalIndex).first;
+ const QObjectPrivate::Connection *c = signalVector->at(signalIndex).first;
return c != nullptr;
}
return false;
@@ -910,14 +975,14 @@ QObject::~QObject()
for (int signal = -1; signal < receiverCount; ++signal) {
QObjectPrivate::ConnectionList &connectionList = cd->connectionsForSignal(signal);
- while (QObjectPrivate::Connection *c = connectionList.first) {
+ while (QObjectPrivate::Connection *c = connectionList.first.load()) {
Q_ASSERT(c->receiver);
- QBasicMutex *m = signalSlotLock(c->receiver);
+ QBasicMutex *m = signalSlotLock(c->receiver.load());
bool needToUnlock = QOrderedMutexLocker::relock(signalSlotMutex, m);
if (c->receiver) {
cd->removeConnection(c);
- Q_ASSERT(connectionList.first != c);
+ Q_ASSERT(connectionList.first.load() != c);
}
if (needToUnlock)
m->unlock();
@@ -1457,6 +1522,9 @@ void QObject::moveToThread(QThread *targetThread)
if (!targetData)
targetData = new QThreadData(0);
+ // make sure nobody adds/removes connections to this object while we're moving it
+ QMutexLocker l(signalSlotLock(this));
+
QOrderedMutexLocker locker(&currentData->postEventList.mutex,
&targetData->postEventList.mutex);
@@ -1507,9 +1575,29 @@ void QObjectPrivate::setThreadData_helper(QThreadData *currentData, QThreadData
// the current emitting thread shouldn't restore currentSender after calling moveToThread()
ConnectionData *cd = connections.load();
- if (cd && cd->currentSender) {
- cd->currentSender->receiverDeleted();
- cd->currentSender = nullptr;
+ if (cd) {
+ if (cd->currentSender) {
+ cd->currentSender->receiverDeleted();
+ cd->currentSender = nullptr;
+ }
+
+ // adjust the receiverThreadId values in the Connections
+ if (cd) {
+ auto *c = cd->senders;
+ while (c) {
+ QObject *r = c->receiver.load();
+ if (r) {
+ Q_ASSERT(r == q);
+ targetData->ref();
+ QThreadData *old = c->receiverThreadData.load();
+ if (old)
+ old->deref();
+ c->receiverThreadData.store(targetData);
+ }
+ c = c->next;
+ }
+ }
+
}
// set new thread data
@@ -2428,10 +2516,10 @@ int QObject::receivers(const char *signal) const
QObjectPrivate::ConnectionData *cd = d->connections.load();
QBasicMutexLocker locker(signalSlotLock(this));
if (cd && signal_index < cd->signalVectorCount()) {
- const QObjectPrivate::Connection *c = cd->signalVector->at(signal_index).first;
+ const QObjectPrivate::Connection *c = cd->signalVector.load()->at(signal_index).first.load();
while (c) {
- receivers += c->receiver ? 1 : 0;
- c = c->nextConnectionList;
+ receivers += c->receiver.load() ? 1 : 0;
+ c = c->nextConnectionList.load();
}
}
}
@@ -3243,14 +3331,14 @@ QObjectPrivate::Connection *QMetaObjectPrivate::connect(const QObject *sender,
QObjectPrivate::ConnectionData *scd = QObjectPrivate::get(s)->connections.load();
if (type & Qt::UniqueConnection && scd) {
if (scd->signalVectorCount() > signal_index) {
- const QObjectPrivate::Connection *c2 = scd->signalVector->at(signal_index).first;
+ const QObjectPrivate::Connection *c2 = scd->signalVector.load()->at(signal_index).first.load();
int method_index_absolute = method_index + method_offset;
while (c2) {
- if (!c2->isSlotObject && c2->receiver == receiver && c2->method() == method_index_absolute)
+ if (!c2->isSlotObject && c2->receiver.load() == receiver && c2->method() == method_index_absolute)
return nullptr;
- c2 = c2->nextConnectionList;
+ c2 = c2->nextConnectionList.load();
}
}
type &= Qt::UniqueConnection - 1;
@@ -3259,13 +3347,15 @@ QObjectPrivate::Connection *QMetaObjectPrivate::connect(const QObject *sender,
QScopedPointer<QObjectPrivate::Connection> c(new QObjectPrivate::Connection);
c->sender = s;
c->signal_index = signal_index;
- c->receiver = r;
+ c->receiver.store(r);
+ QThreadData *td = r->d_func()->threadData;
+ td->ref();
+ c->receiverThreadData.store(td);
c->method_relative = method_index;
c->method_offset = method_offset;
c->connectionType = type;
c->isSlotObject = false;
c->argumentTypes.store(types);
- c->nextConnectionList = 0;
c->callFunction = callFunction;
QObjectPrivate::get(s)->addConnection(signal_index, c.data());
@@ -3318,20 +3408,20 @@ bool QMetaObjectPrivate::disconnectHelper(QObjectPrivate::ConnectionData *connec
bool success = false;
auto &connectionList = connections->connectionsForSignal(signalIndex);
- auto *c = connectionList.first;
+ auto *c = connectionList.first.load();
while (c) {
- if (c->receiver
- && (receiver == nullptr || (c->receiver == receiver
+ QObject *r = c->receiver.load();
+ if (r && (receiver == nullptr || (r == receiver
&& (method_index < 0 || (!c->isSlotObject && c->method() == method_index))
&& (slot == nullptr || (c->isSlotObject && c->slotObj->compare(slot)))))) {
bool needToUnlock = false;
QBasicMutex *receiverMutex = nullptr;
- if (c->receiver) {
- receiverMutex = signalSlotLock(c->receiver);
+ if (r) {
+ receiverMutex = signalSlotLock(r);
// need to relock this receiver and sender in the correct order
needToUnlock = QOrderedMutexLocker::relock(senderMutex, receiverMutex);
}
- if (c->receiver)
+ if (c->receiver.load())
connections->removeConnection(c);
if (needToUnlock)
@@ -3342,7 +3432,7 @@ bool QMetaObjectPrivate::disconnectHelper(QObjectPrivate::ConnectionData *connec
if (disconnectType == DisconnectOne)
return success;
}
- c = c->nextConnectionList;
+ c = c->nextConnectionList.load();
}
return success;
}
@@ -3504,8 +3594,7 @@ void QMetaObject::connectSlotsByName(QObject *o)
\a signal must be in the signal index range (see QObjectPrivate::signalIndex()).
*/
-static void queued_activate(QObject *sender, int signal, QObjectPrivate::Connection *c, void **argv,
- QBasicMutexLocker &locker)
+static void queued_activate(QObject *sender, int signal, QObjectPrivate::Connection *c, void **argv)
{
const int *argumentTypes = c->argumentTypes.load();
if (!argumentTypes) {
@@ -3535,27 +3624,25 @@ static void queued_activate(QObject *sender, int signal, QObjectPrivate::Connect
for (int n = 1; n < nargs; ++n)
types[n] = argumentTypes[n-1];
- locker.unlock();
for (int n = 1; n < nargs; ++n)
args[n] = QMetaType::create(types[n], argv[n]);
- locker.relock();
-
- if (!c->receiver) {
- locker.unlock();
- // we have been disconnected while the mutex was unlocked
- for (int n = 1; n < nargs; ++n)
- QMetaType::destroy(types[n], args[n]);
- free(types);
- free(args);
- locker.relock();
- return;
- }
+ }
+
+ QBasicMutexLocker locker(signalSlotLock(c->receiver.load()));
+ if (!c->receiver.load()) {
+ // the connection has been disconnected before we got the lock
+ locker.unlock();
+ for (int n = 1; n < nargs; ++n)
+ QMetaType::destroy(types[n], args[n]);
+ free(types);
+ free(args);
+ return;
}
QMetaCallEvent *ev = c->isSlotObject ?
new QMetaCallEvent(c->slotObj, sender, signal, nargs, types, args) :
new QMetaCallEvent(c->method_offset, c->method_relative, c->callFunction, sender, signal, nargs, types, args);
- QCoreApplication::postEvent(c->receiver, ev);
+ QCoreApplication::postEvent(c->receiver.load(), ev);
}
template <bool callbacks_enabled>
@@ -3597,38 +3684,51 @@ void doActivate(QObject *sender, int signal_index, void **argv)
bool senderDeleted = false;
{
- QBasicMutexLocker locker(signalSlotLock(sender));
Q_ASSERT(sp->connections);
QObjectPrivate::ConnectionDataPointer connections(sp->connections.load());
+ QObjectPrivate::SignalVector *signalVector = connections->signalVector.load();
const QObjectPrivate::ConnectionList *list;
- if (signal_index < connections->signalVector->count())
- list = &connections->signalVector->at(signal_index);
+ if (signal_index < signalVector->count())
+ list = &signalVector->at(signal_index);
else
- list = &connections->signalVector->at(-1);
+ list = &signalVector->at(-1);
Qt::HANDLE currentThreadId = QThread::currentThreadId();
+ bool inSenderThread = currentThreadId == QObjectPrivate::get(sender)->threadData->threadId.load();
// We need to check against the highest connection id to ensure that signals added
// during the signal emission are not emitted in this emission.
uint highestConnectionId = connections->currentConnectionId.load();
do {
- QObjectPrivate::Connection *c = list->first;
+ QObjectPrivate::Connection *c = list->first.load();
if (!c)
continue;
do {
- if (!c->receiver)
+ QObject * const receiver = c->receiver.load();
+ if (!receiver)
continue;
- QObject * const receiver = c->receiver;
- const bool receiverInSameThread = currentThreadId == QObjectPrivate::get(receiver)->threadData->threadId.load();
+ QThreadData *td = c->receiverThreadData.load();
+ if (!td)
+ continue;
+
+ bool receiverInSameThread;
+ if (inSenderThread) {
+ receiverInSameThread = currentThreadId == td->threadId.load();
+ } else {
+ // need to lock before reading the threadId, because moveToThread() could interfere
+ QMutexLocker lock(signalSlotLock(receiver));
+ receiverInSameThread = currentThreadId == td->threadId.load();
+ }
+
// determine if this connection should be sent immediately or
// put into the event queue
if ((c->connectionType == Qt::AutoConnection && !receiverInSameThread)
|| (c->connectionType == Qt::QueuedConnection)) {
- queued_activate(sender, signal_index, c, argv, locker);
+ queued_activate(sender, signal_index, c, argv);
continue;
#if QT_CONFIG(thread)
} else if (c->connectionType == Qt::BlockingQueuedConnection) {
@@ -3639,13 +3739,16 @@ void doActivate(QObject *sender, int signal_index, void **argv)
receiver->metaObject()->className(), receiver);
}
QSemaphore semaphore;
- QMetaCallEvent *ev = c->isSlotObject ?
- new QMetaCallEvent(c->slotObj, sender, signal_index, 0, 0, argv, &semaphore) :
- new QMetaCallEvent(c->method_offset, c->method_relative, c->callFunction, sender, signal_index, 0, 0, argv, &semaphore);
- QCoreApplication::postEvent(receiver, ev);
- locker.unlock();
+ {
+ QBasicMutexLocker locker(signalSlotLock(sender));
+ if (!c->receiver)
+ continue;
+ QMetaCallEvent *ev = c->isSlotObject ?
+ new QMetaCallEvent(c->slotObj, sender, signal_index, 0, 0, argv, &semaphore) :
+ new QMetaCallEvent(c->method_offset, c->method_relative, c->callFunction, sender, signal_index, 0, 0, argv, &semaphore);
+ QCoreApplication::postEvent(receiver, ev);
+ }
semaphore.acquire();
- locker.relock();
continue;
#endif
}
@@ -3655,22 +3758,13 @@ void doActivate(QObject *sender, int signal_index, void **argv)
if (c->isSlotObject) {
c->slotObj->ref();
QScopedPointer<QtPrivate::QSlotObjectBase, QSlotObjectBaseDeleter> obj(c->slotObj);
- locker.unlock();
Q_TRACE(QMetaObject_activate_begin_slot_functor, obj.data());
obj->call(receiver, argv);
Q_TRACE(QMetaObject_activate_end_slot_functor, obj.data());
-
- // Make sure the slot object gets destroyed before the mutex is locked again, as the
- // destructor of the slot object might also lock a mutex from the signalSlotLock() mutex pool,
- // and that would deadlock if the pool happens to return the same mutex.
- obj.reset();
-
- locker.relock();
} else if (c->callFunction && c->method_offset <= receiver->metaObject()->methodOffset()) {
//we compare the vtable to make sure we are not in the destructor of the object.
const int method_relative = c->method_relative;
const auto callFunction = c->callFunction;
- locker.unlock();
const int methodIndex = (Q_HAS_TRACEPOINTS || callbacks_enabled) ? c->method() : 0;
if (callbacks_enabled && signal_spy_set->slot_begin_callback != nullptr)
signal_spy_set->slot_begin_callback(receiver, methodIndex, argv);
@@ -3681,10 +3775,8 @@ void doActivate(QObject *sender, int signal_index, void **argv)
Q_TRACE(QMetaObject_activate_end_slot, receiver, methodIndex);
if (callbacks_enabled && signal_spy_set->slot_end_callback != nullptr)
signal_spy_set->slot_end_callback(receiver, methodIndex);
- locker.relock();
} else {
const int method = c->method_relative + c->method_offset;
- locker.unlock();
if (callbacks_enabled && signal_spy_set->slot_begin_callback != nullptr) {
signal_spy_set->slot_begin_callback(receiver, method, argv);
@@ -3696,14 +3788,12 @@ void doActivate(QObject *sender, int signal_index, void **argv)
Q_TRACE(QMetaObject_activate_end_slot, receiver, method);
if (callbacks_enabled && signal_spy_set->slot_end_callback != nullptr)
signal_spy_set->slot_end_callback(receiver, method);
-
- locker.relock();
}
- } while ((c = c->nextConnectionList) != 0 && c->id <= highestConnectionId);
+ } while ((c = c->nextConnectionList.load()) != nullptr && c->id <= highestConnectionId);
- } while (list != &connections->signalVector->at(-1) &&
+ } while (list != &signalVector->at(-1) &&
//start over for all signals;
- ((list = &connections->signalVector->at(-1)), true));
+ ((list = &signalVector->at(-1)), true));
if (connections->currentConnectionId.load() == 0)
senderDeleted = true;
@@ -4005,8 +4095,9 @@ void QObject::dumpObjectInfo() const
QObjectPrivate::ConnectionData *cd = d->connections.load();
if (cd && cd->signalVectorCount()) {
- for (int signal_index = 0; signal_index < cd->signalVectorCount(); ++signal_index) {
- const QObjectPrivate::Connection *c = cd->signalVector->at(signal_index).first;
+ QObjectPrivate::SignalVector *signalVector = cd->signalVector.load();
+ for (int signal_index = 0; signal_index < signalVector->count(); ++signal_index) {
+ const QObjectPrivate::Connection *c = signalVector->at(signal_index).first.load();
if (!c)
continue;
const QMetaMethod signal = QMetaObjectPrivate::signal(metaObject(), signal_index);
@@ -4014,23 +4105,23 @@ void QObject::dumpObjectInfo() const
// receivers
while (c) {
- if (!c->receiver) {
+ if (!c->receiver.load()) {
qDebug(" <Disconnected receiver>");
- c = c->nextConnectionList;
+ c = c->nextConnectionList.load();
continue;
}
if (c->isSlotObject) {
qDebug(" <functor or function pointer>");
- c = c->nextConnectionList;
+ c = c->nextConnectionList.load();
continue;
}
- const QMetaObject *receiverMetaObject = c->receiver->metaObject();
+ const QMetaObject *receiverMetaObject = c->receiver.load()->metaObject();
const QMetaMethod method = receiverMetaObject->method(c->method());
qDebug(" --> %s::%s %s",
receiverMetaObject->className(),
- c->receiver->objectName().isEmpty() ? "unnamed" : qPrintable(c->receiver->objectName()),
+ c->receiver.load()->objectName().isEmpty() ? "unnamed" : qPrintable(c->receiver.load()->objectName()),
method.methodSignature().constData());
- c = c->nextConnectionList;
+ c = c->nextConnectionList.load();
}
}
} else {
@@ -4779,14 +4870,14 @@ QMetaObject::Connection QObjectPrivate::connectImpl(const QObject *sender, int s
if (type & Qt::UniqueConnection && slot && QObjectPrivate::get(s)->connections.load()) {
QObjectPrivate::ConnectionData *connections = QObjectPrivate::get(s)->connections.load();
if (connections->signalVectorCount() > signal_index) {
- const QObjectPrivate::Connection *c2 = connections->signalVector->at(signal_index).first;
+ const QObjectPrivate::Connection *c2 = connections->signalVector.load()->at(signal_index).first.load();
while (c2) {
- if (c2->receiver == receiver && c2->isSlotObject && c2->slotObj->compare(slot)) {
+ if (c2->receiver.load() == receiver && c2->isSlotObject && c2->slotObj->compare(slot)) {
slotObj->destroyIfLastRef();
return QMetaObject::Connection();
}
- c2 = c2->nextConnectionList;
+ c2 = c2->nextConnectionList.load();
}
}
type = static_cast<Qt::ConnectionType>(type ^ Qt::UniqueConnection);
@@ -4795,7 +4886,10 @@ QMetaObject::Connection QObjectPrivate::connectImpl(const QObject *sender, int s
QScopedPointer<QObjectPrivate::Connection> c(new QObjectPrivate::Connection);
c->sender = s;
c->signal_index = signal_index;
- c->receiver = r;
+ QThreadData *td = r->d_func()->threadData;
+ td->ref();
+ c->receiverThreadData.store(td);
+ c->receiver.store(r);
c->slotObj = slotObj;
c->connectionType = type;
c->isSlotObject = true;
@@ -4829,15 +4923,20 @@ bool QObject::disconnect(const QMetaObject::Connection &connection)
if (!c)
return false;
+ QObject *receiver = c->receiver.load();
+ if (!receiver)
+ return false;
QBasicMutex *senderMutex = signalSlotLock(c->sender);
- QBasicMutex *receiverMutex = signalSlotLock(c->receiver);
+ QBasicMutex *receiverMutex = signalSlotLock(receiver);
QObjectPrivate::ConnectionData *connections;
{
QOrderedMutexLocker locker(senderMutex, receiverMutex);
- if (!c->receiver)
+ // load receiver once again and recheck to ensure nobody else has removed the connection in the meantime
+ receiver = c->receiver.load();
+ if (!receiver)
return false;
connections = QObjectPrivate::get(c->sender)->connections.load();
@@ -5032,7 +5131,7 @@ bool QMetaObject::Connection::isConnected_helper() const
Q_ASSERT(d_ptr); // we're only called from operator RestrictedBool() const
QObjectPrivate::Connection *c = static_cast<QObjectPrivate::Connection *>(d_ptr);
- return c->receiver;
+ return c->receiver.load();
}
diff --git a/src/corelib/kernel/qobject_p.h b/src/corelib/kernel/qobject_p.h
index f01b709faa..2fb11ecc64 100644
--- a/src/corelib/kernel/qobject_p.h
+++ b/src/corelib/kernel/qobject_p.h
@@ -150,11 +150,12 @@ public:
// linked list of connections connected to slots in this object, next is in base class
Connection **prev;
// linked list of connections connected to signals in this object
- Connection *nextConnectionList;
+ QAtomicPointer<Connection> nextConnectionList;
Connection *prevConnectionList;
QObject *sender;
- QObject *receiver;
+ QAtomicPointer<QObject> receiver;
+ QAtomicPointer<QThreadData> receiverThreadData;
union {
StaticMetaCallFunction callFunction;
QtPrivate::QSlotObjectBase *slotObj;
@@ -168,7 +169,7 @@ public:
ushort connectionType : 3; // 0 == auto, 1 == direct, 2 == queued, 4 == blocking
ushort isSlotObject : 1;
ushort ownArgumentTypes : 1;
- Connection() : nextConnectionList(nullptr), ref_(2), ownArgumentTypes(true) {
+ Connection() : ref_(2), ownArgumentTypes(true) {
//ref_ is 2 for the use in the internal lists, and for the use in QMetaObject::Connection
}
~Connection();
@@ -183,7 +184,7 @@ public:
}
void deref() {
if (!ref_.deref()) {
- Q_ASSERT(!receiver);
+ Q_ASSERT(!receiver.load());
Q_ASSERT(!isSlotObject);
delete this;
}
@@ -191,9 +192,8 @@ public:
};
// ConnectionList is a singly-linked list
struct ConnectionList {
- ConnectionList() : first(nullptr), last(nullptr) {}
- Connection *first;
- Connection *last;
+ QAtomicPointer<Connection> first;
+ QAtomicPointer<Connection> last;
};
struct Sender
@@ -260,15 +260,8 @@ public:
// the id below is used to avoid activating new connections. When the object gets
// deleted it's set to 0, so that signal emission stops
QAtomicInteger<uint> currentConnectionId;
- struct Ref {
- int _ref = 0;
- void ref() { ++_ref; }
- int deref() { return --_ref; }
- operator int() const { return _ref; }
- };
-
- Ref ref;
- SignalVector *signalVector = nullptr;
+ QAtomicInt ref;
+ QAtomicPointer<SignalVector> signalVector;
Connection *senders = nullptr;
Sender *currentSender = nullptr; // object currently activating the object
QAtomicPointer<Connection> orphaned;
@@ -276,66 +269,14 @@ public:
~ConnectionData()
{
deleteOrphaned(orphaned.load());
- if (signalVector)
- free(signalVector);
+ SignalVector *v = signalVector.load();
+ if (v)
+ free(v);
}
// must be called on the senders connection data
// assumes the senders and receivers lock are held
- void removeConnection(Connection *c)
- {
- Q_ASSERT(c->receiver);
- ConnectionList &connections = signalVector->at(c->signal_index);
- c->receiver = nullptr;
-
-#ifndef QT_NO_DEBUG
- bool found = false;
- for (Connection *cc = connections.first; cc; cc = cc->nextConnectionList) {
- if (cc == c) {
- found = true;
- break;
- }
- }
- Q_ASSERT(found);
-#endif
-
- // remove from the senders linked list
- *c->prev = c->next;
- if (c->next)
- c->next->prev = c->prev;
- c->prev = nullptr;
-
- if (connections.first == c)
- connections.first = c->nextConnectionList;
- if (connections.last == c)
- connections.last = c->prevConnectionList;
- Q_ASSERT(signalVector->at(c->signal_index).first != c);
- Q_ASSERT(signalVector->at(c->signal_index).last != c);
-
- // keep c->nextConnectionList intact, as it might still get accessed by activate
- if (c->nextConnectionList)
- c->nextConnectionList->prevConnectionList = c->prevConnectionList;
- if (c->prevConnectionList)
- c->prevConnectionList->nextConnectionList = c->nextConnectionList;
- c->prevConnectionList = nullptr;
-
- Q_ASSERT(c != orphaned.load());
- // add c to orphanedConnections
- c->nextInOrphanList = orphaned.load();
- orphaned.store(c);
-
-#ifndef QT_NO_DEBUG
- found = false;
- for (Connection *cc = connections.first; cc; cc = cc->nextConnectionList) {
- if (cc == c) {
- found = true;
- break;
- }
- }
- Q_ASSERT(!found);
-#endif
-
- }
+ void removeConnection(Connection *c);
void cleanOrphanedConnections(QObject *sender)
{
if (orphaned.load() && ref == 1)
@@ -345,32 +286,33 @@ public:
ConnectionList &connectionsForSignal(int signal)
{
- return signalVector->at(signal);
+ return signalVector.load()->at(signal);
}
void resizeSignalVector(uint size) {
- if (signalVector && signalVector->allocated > size)
+ SignalVector *vector = this->signalVector.load();
+ if (vector && vector->allocated > size)
return;
size = (size + 7) & ~7;
- SignalVector *v = reinterpret_cast<SignalVector *>(malloc(sizeof(SignalVector) + (size + 1) * sizeof(ConnectionList)));
+ SignalVector *newVector = reinterpret_cast<SignalVector *>(malloc(sizeof(SignalVector) + (size + 1) * sizeof(ConnectionList)));
int start = -1;
- if (signalVector) {
- memcpy(v, signalVector, sizeof(SignalVector) + (signalVector->allocated + 1) * sizeof(ConnectionList));
- start = signalVector->count();
+ if (vector) {
+ memcpy(newVector, vector, sizeof(SignalVector) + (vector->allocated + 1) * sizeof(ConnectionList));
+ start = vector->count();
}
for (int i = start; i < int(size); ++i)
- v->at(i) = ConnectionList();
- v->next = nullptr;
- v->allocated = size;
-
- qSwap(v, signalVector);
- if (v) {
- v->next = orphaned.load();
- orphaned.store(ConnectionOrSignalVector::fromSignalVector(v));
+ newVector->at(i) = ConnectionList();
+ newVector->next = nullptr;
+ newVector->allocated = size;
+
+ signalVector.store(newVector);
+ if (vector) {
+ vector->nextInOrphanList = orphaned.load();
+ orphaned.store(ConnectionOrSignalVector::fromSignalVector(vector));
}
}
int signalVectorCount() const {
- return signalVector ? signalVector->count() : -1;
+ return signalVector ? signalVector.load()->count() : -1;
}
static void deleteOrphaned(ConnectionOrSignalVector *c);