diff options
Diffstat (limited to 'src/corelib/kernel/qobject.cpp')
-rw-r--r-- | src/corelib/kernel/qobject.cpp | 546 |
1 files changed, 304 insertions, 242 deletions
diff --git a/src/corelib/kernel/qobject.cpp b/src/corelib/kernel/qobject.cpp index 268f3949fd..73b655cb36 100644 --- a/src/corelib/kernel/qobject.cpp +++ b/src/corelib/kernel/qobject.cpp @@ -1,6 +1,6 @@ /**************************************************************************** ** -** Copyright (C) 2016 The Qt Company Ltd. +** Copyright (C) 2019 The Qt Company Ltd. ** Copyright (C) 2016 Intel Corporation. ** Copyright (C) 2013 Olivier Goffart <ogoffart@woboq.com> ** Contact: https://www.qt.io/licensing/ @@ -265,13 +265,13 @@ bool QObjectPrivate::isSender(const QObject *receiver, const char *signal) const if (signal_index < 0 || !cd) return false; QBasicMutexLocker locker(signalSlotLock(q)); - if (signal_index < cd->signalVector.count()) { - const QObjectPrivate::Connection *c = cd->signalVector.at(signal_index).first; + if (signal_index < cd->signalVectorCount()) { + 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->signalVector.count()) { - const QObjectPrivate::Connection *c = cd->signalVector.at(signal_index).first; + if (signal_index < cd->signalVectorCount()) { + 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; @@ -327,20 +326,20 @@ void QObjectPrivate::addConnection(int signal, Connection *c) Q_ASSERT(c->sender == q_ptr); ensureConnectionData(); ConnectionData *cd = connections.load(); - if (signal >= cd->signalVector.count()) - cd->signalVector.resize(signal + 1); + cd->resizeSignalVector(signal + 1); ConnectionList &connectionList = cd->connectionsForSignal(signal); - if (connectionList.last) { - 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); } - connectionList.last = c; + c->id = ++cd->currentConnectionId; + c->prevConnectionList = connectionList.last.load(); + connectionList.last.store(c); - cleanConnectionLists(); - - QObjectPrivate *rd = QObjectPrivate::get(c->receiver); + QObjectPrivate *rd = QObjectPrivate::get(c->receiver.load()); rd->ensureConnectionData(); c->prev = &(rd->connections.load()->senders); @@ -350,39 +349,99 @@ void QObjectPrivate::addConnection(int signal, Connection *c) c->next->prev = &c->next; } -void QObjectPrivate::cleanConnectionLists() +void QObjectPrivate::ConnectionData::removeConnection(QObjectPrivate::Connection *c) { - ConnectionData *cd = connections.load(); - if (cd->dirty && cd->ref == 1) { - // remove broken connections - for (int signal = -1; signal < cd->signalVector.count(); ++signal) { - ConnectionList &connectionList = cd->connectionsForSignal(signal); - - // Set to the last entry in the connection list that was *not* - // deleted. This is needed to update the list's last pointer - // at the end of the cleanup. - QObjectPrivate::Connection *last = 0; - - QObjectPrivate::Connection **prev = &connectionList.first; - QObjectPrivate::Connection *c = *prev; - while (c) { - if (c->receiver) { - last = c; - prev = &c->nextConnectionList; - c = *prev; - } else { - QObjectPrivate::Connection *next = c->nextConnectionList; - *prev = next; - c->deref(); - c = next; - } - } + 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; + { + QBasicMutexLocker l(signalSlotLock(sender)); + if (ref > 1) + return; - // Correct the connection list's last pointer. - // As conectionList.last could equal last, this could be a noop - connectionList.last = last; + // 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.load(); + orphaned.store(nullptr); + } + deleteOrphaned(c); +} + +void QObjectPrivate::ConnectionData::deleteOrphaned(QObjectPrivate::ConnectionOrSignalVector *o) +{ + while (o) { + QObjectPrivate::ConnectionOrSignalVector *next = nullptr; + if (SignalVector *v = ConnectionOrSignalVector::asSignalVector(o)) { + next = v->nextInOrphanList; + free(v); + } else { + QObjectPrivate::Connection *c = static_cast<Connection *>(o); + next = c->nextInOrphanList; + Q_ASSERT(!c->receiver.load()); + Q_ASSERT(!c->prev); + c->freeSlotObject(); + c->deref(); } - cd->dirty = false; + o = next; } } @@ -400,16 +459,19 @@ bool QObjectPrivate::isSignalConnected(uint signalIndex, bool checkDeclarative) ConnectionData *cd = connections.load(); if (!cd) return false; + SignalVector *signalVector = cd->signalVector.load(); + if (!signalVector) + return false; - if (cd->allsignals.first) + if (signalVector->at(-1).first.load()) return true; - if (signalIndex < uint(cd->signalVector.count())) { - const QObjectPrivate::Connection *c = cd->signalVector.at(signalIndex).first; + if (signalIndex < uint(cd->signalVectorCount())) { + 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; @@ -420,17 +482,30 @@ bool QObjectPrivate::maybeSignalConnected(uint signalIndex) const ConnectionData *cd = connections.load(); if (!cd) return false; + SignalVector *signalVector = cd->signalVector.load(); + if (!signalVector) + return false; - if (cd->allsignals.first) + if (signalVector->at(-1).first) return true; - if (signalIndex < uint(cd->signalVector.count())) { - const QObjectPrivate::Connection *c = cd->signalVector.at(signalIndex).first; + if (signalIndex < uint(cd->signalVectorCount())) { + const QObjectPrivate::Connection *c = signalVector->at(signalIndex).first; return c != nullptr; } return false; } +/*! + \internal + */ +QAbstractMetaCallEvent::~QAbstractMetaCallEvent() +{ +#if QT_CONFIG(thread) + if (semaphore_) + semaphore_->release(); +#endif +} /*! \internal @@ -438,8 +513,8 @@ bool QObjectPrivate::maybeSignalConnected(uint signalIndex) const QMetaCallEvent::QMetaCallEvent(ushort method_offset, ushort method_relative, QObjectPrivate::StaticMetaCallFunction callFunction, const QObject *sender, int signalId, int nargs, int *types, void **args, QSemaphore *semaphore) - : QEvent(MetaCall), slotObj_(0), sender_(sender), signalId_(signalId), - nargs_(nargs), types_(types), args_(args), semaphore_(semaphore), + : QAbstractMetaCallEvent(sender, signalId, semaphore), + slotObj_(nullptr), nargs_(nargs), types_(types), args_(args), callFunction_(callFunction), method_offset_(method_offset), method_relative_(method_relative) { } @@ -448,9 +523,9 @@ QMetaCallEvent::QMetaCallEvent(ushort method_offset, ushort method_relative, QOb */ QMetaCallEvent::QMetaCallEvent(QtPrivate::QSlotObjectBase *slotO, const QObject *sender, int signalId, int nargs, int *types, void **args, QSemaphore *semaphore) - : QEvent(MetaCall), slotObj_(slotO), sender_(sender), signalId_(signalId), - nargs_(nargs), types_(types), args_(args), semaphore_(semaphore), - callFunction_(0), method_offset_(0), method_relative_(ushort(-1)) + : QAbstractMetaCallEvent(sender, signalId, semaphore), + slotObj_(slotO), nargs_(nargs), types_(types), args_(args), + callFunction_(nullptr), method_offset_(0), method_relative_(ushort(-1)) { if (slotObj_) slotObj_->ref(); @@ -469,10 +544,6 @@ QMetaCallEvent::~QMetaCallEvent() free(types_); free(args_); } -#if QT_CONFIG(thread) - if (semaphore_) - semaphore_->release(); -#endif if (slotObj_) slotObj_->destroyIfLastRef(); } @@ -900,52 +971,27 @@ QObject::~QObject() QBasicMutexLocker locker(signalSlotMutex); // disconnect all receivers - int receiverCount = cd->signalVector.count(); + int receiverCount = cd->signalVectorCount(); for (int signal = -1; signal < receiverCount; ++signal) { QObjectPrivate::ConnectionList &connectionList = cd->connectionsForSignal(signal); - while (QObjectPrivate::Connection *c = connectionList.first) { - if (!c->receiver) { - connectionList.first = c->nextConnectionList; - c->deref(); - continue; - } + 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) { - *c->prev = c->next; - if (c->next) c->next->prev = c->prev; + cd->removeConnection(c); + Q_ASSERT(connectionList.first.load() != c); } - c->receiver = 0; if (needToUnlock) m->unlock(); - - connectionList.first = c->nextConnectionList; - - // The destroy operation must happen outside the lock - if (c->isSlotObject) { - c->isSlotObject = false; - locker.unlock(); - c->slotObj->destroyIfLastRef(); - locker.relock(); - } - c->deref(); } } /* Disconnect all senders: - * This loop basically just does - * for (node = d->senders; node; node = node->next) { ... } - * - * We need to temporarily unlock the receiver mutex to destroy the functors or to lock the - * sender's mutex. And when the mutex is released, node->next might be destroyed by another - * thread. That's why we set node->prev to &node, that way, if node is destroyed, node will - * be updated. */ - QObjectPrivate::Connection *node = cd->senders; - while (node) { + while (QObjectPrivate::Connection *node = cd->senders) { Q_ASSERT(node->receiver); QObject *sender = node->sender; // Send disconnectNotify before removing the connection from sender's connection list. @@ -953,19 +999,17 @@ QObject::~QObject() // and not finish until we release it. sender->disconnectNotify(QMetaObjectPrivate::signal(sender->metaObject(), node->signal_index)); QBasicMutex *m = signalSlotLock(sender); - node->prev = &node; bool needToUnlock = QOrderedMutexLocker::relock(signalSlotMutex, m); //the node has maybe been removed while the mutex was unlocked in relock? - if (!node || node->sender != sender) { + if (node != cd->senders) { // We hold the wrong mutex Q_ASSERT(needToUnlock); m->unlock(); continue; } - node->receiver = 0; + QObjectPrivate::ConnectionData *senderData = sender->d_func()->connections.load(); - if (senderData) - senderData->dirty = true; + Q_ASSERT(senderData); QtPrivate::QSlotObjectBase *slotObj = nullptr; if (node->isSlotObject) { @@ -973,20 +1017,20 @@ QObject::~QObject() node->isSlotObject = false; } - node = node->next; + senderData->removeConnection(node); if (needToUnlock) m->unlock(); if (slotObj) { - if (node) - node->prev = &node; locker.unlock(); slotObj->destroyIfLastRef(); locker.relock(); } } - cd->objectDeleted = true; + // invalidate all connections on the object and make sure + // activate() will skip them + cd->currentConnectionId.store(0); } if (cd && !cd->ref.deref()) delete cd; @@ -1217,7 +1261,7 @@ bool QObject::event(QEvent *e) case QEvent::MetaCall: { - QMetaCallEvent *mce = static_cast<QMetaCallEvent*>(e); + QAbstractMetaCallEvent *mce = static_cast<QAbstractMetaCallEvent*>(e); if (!d_func()->connections.load()) { QBasicMutexLocker locker(signalSlotLock(this)); @@ -1383,7 +1427,7 @@ bool QObject::eventFilter(QObject * /* watched */, QEvent * /* event */) \sa signalsBlocked(), QSignalBlocker */ -bool QObject::blockSignals(bool block) Q_DECL_NOTHROW +bool QObject::blockSignals(bool block) noexcept { Q_D(QObject); bool previous = d->blockSig; @@ -1478,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(¤tData->postEventList.mutex, &targetData->postEventList.mutex); @@ -1528,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 @@ -2316,7 +2383,7 @@ static void err_info_about_objects(const char * func, /*! Returns a pointer to the object that sent the signal, if called in - a slot activated by a signal; otherwise it returns 0. The pointer + a slot activated by a signal; otherwise it returns \nullptr. The pointer is valid only during the execution of the slot that calls this function from this object's thread context. @@ -2448,12 +2515,11 @@ int QObject::receivers(const char *signal) const QObjectPrivate::ConnectionData *cd = d->connections.load(); QBasicMutexLocker locker(signalSlotLock(this)); - if (cd && signal_index < cd->signalVector.count()) { - const QObjectPrivate::Connection *c = - cd->signalVector.at(signal_index).first; + if (cd && signal_index < cd->signalVectorCount()) { + 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(); } } } @@ -3264,15 +3330,15 @@ QObjectPrivate::Connection *QMetaObjectPrivate::connect(const QObject *sender, QObjectPrivate::ConnectionData *scd = QObjectPrivate::get(s)->connections.load(); if (type & Qt::UniqueConnection && scd) { - if (scd->signalVector.count() > signal_index) { - const QObjectPrivate::Connection *c2 = scd->signalVector.at(signal_index).first; + if (scd->signalVectorCount() > signal_index) { + 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; @@ -3281,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()); @@ -3333,47 +3401,38 @@ bool QMetaObject::disconnectOne(const QObject *sender, int signal_index, \internal Helper function to remove the connection from the senders list and setting the receivers to 0 */ -bool QMetaObjectPrivate::disconnectHelper(QObjectPrivate::Connection *c, +bool QMetaObjectPrivate::disconnectHelper(QObjectPrivate::ConnectionData *connections, int signalIndex, const QObject *receiver, int method_index, void **slot, QBasicMutex *senderMutex, DisconnectType disconnectType) { bool success = false; + + auto &connectionList = connections->connectionsForSignal(signalIndex); + auto *c = connectionList.first.load(); while (c) { - if (c->receiver - && (receiver == 0 || (c->receiver == receiver + QObject *r = c->receiver.load(); + if (r && (receiver == nullptr || (r == receiver && (method_index < 0 || (!c->isSlotObject && c->method() == method_index)) - && (slot == 0 || (c->isSlotObject && c->slotObj->compare(slot)))))) { + && (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) { - *c->prev = c->next; - if (c->next) - c->next->prev = c->prev; - } + if (c->receiver.load()) + connections->removeConnection(c); if (needToUnlock) receiverMutex->unlock(); - c->receiver = 0; - - if (c->isSlotObject) { - c->isSlotObject = false; - senderMutex->unlock(); - c->slotObj->destroyIfLastRef(); - senderMutex->lock(); - } - success = true; if (disconnectType == DisconnectOne) return success; } - c = c->nextConnectionList; + c = c->nextConnectionList.load(); } return success; } @@ -3406,24 +3465,20 @@ bool QMetaObjectPrivate::disconnect(const QObject *sender, if (signal_index < 0) { // remove from all connection lists - for (int sig_index = -1; sig_index < scd->signalVector.count(); ++sig_index) { - QObjectPrivate::Connection *c = scd->connectionsForSignal(sig_index).first; - if (disconnectHelper(c, receiver, method_index, slot, senderMutex, disconnectType)) { + for (int sig_index = -1; sig_index < scd->signalVectorCount(); ++sig_index) { + if (disconnectHelper(connections.data(), sig_index, receiver, method_index, slot, senderMutex, disconnectType)) success = true; - scd->dirty = true; - } } - } else if (signal_index < scd->signalVector.count()) { - QObjectPrivate::Connection *c = scd->signalVector.at(signal_index).first; - if (disconnectHelper(c, receiver, method_index, slot, senderMutex, disconnectType)) { + } else if (signal_index < scd->signalVectorCount()) { + if (disconnectHelper(connections.data(), signal_index, receiver, method_index, slot, senderMutex, disconnectType)) success = true; - scd->dirty = true; - } } } locker.unlock(); if (success) { + scd->cleanOrphanedConnections(s); + QMetaMethod smethod = QMetaObjectPrivate::signal(smeta, signal_index); if (smethod.isValid()) s->disconnectNotify(smethod); @@ -3539,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) { @@ -3570,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> @@ -3630,38 +3682,53 @@ void doActivate(QObject *sender, int signal_index, void **argv) signal_spy_set->signal_begin_callback(sender, signal_index, argv); Q_TRACE(QMetaObject_activate_begin_signal, sender, signal_index); + 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->allsignals; + 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; - if (!c) continue; - // We need to check against last here to ensure that signals added - // during the signal emission are not emitted in this emission. - QObjectPrivate::Connection *last = list->last; + QObjectPrivate::Connection *c = list->first.load(); + if (!c) + continue; do { - if (!c->receiver) + QObject * const receiver = c->receiver.load(); + if (!receiver) + continue; + + QThreadData *td = c->receiverThreadData.load(); + if (!td) continue; - QObject * const receiver = c->receiver; - const bool receiverInSameThread = currentThreadId == QObjectPrivate::get(receiver)->threadData->threadId.load(); + 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) { @@ -3672,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 } @@ -3688,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); @@ -3714,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); @@ -3729,21 +3788,18 @@ 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.load()) != nullptr && c->id <= highestConnectionId); - if (connections->objectDeleted) - break; - } while (c != last && (c = c->nextConnectionList) != 0); - - if (connections->objectDeleted) - break; - } while (list != &connections->allsignals && + } while (list != &signalVector->at(-1) && //start over for all signals; - ((list = &connections->allsignals), true)); + ((list = &signalVector->at(-1)), true)); + if (connections->currentConnectionId.load() == 0) + senderDeleted = true; } + if (!senderDeleted) + sp->connections.load()->cleanOrphanedConnections(sender); if (callbacks_enabled && signal_spy_set->signal_end_callback != nullptr) signal_spy_set->signal_end_callback(sender, signal_index); @@ -4038,31 +4094,34 @@ void QObject::dumpObjectInfo() const qDebug(" SIGNALS OUT"); QObjectPrivate::ConnectionData *cd = d->connections.load(); - if (cd && cd->signalVector.count()) { - for (int signal_index = 0; signal_index < cd->signalVector.count(); ++signal_index) { + if (cd && cd->signalVectorCount()) { + 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); qDebug(" signal: %s", signal.methodSignature().constData()); // receivers - const QObjectPrivate::Connection *c = cd->signalVector.at(signal_index).first; 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 { @@ -4810,15 +4869,15 @@ 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->signalVector.count() > signal_index) { - const QObjectPrivate::Connection *c2 = connections->signalVector.at(signal_index).first; + if (connections->signalVectorCount() > signal_index) { + 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); @@ -4827,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; @@ -4859,30 +4921,30 @@ bool QObject::disconnect(const QMetaObject::Connection &connection) { QObjectPrivate::Connection *c = static_cast<QObjectPrivate::Connection *>(connection.d_ptr); - if (!c || !c->receiver) + 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); - QObjectPrivate::ConnectionData *connections = QObjectPrivate::get(c->sender)->connections.load(); - Q_ASSERT(connections); - connections->dirty = true; + // 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; - *c->prev = c->next; - if (c->next) - c->next->prev = c->prev; - c->receiver = nullptr; + connections = QObjectPrivate::get(c->sender)->connections.load(); + Q_ASSERT(connections); + connections->removeConnection(c); } - // destroy the QSlotObject, if possible - if (c->isSlotObject) { - c->slotObj->destroyIfLastRef(); - c->isSlotObject = false; - } + connections->cleanOrphanedConnections(c->sender); c->sender->disconnectNotify(QMetaObjectPrivate::signal(c->sender->metaObject(), c->signal_index)); @@ -5069,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(); } |