Get rid of the locking in activate()
Removing connections and resizing the signal vector now happens in a way that will not interfere with activate(), as the old objects won't get deleted if we are somewhere inside a signal emission. This means that we don't need to lock the senders mutex in activate anymore, as long as the reference counting on the ConnectionData is atomic and we are in the senders thread. This implies that we now need to lock the receivers mutex in queued_activate() abd blocking queued activation to ensure it hasn't been deleted while we are emitting. In addition, some precautions need to be taken to not read from the receiver without holding the lock, as it could get deleted while we're activating (if it's in a different thread). To make that possible store the receivers thread id in the connection data. Use atomic pointers for all variables that can get modified with the signalSlotLock() held and that are being read without the lock being held. This gives us a very nice additional speed improvement for signal emissions. without change with change string based connect: 3287 2436 pointer based connect: 3941 3265 not connected: 403 400 disconnected: 460 489 5 slots connected: 9112 4515 Change-Id: Ib7324bb74c389dcc3b6581a03c31469a6e589fc2 Reviewed-by: Olivier Goffart (Woboq GmbH) <ogoffart@woboq.com>
This commit is contained in:
parent
13ab090977
commit
993b049adf
@ -266,12 +266,12 @@ bool QObjectPrivate::isSender(const QObject *receiver, const char *signal) const
|
|||||||
return false;
|
return false;
|
||||||
QBasicMutexLocker locker(signalSlotLock(q));
|
QBasicMutexLocker locker(signalSlotLock(q));
|
||||||
if (signal_index < cd->signalVectorCount()) {
|
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) {
|
while (c) {
|
||||||
if (c->receiver == receiver)
|
if (c->receiver.load() == receiver)
|
||||||
return true;
|
return true;
|
||||||
c = c->nextConnectionList;
|
c = c->nextConnectionList.load();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
@ -280,20 +280,19 @@ bool QObjectPrivate::isSender(const QObject *receiver, const char *signal) const
|
|||||||
// Used by QAccessibleWidget
|
// Used by QAccessibleWidget
|
||||||
QObjectList QObjectPrivate::receiverList(const char *signal) const
|
QObjectList QObjectPrivate::receiverList(const char *signal) const
|
||||||
{
|
{
|
||||||
Q_Q(const QObject);
|
|
||||||
QObjectList returnValue;
|
QObjectList returnValue;
|
||||||
int signal_index = signalIndex(signal);
|
int signal_index = signalIndex(signal);
|
||||||
ConnectionData *cd = connections.load();
|
ConnectionData *cd = connections.load();
|
||||||
if (signal_index < 0 || !cd)
|
if (signal_index < 0 || !cd)
|
||||||
return returnValue;
|
return returnValue;
|
||||||
QBasicMutexLocker locker(signalSlotLock(q));
|
|
||||||
if (signal_index < cd->signalVectorCount()) {
|
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) {
|
while (c) {
|
||||||
if (c->receiver)
|
QObject *r = c->receiver.load();
|
||||||
returnValue << c->receiver;
|
if (r)
|
||||||
c = c->nextConnectionList;
|
returnValue << r;
|
||||||
|
c = c->nextConnectionList.load();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return returnValue;
|
return returnValue;
|
||||||
@ -330,17 +329,17 @@ void QObjectPrivate::addConnection(int signal, Connection *c)
|
|||||||
cd->resizeSignalVector(signal + 1);
|
cd->resizeSignalVector(signal + 1);
|
||||||
|
|
||||||
ConnectionList &connectionList = cd->connectionsForSignal(signal);
|
ConnectionList &connectionList = cd->connectionsForSignal(signal);
|
||||||
if (connectionList.last) {
|
if (connectionList.last.load()) {
|
||||||
Q_ASSERT(connectionList.last->receiver);
|
Q_ASSERT(connectionList.last.load()->receiver.load());
|
||||||
connectionList.last->nextConnectionList = c;
|
connectionList.last.load()->nextConnectionList.store(c);
|
||||||
} else {
|
} else {
|
||||||
connectionList.first = c;
|
connectionList.first.store(c);
|
||||||
}
|
}
|
||||||
c->id = ++cd->currentConnectionId;
|
c->id = ++cd->currentConnectionId;
|
||||||
c->prevConnectionList = connectionList.last;
|
c->prevConnectionList = connectionList.last.load();
|
||||||
connectionList.last = c;
|
connectionList.last.store(c);
|
||||||
|
|
||||||
QObjectPrivate *rd = QObjectPrivate::get(c->receiver);
|
QObjectPrivate *rd = QObjectPrivate::get(c->receiver.load());
|
||||||
rd->ensureConnectionData();
|
rd->ensureConnectionData();
|
||||||
|
|
||||||
c->prev = &(rd->connections.load()->senders);
|
c->prev = &(rd->connections.load()->senders);
|
||||||
@ -350,6 +349,66 @@ void QObjectPrivate::addConnection(int signal, Connection *c)
|
|||||||
c->next->prev = &c->next;
|
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)
|
void QObjectPrivate::ConnectionData::cleanOrphanedConnectionsImpl(QObject *sender)
|
||||||
{
|
{
|
||||||
ConnectionOrSignalVector *c = nullptr;
|
ConnectionOrSignalVector *c = nullptr;
|
||||||
@ -377,7 +436,7 @@ void QObjectPrivate::ConnectionData::deleteOrphaned(QObjectPrivate::ConnectionOr
|
|||||||
} else {
|
} else {
|
||||||
QObjectPrivate::Connection *c = static_cast<Connection *>(o);
|
QObjectPrivate::Connection *c = static_cast<Connection *>(o);
|
||||||
next = c->nextInOrphanList;
|
next = c->nextInOrphanList;
|
||||||
Q_ASSERT(!c->receiver);
|
Q_ASSERT(!c->receiver.load());
|
||||||
Q_ASSERT(!c->prev);
|
Q_ASSERT(!c->prev);
|
||||||
c->freeSlotObject();
|
c->freeSlotObject();
|
||||||
c->deref();
|
c->deref();
|
||||||
@ -398,18 +457,21 @@ bool QObjectPrivate::isSignalConnected(uint signalIndex, bool checkDeclarative)
|
|||||||
return true;
|
return true;
|
||||||
|
|
||||||
ConnectionData *cd = connections.load();
|
ConnectionData *cd = connections.load();
|
||||||
if (!cd || !cd->signalVector)
|
if (!cd)
|
||||||
|
return false;
|
||||||
|
SignalVector *signalVector = cd->signalVector.load();
|
||||||
|
if (!signalVector)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (cd->signalVector->at(-1).first)
|
if (signalVector->at(-1).first.load())
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
if (signalIndex < uint(cd->signalVectorCount())) {
|
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) {
|
while (c) {
|
||||||
if (c->receiver)
|
if (c->receiver.load())
|
||||||
return true;
|
return true;
|
||||||
c = c->nextConnectionList;
|
c = c->nextConnectionList.load();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
@ -418,14 +480,17 @@ bool QObjectPrivate::isSignalConnected(uint signalIndex, bool checkDeclarative)
|
|||||||
bool QObjectPrivate::maybeSignalConnected(uint signalIndex) const
|
bool QObjectPrivate::maybeSignalConnected(uint signalIndex) const
|
||||||
{
|
{
|
||||||
ConnectionData *cd = connections.load();
|
ConnectionData *cd = connections.load();
|
||||||
if (!cd || !cd->signalVector)
|
if (!cd)
|
||||||
|
return false;
|
||||||
|
SignalVector *signalVector = cd->signalVector.load();
|
||||||
|
if (!signalVector)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (cd->signalVector->at(-1).first)
|
if (signalVector->at(-1).first)
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
if (signalIndex < uint(cd->signalVectorCount())) {
|
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 c != nullptr;
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
@ -910,14 +975,14 @@ QObject::~QObject()
|
|||||||
for (int signal = -1; signal < receiverCount; ++signal) {
|
for (int signal = -1; signal < receiverCount; ++signal) {
|
||||||
QObjectPrivate::ConnectionList &connectionList = cd->connectionsForSignal(signal);
|
QObjectPrivate::ConnectionList &connectionList = cd->connectionsForSignal(signal);
|
||||||
|
|
||||||
while (QObjectPrivate::Connection *c = connectionList.first) {
|
while (QObjectPrivate::Connection *c = connectionList.first.load()) {
|
||||||
Q_ASSERT(c->receiver);
|
Q_ASSERT(c->receiver);
|
||||||
|
|
||||||
QBasicMutex *m = signalSlotLock(c->receiver);
|
QBasicMutex *m = signalSlotLock(c->receiver.load());
|
||||||
bool needToUnlock = QOrderedMutexLocker::relock(signalSlotMutex, m);
|
bool needToUnlock = QOrderedMutexLocker::relock(signalSlotMutex, m);
|
||||||
if (c->receiver) {
|
if (c->receiver) {
|
||||||
cd->removeConnection(c);
|
cd->removeConnection(c);
|
||||||
Q_ASSERT(connectionList.first != c);
|
Q_ASSERT(connectionList.first.load() != c);
|
||||||
}
|
}
|
||||||
if (needToUnlock)
|
if (needToUnlock)
|
||||||
m->unlock();
|
m->unlock();
|
||||||
@ -1457,6 +1522,9 @@ void QObject::moveToThread(QThread *targetThread)
|
|||||||
if (!targetData)
|
if (!targetData)
|
||||||
targetData = new QThreadData(0);
|
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,
|
QOrderedMutexLocker locker(¤tData->postEventList.mutex,
|
||||||
&targetData->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()
|
// the current emitting thread shouldn't restore currentSender after calling moveToThread()
|
||||||
ConnectionData *cd = connections.load();
|
ConnectionData *cd = connections.load();
|
||||||
if (cd && cd->currentSender) {
|
if (cd) {
|
||||||
cd->currentSender->receiverDeleted();
|
if (cd->currentSender) {
|
||||||
cd->currentSender = nullptr;
|
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
|
// set new thread data
|
||||||
@ -2428,10 +2516,10 @@ int QObject::receivers(const char *signal) const
|
|||||||
QObjectPrivate::ConnectionData *cd = d->connections.load();
|
QObjectPrivate::ConnectionData *cd = d->connections.load();
|
||||||
QBasicMutexLocker locker(signalSlotLock(this));
|
QBasicMutexLocker locker(signalSlotLock(this));
|
||||||
if (cd && signal_index < cd->signalVectorCount()) {
|
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) {
|
while (c) {
|
||||||
receivers += c->receiver ? 1 : 0;
|
receivers += c->receiver.load() ? 1 : 0;
|
||||||
c = c->nextConnectionList;
|
c = c->nextConnectionList.load();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -3243,14 +3331,14 @@ QObjectPrivate::Connection *QMetaObjectPrivate::connect(const QObject *sender,
|
|||||||
QObjectPrivate::ConnectionData *scd = QObjectPrivate::get(s)->connections.load();
|
QObjectPrivate::ConnectionData *scd = QObjectPrivate::get(s)->connections.load();
|
||||||
if (type & Qt::UniqueConnection && scd) {
|
if (type & Qt::UniqueConnection && scd) {
|
||||||
if (scd->signalVectorCount() > signal_index) {
|
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;
|
int method_index_absolute = method_index + method_offset;
|
||||||
|
|
||||||
while (c2) {
|
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;
|
return nullptr;
|
||||||
c2 = c2->nextConnectionList;
|
c2 = c2->nextConnectionList.load();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
type &= Qt::UniqueConnection - 1;
|
type &= Qt::UniqueConnection - 1;
|
||||||
@ -3259,13 +3347,15 @@ QObjectPrivate::Connection *QMetaObjectPrivate::connect(const QObject *sender,
|
|||||||
QScopedPointer<QObjectPrivate::Connection> c(new QObjectPrivate::Connection);
|
QScopedPointer<QObjectPrivate::Connection> c(new QObjectPrivate::Connection);
|
||||||
c->sender = s;
|
c->sender = s;
|
||||||
c->signal_index = signal_index;
|
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_relative = method_index;
|
||||||
c->method_offset = method_offset;
|
c->method_offset = method_offset;
|
||||||
c->connectionType = type;
|
c->connectionType = type;
|
||||||
c->isSlotObject = false;
|
c->isSlotObject = false;
|
||||||
c->argumentTypes.store(types);
|
c->argumentTypes.store(types);
|
||||||
c->nextConnectionList = 0;
|
|
||||||
c->callFunction = callFunction;
|
c->callFunction = callFunction;
|
||||||
|
|
||||||
QObjectPrivate::get(s)->addConnection(signal_index, c.data());
|
QObjectPrivate::get(s)->addConnection(signal_index, c.data());
|
||||||
@ -3318,20 +3408,20 @@ bool QMetaObjectPrivate::disconnectHelper(QObjectPrivate::ConnectionData *connec
|
|||||||
bool success = false;
|
bool success = false;
|
||||||
|
|
||||||
auto &connectionList = connections->connectionsForSignal(signalIndex);
|
auto &connectionList = connections->connectionsForSignal(signalIndex);
|
||||||
auto *c = connectionList.first;
|
auto *c = connectionList.first.load();
|
||||||
while (c) {
|
while (c) {
|
||||||
if (c->receiver
|
QObject *r = c->receiver.load();
|
||||||
&& (receiver == nullptr || (c->receiver == receiver
|
if (r && (receiver == nullptr || (r == receiver
|
||||||
&& (method_index < 0 || (!c->isSlotObject && c->method() == method_index))
|
&& (method_index < 0 || (!c->isSlotObject && c->method() == method_index))
|
||||||
&& (slot == nullptr || (c->isSlotObject && c->slotObj->compare(slot)))))) {
|
&& (slot == nullptr || (c->isSlotObject && c->slotObj->compare(slot)))))) {
|
||||||
bool needToUnlock = false;
|
bool needToUnlock = false;
|
||||||
QBasicMutex *receiverMutex = nullptr;
|
QBasicMutex *receiverMutex = nullptr;
|
||||||
if (c->receiver) {
|
if (r) {
|
||||||
receiverMutex = signalSlotLock(c->receiver);
|
receiverMutex = signalSlotLock(r);
|
||||||
// need to relock this receiver and sender in the correct order
|
// need to relock this receiver and sender in the correct order
|
||||||
needToUnlock = QOrderedMutexLocker::relock(senderMutex, receiverMutex);
|
needToUnlock = QOrderedMutexLocker::relock(senderMutex, receiverMutex);
|
||||||
}
|
}
|
||||||
if (c->receiver)
|
if (c->receiver.load())
|
||||||
connections->removeConnection(c);
|
connections->removeConnection(c);
|
||||||
|
|
||||||
if (needToUnlock)
|
if (needToUnlock)
|
||||||
@ -3342,7 +3432,7 @@ bool QMetaObjectPrivate::disconnectHelper(QObjectPrivate::ConnectionData *connec
|
|||||||
if (disconnectType == DisconnectOne)
|
if (disconnectType == DisconnectOne)
|
||||||
return success;
|
return success;
|
||||||
}
|
}
|
||||||
c = c->nextConnectionList;
|
c = c->nextConnectionList.load();
|
||||||
}
|
}
|
||||||
return success;
|
return success;
|
||||||
}
|
}
|
||||||
@ -3504,8 +3594,7 @@ void QMetaObject::connectSlotsByName(QObject *o)
|
|||||||
|
|
||||||
\a signal must be in the signal index range (see QObjectPrivate::signalIndex()).
|
\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,
|
static void queued_activate(QObject *sender, int signal, QObjectPrivate::Connection *c, void **argv)
|
||||||
QBasicMutexLocker &locker)
|
|
||||||
{
|
{
|
||||||
const int *argumentTypes = c->argumentTypes.load();
|
const int *argumentTypes = c->argumentTypes.load();
|
||||||
if (!argumentTypes) {
|
if (!argumentTypes) {
|
||||||
@ -3535,27 +3624,25 @@ static void queued_activate(QObject *sender, int signal, QObjectPrivate::Connect
|
|||||||
for (int n = 1; n < nargs; ++n)
|
for (int n = 1; n < nargs; ++n)
|
||||||
types[n] = argumentTypes[n-1];
|
types[n] = argumentTypes[n-1];
|
||||||
|
|
||||||
locker.unlock();
|
|
||||||
for (int n = 1; n < nargs; ++n)
|
for (int n = 1; n < nargs; ++n)
|
||||||
args[n] = QMetaType::create(types[n], argv[n]);
|
args[n] = QMetaType::create(types[n], argv[n]);
|
||||||
locker.relock();
|
}
|
||||||
|
|
||||||
if (!c->receiver) {
|
QBasicMutexLocker locker(signalSlotLock(c->receiver.load()));
|
||||||
locker.unlock();
|
if (!c->receiver.load()) {
|
||||||
// we have been disconnected while the mutex was unlocked
|
// the connection has been disconnected before we got the lock
|
||||||
for (int n = 1; n < nargs; ++n)
|
locker.unlock();
|
||||||
QMetaType::destroy(types[n], args[n]);
|
for (int n = 1; n < nargs; ++n)
|
||||||
free(types);
|
QMetaType::destroy(types[n], args[n]);
|
||||||
free(args);
|
free(types);
|
||||||
locker.relock();
|
free(args);
|
||||||
return;
|
return;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
QMetaCallEvent *ev = c->isSlotObject ?
|
QMetaCallEvent *ev = c->isSlotObject ?
|
||||||
new QMetaCallEvent(c->slotObj, sender, signal, nargs, types, args) :
|
new QMetaCallEvent(c->slotObj, sender, signal, nargs, types, args) :
|
||||||
new QMetaCallEvent(c->method_offset, c->method_relative, c->callFunction, 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>
|
template <bool callbacks_enabled>
|
||||||
@ -3597,38 +3684,51 @@ void doActivate(QObject *sender, int signal_index, void **argv)
|
|||||||
|
|
||||||
bool senderDeleted = false;
|
bool senderDeleted = false;
|
||||||
{
|
{
|
||||||
QBasicMutexLocker locker(signalSlotLock(sender));
|
|
||||||
Q_ASSERT(sp->connections);
|
Q_ASSERT(sp->connections);
|
||||||
QObjectPrivate::ConnectionDataPointer connections(sp->connections.load());
|
QObjectPrivate::ConnectionDataPointer connections(sp->connections.load());
|
||||||
|
QObjectPrivate::SignalVector *signalVector = connections->signalVector.load();
|
||||||
|
|
||||||
const QObjectPrivate::ConnectionList *list;
|
const QObjectPrivate::ConnectionList *list;
|
||||||
if (signal_index < connections->signalVector->count())
|
if (signal_index < signalVector->count())
|
||||||
list = &connections->signalVector->at(signal_index);
|
list = &signalVector->at(signal_index);
|
||||||
else
|
else
|
||||||
list = &connections->signalVector->at(-1);
|
list = &signalVector->at(-1);
|
||||||
|
|
||||||
Qt::HANDLE currentThreadId = QThread::currentThreadId();
|
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
|
// We need to check against the highest connection id to ensure that signals added
|
||||||
// during the signal emission are not emitted in this emission.
|
// during the signal emission are not emitted in this emission.
|
||||||
uint highestConnectionId = connections->currentConnectionId.load();
|
uint highestConnectionId = connections->currentConnectionId.load();
|
||||||
do {
|
do {
|
||||||
QObjectPrivate::Connection *c = list->first;
|
QObjectPrivate::Connection *c = list->first.load();
|
||||||
if (!c)
|
if (!c)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
do {
|
do {
|
||||||
if (!c->receiver)
|
QObject * const receiver = c->receiver.load();
|
||||||
|
if (!receiver)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
QObject * const receiver = c->receiver;
|
QThreadData *td = c->receiverThreadData.load();
|
||||||
const bool receiverInSameThread = currentThreadId == QObjectPrivate::get(receiver)->threadData->threadId.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
|
// determine if this connection should be sent immediately or
|
||||||
// put into the event queue
|
// put into the event queue
|
||||||
if ((c->connectionType == Qt::AutoConnection && !receiverInSameThread)
|
if ((c->connectionType == Qt::AutoConnection && !receiverInSameThread)
|
||||||
|| (c->connectionType == Qt::QueuedConnection)) {
|
|| (c->connectionType == Qt::QueuedConnection)) {
|
||||||
queued_activate(sender, signal_index, c, argv, locker);
|
queued_activate(sender, signal_index, c, argv);
|
||||||
continue;
|
continue;
|
||||||
#if QT_CONFIG(thread)
|
#if QT_CONFIG(thread)
|
||||||
} else if (c->connectionType == Qt::BlockingQueuedConnection) {
|
} else if (c->connectionType == Qt::BlockingQueuedConnection) {
|
||||||
@ -3639,13 +3739,16 @@ void doActivate(QObject *sender, int signal_index, void **argv)
|
|||||||
receiver->metaObject()->className(), receiver);
|
receiver->metaObject()->className(), receiver);
|
||||||
}
|
}
|
||||||
QSemaphore semaphore;
|
QSemaphore semaphore;
|
||||||
QMetaCallEvent *ev = c->isSlotObject ?
|
{
|
||||||
new QMetaCallEvent(c->slotObj, sender, signal_index, 0, 0, argv, &semaphore) :
|
QBasicMutexLocker locker(signalSlotLock(sender));
|
||||||
new QMetaCallEvent(c->method_offset, c->method_relative, c->callFunction, sender, signal_index, 0, 0, argv, &semaphore);
|
if (!c->receiver)
|
||||||
QCoreApplication::postEvent(receiver, ev);
|
continue;
|
||||||
locker.unlock();
|
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();
|
semaphore.acquire();
|
||||||
locker.relock();
|
|
||||||
continue;
|
continue;
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
@ -3655,22 +3758,13 @@ void doActivate(QObject *sender, int signal_index, void **argv)
|
|||||||
if (c->isSlotObject) {
|
if (c->isSlotObject) {
|
||||||
c->slotObj->ref();
|
c->slotObj->ref();
|
||||||
QScopedPointer<QtPrivate::QSlotObjectBase, QSlotObjectBaseDeleter> obj(c->slotObj);
|
QScopedPointer<QtPrivate::QSlotObjectBase, QSlotObjectBaseDeleter> obj(c->slotObj);
|
||||||
locker.unlock();
|
|
||||||
Q_TRACE(QMetaObject_activate_begin_slot_functor, obj.data());
|
Q_TRACE(QMetaObject_activate_begin_slot_functor, obj.data());
|
||||||
obj->call(receiver, argv);
|
obj->call(receiver, argv);
|
||||||
Q_TRACE(QMetaObject_activate_end_slot_functor, obj.data());
|
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()) {
|
} 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.
|
//we compare the vtable to make sure we are not in the destructor of the object.
|
||||||
const int method_relative = c->method_relative;
|
const int method_relative = c->method_relative;
|
||||||
const auto callFunction = c->callFunction;
|
const auto callFunction = c->callFunction;
|
||||||
locker.unlock();
|
|
||||||
const int methodIndex = (Q_HAS_TRACEPOINTS || callbacks_enabled) ? c->method() : 0;
|
const int methodIndex = (Q_HAS_TRACEPOINTS || callbacks_enabled) ? c->method() : 0;
|
||||||
if (callbacks_enabled && signal_spy_set->slot_begin_callback != nullptr)
|
if (callbacks_enabled && signal_spy_set->slot_begin_callback != nullptr)
|
||||||
signal_spy_set->slot_begin_callback(receiver, methodIndex, argv);
|
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);
|
Q_TRACE(QMetaObject_activate_end_slot, receiver, methodIndex);
|
||||||
if (callbacks_enabled && signal_spy_set->slot_end_callback != nullptr)
|
if (callbacks_enabled && signal_spy_set->slot_end_callback != nullptr)
|
||||||
signal_spy_set->slot_end_callback(receiver, methodIndex);
|
signal_spy_set->slot_end_callback(receiver, methodIndex);
|
||||||
locker.relock();
|
|
||||||
} else {
|
} else {
|
||||||
const int method = c->method_relative + c->method_offset;
|
const int method = c->method_relative + c->method_offset;
|
||||||
locker.unlock();
|
|
||||||
|
|
||||||
if (callbacks_enabled && signal_spy_set->slot_begin_callback != nullptr) {
|
if (callbacks_enabled && signal_spy_set->slot_begin_callback != nullptr) {
|
||||||
signal_spy_set->slot_begin_callback(receiver, method, argv);
|
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);
|
Q_TRACE(QMetaObject_activate_end_slot, receiver, method);
|
||||||
if (callbacks_enabled && signal_spy_set->slot_end_callback != nullptr)
|
if (callbacks_enabled && signal_spy_set->slot_end_callback != nullptr)
|
||||||
signal_spy_set->slot_end_callback(receiver, method);
|
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;
|
//start over for all signals;
|
||||||
((list = &connections->signalVector->at(-1)), true));
|
((list = &signalVector->at(-1)), true));
|
||||||
|
|
||||||
if (connections->currentConnectionId.load() == 0)
|
if (connections->currentConnectionId.load() == 0)
|
||||||
senderDeleted = true;
|
senderDeleted = true;
|
||||||
@ -4005,8 +4095,9 @@ void QObject::dumpObjectInfo() const
|
|||||||
|
|
||||||
QObjectPrivate::ConnectionData *cd = d->connections.load();
|
QObjectPrivate::ConnectionData *cd = d->connections.load();
|
||||||
if (cd && cd->signalVectorCount()) {
|
if (cd && cd->signalVectorCount()) {
|
||||||
for (int signal_index = 0; signal_index < cd->signalVectorCount(); ++signal_index) {
|
QObjectPrivate::SignalVector *signalVector = cd->signalVector.load();
|
||||||
const QObjectPrivate::Connection *c = cd->signalVector->at(signal_index).first;
|
for (int signal_index = 0; signal_index < signalVector->count(); ++signal_index) {
|
||||||
|
const QObjectPrivate::Connection *c = signalVector->at(signal_index).first.load();
|
||||||
if (!c)
|
if (!c)
|
||||||
continue;
|
continue;
|
||||||
const QMetaMethod signal = QMetaObjectPrivate::signal(metaObject(), signal_index);
|
const QMetaMethod signal = QMetaObjectPrivate::signal(metaObject(), signal_index);
|
||||||
@ -4014,23 +4105,23 @@ void QObject::dumpObjectInfo() const
|
|||||||
|
|
||||||
// receivers
|
// receivers
|
||||||
while (c) {
|
while (c) {
|
||||||
if (!c->receiver) {
|
if (!c->receiver.load()) {
|
||||||
qDebug(" <Disconnected receiver>");
|
qDebug(" <Disconnected receiver>");
|
||||||
c = c->nextConnectionList;
|
c = c->nextConnectionList.load();
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (c->isSlotObject) {
|
if (c->isSlotObject) {
|
||||||
qDebug(" <functor or function pointer>");
|
qDebug(" <functor or function pointer>");
|
||||||
c = c->nextConnectionList;
|
c = c->nextConnectionList.load();
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
const QMetaObject *receiverMetaObject = c->receiver->metaObject();
|
const QMetaObject *receiverMetaObject = c->receiver.load()->metaObject();
|
||||||
const QMetaMethod method = receiverMetaObject->method(c->method());
|
const QMetaMethod method = receiverMetaObject->method(c->method());
|
||||||
qDebug(" --> %s::%s %s",
|
qDebug(" --> %s::%s %s",
|
||||||
receiverMetaObject->className(),
|
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());
|
method.methodSignature().constData());
|
||||||
c = c->nextConnectionList;
|
c = c->nextConnectionList.load();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@ -4779,14 +4870,14 @@ QMetaObject::Connection QObjectPrivate::connectImpl(const QObject *sender, int s
|
|||||||
if (type & Qt::UniqueConnection && slot && QObjectPrivate::get(s)->connections.load()) {
|
if (type & Qt::UniqueConnection && slot && QObjectPrivate::get(s)->connections.load()) {
|
||||||
QObjectPrivate::ConnectionData *connections = QObjectPrivate::get(s)->connections.load();
|
QObjectPrivate::ConnectionData *connections = QObjectPrivate::get(s)->connections.load();
|
||||||
if (connections->signalVectorCount() > signal_index) {
|
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) {
|
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();
|
slotObj->destroyIfLastRef();
|
||||||
return QMetaObject::Connection();
|
return QMetaObject::Connection();
|
||||||
}
|
}
|
||||||
c2 = c2->nextConnectionList;
|
c2 = c2->nextConnectionList.load();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
type = static_cast<Qt::ConnectionType>(type ^ Qt::UniqueConnection);
|
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);
|
QScopedPointer<QObjectPrivate::Connection> c(new QObjectPrivate::Connection);
|
||||||
c->sender = s;
|
c->sender = s;
|
||||||
c->signal_index = signal_index;
|
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->slotObj = slotObj;
|
||||||
c->connectionType = type;
|
c->connectionType = type;
|
||||||
c->isSlotObject = true;
|
c->isSlotObject = true;
|
||||||
@ -4829,15 +4923,20 @@ bool QObject::disconnect(const QMetaObject::Connection &connection)
|
|||||||
|
|
||||||
if (!c)
|
if (!c)
|
||||||
return false;
|
return false;
|
||||||
|
QObject *receiver = c->receiver.load();
|
||||||
|
if (!receiver)
|
||||||
|
return false;
|
||||||
|
|
||||||
QBasicMutex *senderMutex = signalSlotLock(c->sender);
|
QBasicMutex *senderMutex = signalSlotLock(c->sender);
|
||||||
QBasicMutex *receiverMutex = signalSlotLock(c->receiver);
|
QBasicMutex *receiverMutex = signalSlotLock(receiver);
|
||||||
|
|
||||||
QObjectPrivate::ConnectionData *connections;
|
QObjectPrivate::ConnectionData *connections;
|
||||||
{
|
{
|
||||||
QOrderedMutexLocker locker(senderMutex, receiverMutex);
|
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;
|
return false;
|
||||||
|
|
||||||
connections = QObjectPrivate::get(c->sender)->connections.load();
|
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
|
Q_ASSERT(d_ptr); // we're only called from operator RestrictedBool() const
|
||||||
QObjectPrivate::Connection *c = static_cast<QObjectPrivate::Connection *>(d_ptr);
|
QObjectPrivate::Connection *c = static_cast<QObjectPrivate::Connection *>(d_ptr);
|
||||||
|
|
||||||
return c->receiver;
|
return c->receiver.load();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -150,11 +150,12 @@ public:
|
|||||||
// linked list of connections connected to slots in this object, next is in base class
|
// linked list of connections connected to slots in this object, next is in base class
|
||||||
Connection **prev;
|
Connection **prev;
|
||||||
// linked list of connections connected to signals in this object
|
// linked list of connections connected to signals in this object
|
||||||
Connection *nextConnectionList;
|
QAtomicPointer<Connection> nextConnectionList;
|
||||||
Connection *prevConnectionList;
|
Connection *prevConnectionList;
|
||||||
|
|
||||||
QObject *sender;
|
QObject *sender;
|
||||||
QObject *receiver;
|
QAtomicPointer<QObject> receiver;
|
||||||
|
QAtomicPointer<QThreadData> receiverThreadData;
|
||||||
union {
|
union {
|
||||||
StaticMetaCallFunction callFunction;
|
StaticMetaCallFunction callFunction;
|
||||||
QtPrivate::QSlotObjectBase *slotObj;
|
QtPrivate::QSlotObjectBase *slotObj;
|
||||||
@ -168,7 +169,7 @@ public:
|
|||||||
ushort connectionType : 3; // 0 == auto, 1 == direct, 2 == queued, 4 == blocking
|
ushort connectionType : 3; // 0 == auto, 1 == direct, 2 == queued, 4 == blocking
|
||||||
ushort isSlotObject : 1;
|
ushort isSlotObject : 1;
|
||||||
ushort ownArgumentTypes : 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
|
//ref_ is 2 for the use in the internal lists, and for the use in QMetaObject::Connection
|
||||||
}
|
}
|
||||||
~Connection();
|
~Connection();
|
||||||
@ -183,7 +184,7 @@ public:
|
|||||||
}
|
}
|
||||||
void deref() {
|
void deref() {
|
||||||
if (!ref_.deref()) {
|
if (!ref_.deref()) {
|
||||||
Q_ASSERT(!receiver);
|
Q_ASSERT(!receiver.load());
|
||||||
Q_ASSERT(!isSlotObject);
|
Q_ASSERT(!isSlotObject);
|
||||||
delete this;
|
delete this;
|
||||||
}
|
}
|
||||||
@ -191,9 +192,8 @@ public:
|
|||||||
};
|
};
|
||||||
// ConnectionList is a singly-linked list
|
// ConnectionList is a singly-linked list
|
||||||
struct ConnectionList {
|
struct ConnectionList {
|
||||||
ConnectionList() : first(nullptr), last(nullptr) {}
|
QAtomicPointer<Connection> first;
|
||||||
Connection *first;
|
QAtomicPointer<Connection> last;
|
||||||
Connection *last;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
struct Sender
|
struct Sender
|
||||||
@ -260,15 +260,8 @@ public:
|
|||||||
// the id below is used to avoid activating new connections. When the object gets
|
// 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
|
// deleted it's set to 0, so that signal emission stops
|
||||||
QAtomicInteger<uint> currentConnectionId;
|
QAtomicInteger<uint> currentConnectionId;
|
||||||
struct Ref {
|
QAtomicInt ref;
|
||||||
int _ref = 0;
|
QAtomicPointer<SignalVector> signalVector;
|
||||||
void ref() { ++_ref; }
|
|
||||||
int deref() { return --_ref; }
|
|
||||||
operator int() const { return _ref; }
|
|
||||||
};
|
|
||||||
|
|
||||||
Ref ref;
|
|
||||||
SignalVector *signalVector = nullptr;
|
|
||||||
Connection *senders = nullptr;
|
Connection *senders = nullptr;
|
||||||
Sender *currentSender = nullptr; // object currently activating the object
|
Sender *currentSender = nullptr; // object currently activating the object
|
||||||
QAtomicPointer<Connection> orphaned;
|
QAtomicPointer<Connection> orphaned;
|
||||||
@ -276,66 +269,14 @@ public:
|
|||||||
~ConnectionData()
|
~ConnectionData()
|
||||||
{
|
{
|
||||||
deleteOrphaned(orphaned.load());
|
deleteOrphaned(orphaned.load());
|
||||||
if (signalVector)
|
SignalVector *v = signalVector.load();
|
||||||
free(signalVector);
|
if (v)
|
||||||
|
free(v);
|
||||||
}
|
}
|
||||||
|
|
||||||
// must be called on the senders connection data
|
// must be called on the senders connection data
|
||||||
// assumes the senders and receivers lock are held
|
// assumes the senders and receivers lock are held
|
||||||
void removeConnection(Connection *c)
|
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 cleanOrphanedConnections(QObject *sender)
|
void cleanOrphanedConnections(QObject *sender)
|
||||||
{
|
{
|
||||||
if (orphaned.load() && ref == 1)
|
if (orphaned.load() && ref == 1)
|
||||||
@ -345,32 +286,33 @@ public:
|
|||||||
|
|
||||||
ConnectionList &connectionsForSignal(int signal)
|
ConnectionList &connectionsForSignal(int signal)
|
||||||
{
|
{
|
||||||
return signalVector->at(signal);
|
return signalVector.load()->at(signal);
|
||||||
}
|
}
|
||||||
|
|
||||||
void resizeSignalVector(uint size) {
|
void resizeSignalVector(uint size) {
|
||||||
if (signalVector && signalVector->allocated > size)
|
SignalVector *vector = this->signalVector.load();
|
||||||
|
if (vector && vector->allocated > size)
|
||||||
return;
|
return;
|
||||||
size = (size + 7) & ~7;
|
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;
|
int start = -1;
|
||||||
if (signalVector) {
|
if (vector) {
|
||||||
memcpy(v, signalVector, sizeof(SignalVector) + (signalVector->allocated + 1) * sizeof(ConnectionList));
|
memcpy(newVector, vector, sizeof(SignalVector) + (vector->allocated + 1) * sizeof(ConnectionList));
|
||||||
start = signalVector->count();
|
start = vector->count();
|
||||||
}
|
}
|
||||||
for (int i = start; i < int(size); ++i)
|
for (int i = start; i < int(size); ++i)
|
||||||
v->at(i) = ConnectionList();
|
newVector->at(i) = ConnectionList();
|
||||||
v->next = nullptr;
|
newVector->next = nullptr;
|
||||||
v->allocated = size;
|
newVector->allocated = size;
|
||||||
|
|
||||||
qSwap(v, signalVector);
|
signalVector.store(newVector);
|
||||||
if (v) {
|
if (vector) {
|
||||||
v->next = orphaned.load();
|
vector->nextInOrphanList = orphaned.load();
|
||||||
orphaned.store(ConnectionOrSignalVector::fromSignalVector(v));
|
orphaned.store(ConnectionOrSignalVector::fromSignalVector(vector));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
int signalVectorCount() const {
|
int signalVectorCount() const {
|
||||||
return signalVector ? signalVector->count() : -1;
|
return signalVector ? signalVector.load()->count() : -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void deleteOrphaned(ConnectionOrSignalVector *c);
|
static void deleteOrphaned(ConnectionOrSignalVector *c);
|
||||||
|
Loading…
x
Reference in New Issue
Block a user