Move all connection related data into one data structure
Adn create that data structure on demand on the heap. This reduces the size of QObjectPrivate if there are no connections. If we have connections, it'll use the same amount of allocations and memory as before. Change-Id: I900f6980a2cd8a5f72c3ad18697b5dd49100217d Reviewed-by: Olivier Goffart (Woboq GmbH) <ogoffart@woboq.com>
This commit is contained in:
parent
ab92b9e400
commit
5cc6f90910
@ -182,7 +182,7 @@ QMetaObject *QObjectData::dynamicMetaObject() const
|
|||||||
}
|
}
|
||||||
|
|
||||||
QObjectPrivate::QObjectPrivate(int version)
|
QObjectPrivate::QObjectPrivate(int version)
|
||||||
: threadData(0), connectionLists(0), senders(0), currentSender(0), currentChildBeingDeleted(0)
|
: threadData(0), currentChildBeingDeleted(0)
|
||||||
{
|
{
|
||||||
#ifdef QT_BUILD_INTERNAL
|
#ifdef QT_BUILD_INTERNAL
|
||||||
// Don't check the version parameter in internal builds.
|
// Don't check the version parameter in internal builds.
|
||||||
@ -257,59 +257,22 @@ static void computeOffsets(const QMetaObject *metaobject, int *signalOffset, int
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
This vector contains the all connections from an object.
|
|
||||||
|
|
||||||
Each object may have one vector containing the lists of
|
|
||||||
connections for a given signal. The index in the vector correspond
|
|
||||||
to the signal index. The signal index is the one returned by
|
|
||||||
QObjectPrivate::signalIndex (not QMetaObject::indexOfSignal).
|
|
||||||
Negative index means connections to all signals.
|
|
||||||
|
|
||||||
This vector is protected by the object mutex (signalSlotMutexes())
|
|
||||||
|
|
||||||
Each Connection is also part of a 'senders' linked list. The mutex
|
|
||||||
of the receiver must be locked when touching the pointers of this
|
|
||||||
linked list.
|
|
||||||
*/
|
|
||||||
class QObjectConnectionListVector : public QVector<QObjectPrivate::ConnectionList>
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
bool orphaned; //the QObject owner of this vector has been destroyed while the vector was inUse
|
|
||||||
bool dirty; //some Connection have been disconnected (their receiver is 0) but not removed from the list yet
|
|
||||||
int inUse; //number of functions that are currently accessing this object or its connections
|
|
||||||
QObjectPrivate::ConnectionList allsignals;
|
|
||||||
|
|
||||||
QObjectConnectionListVector()
|
|
||||||
: QVector<QObjectPrivate::ConnectionList>(), orphaned(false), dirty(false), inUse(0)
|
|
||||||
{ }
|
|
||||||
|
|
||||||
QObjectPrivate::ConnectionList &operator[](int at)
|
|
||||||
{
|
|
||||||
if (at < 0)
|
|
||||||
return allsignals;
|
|
||||||
return QVector<QObjectPrivate::ConnectionList>::operator[](at);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// Used by QAccessibleWidget
|
// Used by QAccessibleWidget
|
||||||
bool QObjectPrivate::isSender(const QObject *receiver, const char *signal) const
|
bool QObjectPrivate::isSender(const QObject *receiver, const char *signal) const
|
||||||
{
|
{
|
||||||
Q_Q(const QObject);
|
Q_Q(const QObject);
|
||||||
int signal_index = signalIndex(signal);
|
int signal_index = signalIndex(signal);
|
||||||
if (signal_index < 0)
|
ConnectionData *cd = connections.load();
|
||||||
|
if (signal_index < 0 || !cd)
|
||||||
return false;
|
return false;
|
||||||
QMutexLocker locker(signalSlotLock(q));
|
QMutexLocker locker(signalSlotLock(q));
|
||||||
if (connectionLists) {
|
if (signal_index < cd->signalVector.count()) {
|
||||||
if (signal_index < connectionLists->count()) {
|
const QObjectPrivate::Connection *c = cd->signalVector.at(signal_index).first;
|
||||||
const QObjectPrivate::Connection *c =
|
|
||||||
connectionLists->at(signal_index).first;
|
|
||||||
|
|
||||||
while (c) {
|
while (c) {
|
||||||
if (c->receiver == receiver)
|
if (c->receiver == receiver)
|
||||||
return true;
|
return true;
|
||||||
c = c->nextConnectionList;
|
c = c->nextConnectionList;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
@ -321,18 +284,17 @@ QObjectList QObjectPrivate::receiverList(const char *signal) const
|
|||||||
Q_Q(const QObject);
|
Q_Q(const QObject);
|
||||||
QObjectList returnValue;
|
QObjectList returnValue;
|
||||||
int signal_index = signalIndex(signal);
|
int signal_index = signalIndex(signal);
|
||||||
if (signal_index < 0)
|
ConnectionData *cd = connections.load();
|
||||||
|
if (signal_index < 0 || !cd)
|
||||||
return returnValue;
|
return returnValue;
|
||||||
QMutexLocker locker(signalSlotLock(q));
|
QMutexLocker locker(signalSlotLock(q));
|
||||||
if (connectionLists) {
|
if (signal_index < cd->signalVector.count()) {
|
||||||
if (signal_index < connectionLists->count()) {
|
const QObjectPrivate::Connection *c = cd->signalVector.at(signal_index).first;
|
||||||
const QObjectPrivate::Connection *c = connectionLists->at(signal_index).first;
|
|
||||||
|
|
||||||
while (c) {
|
while (c) {
|
||||||
if (c->receiver)
|
if (c->receiver)
|
||||||
returnValue << c->receiver;
|
returnValue << c->receiver;
|
||||||
c = c->nextConnectionList;
|
c = c->nextConnectionList;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return returnValue;
|
return returnValue;
|
||||||
@ -342,9 +304,12 @@ QObjectList QObjectPrivate::receiverList(const char *signal) const
|
|||||||
QObjectList QObjectPrivate::senderList() const
|
QObjectList QObjectPrivate::senderList() const
|
||||||
{
|
{
|
||||||
QObjectList returnValue;
|
QObjectList returnValue;
|
||||||
QMutexLocker locker(signalSlotLock(q_func()));
|
ConnectionData *cd = connections.load();
|
||||||
for (Connection *c = senders; c; c = c->next)
|
if (cd) {
|
||||||
returnValue << c->sender;
|
QMutexLocker locker(signalSlotLock(q_func()));
|
||||||
|
for (Connection *c = cd->senders; c; c = c->next)
|
||||||
|
returnValue << c->sender;
|
||||||
|
}
|
||||||
return returnValue;
|
return returnValue;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -361,12 +326,12 @@ QObjectList QObjectPrivate::senderList() const
|
|||||||
void QObjectPrivate::addConnection(int signal, Connection *c)
|
void QObjectPrivate::addConnection(int signal, Connection *c)
|
||||||
{
|
{
|
||||||
Q_ASSERT(c->sender == q_ptr);
|
Q_ASSERT(c->sender == q_ptr);
|
||||||
if (!connectionLists)
|
ensureConnectionData();
|
||||||
connectionLists = new QObjectConnectionListVector();
|
ConnectionData *cd = connections.load();
|
||||||
if (signal >= connectionLists->count())
|
if (signal >= cd->signalVector.count())
|
||||||
connectionLists->resize(signal + 1);
|
cd->signalVector.resize(signal + 1);
|
||||||
|
|
||||||
ConnectionList &connectionList = (*connectionLists)[signal];
|
ConnectionList &connectionList = cd->connectionsForSignal(signal);
|
||||||
if (connectionList.last) {
|
if (connectionList.last) {
|
||||||
connectionList.last->nextConnectionList = c;
|
connectionList.last->nextConnectionList = c;
|
||||||
} else {
|
} else {
|
||||||
@ -376,7 +341,10 @@ void QObjectPrivate::addConnection(int signal, Connection *c)
|
|||||||
|
|
||||||
cleanConnectionLists();
|
cleanConnectionLists();
|
||||||
|
|
||||||
c->prev = &(QObjectPrivate::get(c->receiver)->senders);
|
QObjectPrivate *rd = QObjectPrivate::get(c->receiver);
|
||||||
|
rd->ensureConnectionData();
|
||||||
|
|
||||||
|
c->prev = &(rd->connections.load()->senders);
|
||||||
c->next = *c->prev;
|
c->next = *c->prev;
|
||||||
*c->prev = c;
|
*c->prev = c;
|
||||||
if (c->next)
|
if (c->next)
|
||||||
@ -385,11 +353,11 @@ void QObjectPrivate::addConnection(int signal, Connection *c)
|
|||||||
|
|
||||||
void QObjectPrivate::cleanConnectionLists()
|
void QObjectPrivate::cleanConnectionLists()
|
||||||
{
|
{
|
||||||
if (connectionLists->dirty && !connectionLists->inUse) {
|
ConnectionData *cd = connections.load();
|
||||||
|
if (cd->dirty && !cd->inUse) {
|
||||||
// remove broken connections
|
// remove broken connections
|
||||||
for (int signal = -1; signal < connectionLists->count(); ++signal) {
|
for (int signal = -1; signal < cd->signalVector.count(); ++signal) {
|
||||||
QObjectPrivate::ConnectionList &connectionList =
|
ConnectionList &connectionList = cd->connectionsForSignal(signal);
|
||||||
(*connectionLists)[signal];
|
|
||||||
|
|
||||||
// Set to the last entry in the connection list that was *not*
|
// Set to the last entry in the connection list that was *not*
|
||||||
// deleted. This is needed to update the list's last pointer
|
// deleted. This is needed to update the list's last pointer
|
||||||
@ -415,7 +383,7 @@ void QObjectPrivate::cleanConnectionLists()
|
|||||||
// As conectionList.last could equal last, this could be a noop
|
// As conectionList.last could equal last, this could be a noop
|
||||||
connectionList.last = last;
|
connectionList.last = last;
|
||||||
}
|
}
|
||||||
connectionLists->dirty = false;
|
cd->dirty = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -430,14 +398,15 @@ bool QObjectPrivate::isSignalConnected(uint signalIndex, bool checkDeclarative)
|
|||||||
if (checkDeclarative && isDeclarativeSignalConnected(signalIndex))
|
if (checkDeclarative && isDeclarativeSignalConnected(signalIndex))
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
if (!connectionLists)
|
ConnectionData *cd = connections.load();
|
||||||
|
if (!cd)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (connectionLists->allsignals.first)
|
if (cd->allsignals.first)
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
if (signalIndex < uint(connectionLists->count())) {
|
if (signalIndex < uint(cd->signalVector.count())) {
|
||||||
const QObjectPrivate::Connection *c = connectionLists->at(signalIndex).first;
|
const QObjectPrivate::Connection *c = cd->signalVector.at(signalIndex).first;
|
||||||
while (c) {
|
while (c) {
|
||||||
if (c->receiver)
|
if (c->receiver)
|
||||||
return true;
|
return true;
|
||||||
@ -905,60 +874,51 @@ QObject::~QObject()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (d->currentSender) {
|
QObjectPrivate::ConnectionData *cd = d->connections.load();
|
||||||
d->currentSender->receiverDeleted();
|
if (cd) {
|
||||||
d->currentSender = nullptr;
|
if (cd->currentSender) {
|
||||||
}
|
cd->currentSender->receiverDeleted();
|
||||||
|
cd->currentSender = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
if (d->connectionLists || d->senders) {
|
|
||||||
QMutex *signalSlotMutex = signalSlotLock(this);
|
QMutex *signalSlotMutex = signalSlotLock(this);
|
||||||
QMutexLocker locker(signalSlotMutex);
|
QMutexLocker locker(signalSlotMutex);
|
||||||
|
++cd->inUse;
|
||||||
|
|
||||||
// disconnect all receivers
|
// disconnect all receivers
|
||||||
if (d->connectionLists) {
|
int receiverCount = cd->signalVector.count();
|
||||||
++d->connectionLists->inUse;
|
for (int signal = -1; signal < receiverCount; ++signal) {
|
||||||
int connectionListsCount = d->connectionLists->count();
|
QObjectPrivate::ConnectionList &connectionList = cd->connectionsForSignal(signal);
|
||||||
for (int signal = -1; signal < connectionListsCount; ++signal) {
|
|
||||||
QObjectPrivate::ConnectionList &connectionList =
|
|
||||||
(*d->connectionLists)[signal];
|
|
||||||
|
|
||||||
while (QObjectPrivate::Connection *c = connectionList.first) {
|
|
||||||
if (!c->receiver) {
|
|
||||||
connectionList.first = c->nextConnectionList;
|
|
||||||
c->deref();
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
QMutex *m = signalSlotLock(c->receiver);
|
|
||||||
bool needToUnlock = QOrderedMutexLocker::relock(signalSlotMutex, m);
|
|
||||||
|
|
||||||
if (c->receiver) {
|
|
||||||
*c->prev = c->next;
|
|
||||||
if (c->next) c->next->prev = c->prev;
|
|
||||||
}
|
|
||||||
c->receiver = 0;
|
|
||||||
if (needToUnlock)
|
|
||||||
m->unlock();
|
|
||||||
|
|
||||||
|
while (QObjectPrivate::Connection *c = connectionList.first) {
|
||||||
|
if (!c->receiver) {
|
||||||
connectionList.first = c->nextConnectionList;
|
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();
|
c->deref();
|
||||||
|
continue;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
if (!--d->connectionLists->inUse) {
|
QMutex *m = signalSlotLock(c->receiver);
|
||||||
delete d->connectionLists;
|
bool needToUnlock = QOrderedMutexLocker::relock(signalSlotMutex, m);
|
||||||
} else {
|
|
||||||
d->connectionLists->orphaned = true;
|
if (c->receiver) {
|
||||||
|
*c->prev = c->next;
|
||||||
|
if (c->next) c->next->prev = c->prev;
|
||||||
|
}
|
||||||
|
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();
|
||||||
}
|
}
|
||||||
d->connectionLists = 0;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Disconnect all senders:
|
/* Disconnect all senders:
|
||||||
@ -970,8 +930,9 @@ QObject::~QObject()
|
|||||||
* thread. That's why we set node->prev to &node, that way, if node is destroyed, node will
|
* thread. That's why we set node->prev to &node, that way, if node is destroyed, node will
|
||||||
* be updated.
|
* be updated.
|
||||||
*/
|
*/
|
||||||
QObjectPrivate::Connection *node = d->senders;
|
QObjectPrivate::Connection *node = cd->senders;
|
||||||
while (node) {
|
while (node) {
|
||||||
|
Q_ASSERT(node->receiver);
|
||||||
QObject *sender = node->sender;
|
QObject *sender = node->sender;
|
||||||
// Send disconnectNotify before removing the connection from sender's connection list.
|
// Send disconnectNotify before removing the connection from sender's connection list.
|
||||||
// This ensures any eventual destructor of sender will block on getting receiver's lock
|
// This ensures any eventual destructor of sender will block on getting receiver's lock
|
||||||
@ -988,9 +949,9 @@ QObject::~QObject()
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
node->receiver = 0;
|
node->receiver = 0;
|
||||||
QObjectConnectionListVector *senderLists = sender->d_func()->connectionLists;
|
QObjectPrivate::ConnectionData *senderData = sender->d_func()->connections;
|
||||||
if (senderLists)
|
if (senderData)
|
||||||
senderLists->dirty = true;
|
senderData->dirty = true;
|
||||||
|
|
||||||
QtPrivate::QSlotObjectBase *slotObj = nullptr;
|
QtPrivate::QSlotObjectBase *slotObj = nullptr;
|
||||||
if (node->isSlotObject) {
|
if (node->isSlotObject) {
|
||||||
@ -1010,6 +971,13 @@ QObject::~QObject()
|
|||||||
locker.relock();
|
locker.relock();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!--cd->inUse) {
|
||||||
|
delete cd;
|
||||||
|
} else {
|
||||||
|
cd->orphaned = true;
|
||||||
|
}
|
||||||
|
d->connections.store(nullptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!d->children.isEmpty())
|
if (!d->children.isEmpty())
|
||||||
@ -1239,6 +1207,10 @@ bool QObject::event(QEvent *e)
|
|||||||
{
|
{
|
||||||
QMetaCallEvent *mce = static_cast<QMetaCallEvent*>(e);
|
QMetaCallEvent *mce = static_cast<QMetaCallEvent*>(e);
|
||||||
|
|
||||||
|
if (!d_func()->connections.load()) {
|
||||||
|
QMutexLocker locker(signalSlotLock(this));
|
||||||
|
d_func()->ensureConnectionData();
|
||||||
|
}
|
||||||
QObjectPrivate::Sender sender(this, const_cast<QObject*>(mce->sender()), mce->signalId());
|
QObjectPrivate::Sender sender(this, const_cast<QObject*>(mce->sender()), mce->signalId());
|
||||||
|
|
||||||
mce->placeMetaCall(this);
|
mce->placeMetaCall(this);
|
||||||
@ -1543,9 +1515,10 @@ 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()
|
||||||
if (currentSender) {
|
ConnectionData *cd = connections.load();
|
||||||
currentSender->receiverDeleted();
|
if (cd && cd->currentSender) {
|
||||||
currentSender = nullptr;
|
cd->currentSender->receiverDeleted();
|
||||||
|
cd->currentSender = nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
// set new thread data
|
// set new thread data
|
||||||
@ -2354,12 +2327,13 @@ QObject *QObject::sender() const
|
|||||||
Q_D(const QObject);
|
Q_D(const QObject);
|
||||||
|
|
||||||
QMutexLocker locker(signalSlotLock(this));
|
QMutexLocker locker(signalSlotLock(this));
|
||||||
if (!d->currentSender)
|
QObjectPrivate::ConnectionData *cd = d->connections.load();
|
||||||
return 0;
|
if (!cd || !cd->currentSender)
|
||||||
|
return nullptr;
|
||||||
|
|
||||||
for (QObjectPrivate::Connection *c = d->senders; c; c = c->next) {
|
for (QObjectPrivate::Connection *c = cd->senders; c; c = c->next) {
|
||||||
if (c->sender == d->currentSender->sender)
|
if (c->sender == cd->currentSender->sender)
|
||||||
return d->currentSender->sender;
|
return cd->currentSender->sender;
|
||||||
}
|
}
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
@ -2395,13 +2369,14 @@ int QObject::senderSignalIndex() const
|
|||||||
Q_D(const QObject);
|
Q_D(const QObject);
|
||||||
|
|
||||||
QMutexLocker locker(signalSlotLock(this));
|
QMutexLocker locker(signalSlotLock(this));
|
||||||
if (!d->currentSender)
|
QObjectPrivate::ConnectionData *cd = d->connections.load();
|
||||||
|
if (!cd || !cd->currentSender)
|
||||||
return -1;
|
return -1;
|
||||||
|
|
||||||
for (QObjectPrivate::Connection *c = d->senders; c; c = c->next) {
|
for (QObjectPrivate::Connection *c = cd->senders; c; c = c->next) {
|
||||||
if (c->sender == d->currentSender->sender) {
|
if (c->sender == cd->currentSender->sender) {
|
||||||
// Convert from signal range to method range
|
// Convert from signal range to method range
|
||||||
return QMetaObjectPrivate::signal(c->sender->metaObject(), d->currentSender->signal).methodIndex();
|
return QMetaObjectPrivate::signal(c->sender->metaObject(), cd->currentSender->signal).methodIndex();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2433,7 +2408,8 @@ int QObject::receivers(const char *signal) const
|
|||||||
{
|
{
|
||||||
Q_D(const QObject);
|
Q_D(const QObject);
|
||||||
int receivers = 0;
|
int receivers = 0;
|
||||||
if (signal) {
|
QObjectPrivate::ConnectionData *cd = d->connections.load();
|
||||||
|
if (signal && cd) {
|
||||||
QByteArray signal_name = QMetaObject::normalizedSignature(signal);
|
QByteArray signal_name = QMetaObject::normalizedSignature(signal);
|
||||||
signal = signal_name;
|
signal = signal_name;
|
||||||
#ifndef QT_NO_DEBUG
|
#ifndef QT_NO_DEBUG
|
||||||
@ -2458,14 +2434,12 @@ int QObject::receivers(const char *signal) const
|
|||||||
}
|
}
|
||||||
|
|
||||||
QMutexLocker locker(signalSlotLock(this));
|
QMutexLocker locker(signalSlotLock(this));
|
||||||
if (d->connectionLists) {
|
if (signal_index < cd->signalVector.count()) {
|
||||||
if (signal_index < d->connectionLists->count()) {
|
const QObjectPrivate::Connection *c =
|
||||||
const QObjectPrivate::Connection *c =
|
cd->signalVector.at(signal_index).first;
|
||||||
d->connectionLists->at(signal_index).first;
|
while (c) {
|
||||||
while (c) {
|
receivers += c->receiver ? 1 : 0;
|
||||||
receivers += c->receiver ? 1 : 0;
|
c = c->nextConnectionList;
|
||||||
c = c->nextConnectionList;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -3269,23 +3243,21 @@ QObjectPrivate::Connection *QMetaObjectPrivate::connect(const QObject *sender,
|
|||||||
|
|
||||||
int method_offset = rmeta ? rmeta->methodOffset() : 0;
|
int method_offset = rmeta ? rmeta->methodOffset() : 0;
|
||||||
Q_ASSERT(!rmeta || QMetaObjectPrivate::get(rmeta)->revision >= 6);
|
Q_ASSERT(!rmeta || QMetaObjectPrivate::get(rmeta)->revision >= 6);
|
||||||
QObjectPrivate::StaticMetaCallFunction callFunction =
|
QObjectPrivate::StaticMetaCallFunction callFunction = rmeta ? rmeta->d.static_metacall : nullptr;
|
||||||
rmeta ? rmeta->d.static_metacall : 0;
|
|
||||||
|
|
||||||
QOrderedMutexLocker locker(signalSlotLock(sender),
|
QOrderedMutexLocker locker(signalSlotLock(sender),
|
||||||
signalSlotLock(receiver));
|
signalSlotLock(receiver));
|
||||||
|
|
||||||
if (type & Qt::UniqueConnection) {
|
QObjectPrivate::ConnectionData *scd = QObjectPrivate::get(s)->connections.load();
|
||||||
QObjectConnectionListVector *connectionLists = QObjectPrivate::get(s)->connectionLists;
|
if (type & Qt::UniqueConnection && scd) {
|
||||||
if (connectionLists && connectionLists->count() > signal_index) {
|
if (scd->signalVector.count() > signal_index) {
|
||||||
const QObjectPrivate::Connection *c2 =
|
const QObjectPrivate::Connection *c2 = scd->signalVector.at(signal_index).first;
|
||||||
(*connectionLists)[signal_index].first;
|
|
||||||
|
|
||||||
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 == receiver && c2->method() == method_index_absolute)
|
||||||
return 0;
|
return nullptr;
|
||||||
c2 = c2->nextConnectionList;
|
c2 = c2->nextConnectionList;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -3409,37 +3381,35 @@ bool QMetaObjectPrivate::disconnect(const QObject *sender,
|
|||||||
QMutex *senderMutex = signalSlotLock(sender);
|
QMutex *senderMutex = signalSlotLock(sender);
|
||||||
QMutexLocker locker(senderMutex);
|
QMutexLocker locker(senderMutex);
|
||||||
|
|
||||||
QObjectConnectionListVector *connectionLists = QObjectPrivate::get(s)->connectionLists;
|
QObjectPrivate::ConnectionData *scd = QObjectPrivate::get(s)->connections.load();
|
||||||
if (!connectionLists)
|
if (!scd)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
// prevent incoming connections changing the connectionLists while unlocked
|
// prevent incoming connections changing the connections->receivers while unlocked
|
||||||
++connectionLists->inUse;
|
++scd->inUse;
|
||||||
|
|
||||||
bool success = false;
|
bool success = false;
|
||||||
if (signal_index < 0) {
|
if (signal_index < 0) {
|
||||||
// remove from all connection lists
|
// remove from all connection lists
|
||||||
for (int sig_index = -1; sig_index < connectionLists->count(); ++sig_index) {
|
for (int sig_index = -1; sig_index < scd->signalVector.count(); ++sig_index) {
|
||||||
QObjectPrivate::Connection *c =
|
QObjectPrivate::Connection *c = scd->connectionsForSignal(sig_index).first;
|
||||||
(*connectionLists)[sig_index].first;
|
|
||||||
if (disconnectHelper(c, receiver, method_index, slot, senderMutex, disconnectType)) {
|
if (disconnectHelper(c, receiver, method_index, slot, senderMutex, disconnectType)) {
|
||||||
success = true;
|
success = true;
|
||||||
connectionLists->dirty = true;
|
scd->dirty = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if (signal_index < connectionLists->count()) {
|
} else if (signal_index < scd->signalVector.count()) {
|
||||||
QObjectPrivate::Connection *c =
|
QObjectPrivate::Connection *c = scd->signalVector.at(signal_index).first;
|
||||||
(*connectionLists)[signal_index].first;
|
|
||||||
if (disconnectHelper(c, receiver, method_index, slot, senderMutex, disconnectType)) {
|
if (disconnectHelper(c, receiver, method_index, slot, senderMutex, disconnectType)) {
|
||||||
success = true;
|
success = true;
|
||||||
connectionLists->dirty = true;
|
scd->dirty = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
--connectionLists->inUse;
|
--scd->inUse;
|
||||||
Q_ASSERT(connectionLists->inUse >= 0);
|
Q_ASSERT(scd->inUse >= 0);
|
||||||
if (connectionLists->orphaned && !connectionLists->inUse)
|
if (scd->orphaned && !scd->inUse)
|
||||||
delete connectionLists;
|
delete scd;
|
||||||
|
|
||||||
locker.unlock();
|
locker.unlock();
|
||||||
if (success) {
|
if (success) {
|
||||||
@ -3651,35 +3621,36 @@ void doActivate(QObject *sender, int signal_index, void **argv)
|
|||||||
|
|
||||||
{
|
{
|
||||||
QMutexLocker locker(signalSlotLock(sender));
|
QMutexLocker locker(signalSlotLock(sender));
|
||||||
struct ConnectionListsRef {
|
struct ConnectionDataRef {
|
||||||
QObjectConnectionListVector *connectionLists;
|
QObjectPrivate::ConnectionData *connections;
|
||||||
ConnectionListsRef(QObjectConnectionListVector *connectionLists) : connectionLists(connectionLists)
|
ConnectionDataRef(QObjectPrivate::ConnectionData *connections) : connections(connections)
|
||||||
{
|
{
|
||||||
if (connectionLists)
|
if (connections)
|
||||||
++connectionLists->inUse;
|
++connections->inUse;
|
||||||
}
|
}
|
||||||
~ConnectionListsRef()
|
~ConnectionDataRef()
|
||||||
{
|
{
|
||||||
if (!connectionLists)
|
if (!connections)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
--connectionLists->inUse;
|
--connections->inUse;
|
||||||
Q_ASSERT(connectionLists->inUse >= 0);
|
Q_ASSERT(connections->inUse >= 0);
|
||||||
if (connectionLists->orphaned) {
|
if (connections->orphaned) {
|
||||||
if (!connectionLists->inUse)
|
if (!connections->inUse)
|
||||||
delete connectionLists;
|
delete connections;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
QObjectConnectionListVector *operator->() const { return connectionLists; }
|
QObjectPrivate::ConnectionData *operator->() const { return connections; }
|
||||||
};
|
};
|
||||||
ConnectionListsRef connectionLists = sp->connectionLists;
|
Q_ASSERT(sp->connections.load());
|
||||||
|
ConnectionDataRef connections = sp->connections.load();
|
||||||
|
|
||||||
const QObjectPrivate::ConnectionList *list;
|
const QObjectPrivate::ConnectionList *list;
|
||||||
if (signal_index < connectionLists->count())
|
if (signal_index < connections->signalVector.count())
|
||||||
list = &connectionLists->at(signal_index);
|
list = &connections->signalVector.at(signal_index);
|
||||||
else
|
else
|
||||||
list = &connectionLists->allsignals;
|
list = &connections->allsignals;
|
||||||
|
|
||||||
Qt::HANDLE currentThreadId = QThread::currentThreadId();
|
Qt::HANDLE currentThreadId = QThread::currentThreadId();
|
||||||
|
|
||||||
@ -3773,15 +3744,15 @@ void doActivate(QObject *sender, int signal_index, void **argv)
|
|||||||
locker.relock();
|
locker.relock();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (connectionLists->orphaned)
|
if (connections->orphaned)
|
||||||
break;
|
break;
|
||||||
} while (c != last && (c = c->nextConnectionList) != 0);
|
} while (c != last && (c = c->nextConnectionList) != 0);
|
||||||
|
|
||||||
if (connectionLists->orphaned)
|
if (connections->orphaned)
|
||||||
break;
|
break;
|
||||||
} while (list != &connectionLists->allsignals &&
|
} while (list != &connections->allsignals &&
|
||||||
//start over for all signals;
|
//start over for all signals;
|
||||||
((list = &connectionLists->allsignals), true));
|
((list = &connections->allsignals), true));
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -3832,7 +3803,7 @@ void QMetaObject::activate(QObject *sender, int signal_index, void **argv)
|
|||||||
|
|
||||||
/*!
|
/*!
|
||||||
\internal
|
\internal
|
||||||
Returns the signal index used in the internal connectionLists vector.
|
Returns the signal index used in the internal connections->receivers vector.
|
||||||
|
|
||||||
It is different from QMetaObject::indexOfSignal(): indexOfSignal is the same as indexOfMethod
|
It is different from QMetaObject::indexOfSignal(): indexOfSignal is the same as indexOfMethod
|
||||||
while QObjectPrivate::signalIndex is smaller because it doesn't give index to slots.
|
while QObjectPrivate::signalIndex is smaller because it doesn't give index to slots.
|
||||||
@ -4077,14 +4048,14 @@ void QObject::dumpObjectInfo() const
|
|||||||
// first, look for connections where this object is the sender
|
// first, look for connections where this object is the sender
|
||||||
qDebug(" SIGNALS OUT");
|
qDebug(" SIGNALS OUT");
|
||||||
|
|
||||||
if (d->connectionLists) {
|
QObjectPrivate::ConnectionData *cd = d->connections.load();
|
||||||
for (int signal_index = 0; signal_index < d->connectionLists->count(); ++signal_index) {
|
if (cd && cd->signalVector.count()) {
|
||||||
|
for (int signal_index = 0; signal_index < cd->signalVector.count(); ++signal_index) {
|
||||||
const QMetaMethod signal = QMetaObjectPrivate::signal(metaObject(), signal_index);
|
const QMetaMethod signal = QMetaObjectPrivate::signal(metaObject(), signal_index);
|
||||||
qDebug(" signal: %s", signal.methodSignature().constData());
|
qDebug(" signal: %s", signal.methodSignature().constData());
|
||||||
|
|
||||||
// receivers
|
// receivers
|
||||||
const QObjectPrivate::Connection *c =
|
const QObjectPrivate::Connection *c = cd->signalVector.at(signal_index).first;
|
||||||
d->connectionLists->at(signal_index).first;
|
|
||||||
while (c) {
|
while (c) {
|
||||||
if (!c->receiver) {
|
if (!c->receiver) {
|
||||||
qDebug(" <Disconnected receiver>");
|
qDebug(" <Disconnected receiver>");
|
||||||
@ -4112,8 +4083,8 @@ void QObject::dumpObjectInfo() const
|
|||||||
// now look for connections where this object is the receiver
|
// now look for connections where this object is the receiver
|
||||||
qDebug(" SIGNALS IN");
|
qDebug(" SIGNALS IN");
|
||||||
|
|
||||||
if (d->senders) {
|
if (cd && cd->senders) {
|
||||||
for (QObjectPrivate::Connection *s = d->senders; s; s = s->next) {
|
for (QObjectPrivate::Connection *s = cd->senders; s; s = s->next) {
|
||||||
QByteArray slotName = QByteArrayLiteral("<unknown>");
|
QByteArray slotName = QByteArrayLiteral("<unknown>");
|
||||||
if (!s->isSlotObject) {
|
if (!s->isSlotObject) {
|
||||||
const QMetaMethod slot = metaObject()->method(s->method());
|
const QMetaMethod slot = metaObject()->method(s->method());
|
||||||
@ -4848,11 +4819,10 @@ QMetaObject::Connection QObjectPrivate::connectImpl(const QObject *sender, int s
|
|||||||
QOrderedMutexLocker locker(signalSlotLock(sender),
|
QOrderedMutexLocker locker(signalSlotLock(sender),
|
||||||
signalSlotLock(receiver));
|
signalSlotLock(receiver));
|
||||||
|
|
||||||
if (type & Qt::UniqueConnection && slot) {
|
if (type & Qt::UniqueConnection && slot && QObjectPrivate::get(s)->connections.load()) {
|
||||||
QObjectConnectionListVector *connectionLists = QObjectPrivate::get(s)->connectionLists;
|
QObjectPrivate::ConnectionData *connections = QObjectPrivate::get(s)->connections.load();
|
||||||
if (connectionLists && connectionLists->count() > signal_index) {
|
if (connections->signalVector.count() > signal_index) {
|
||||||
const QObjectPrivate::Connection *c2 =
|
const QObjectPrivate::Connection *c2 = connections->signalVector.at(signal_index).first;
|
||||||
(*connectionLists)[signal_index].first;
|
|
||||||
|
|
||||||
while (c2) {
|
while (c2) {
|
||||||
if (c2->receiver == receiver && c2->isSlotObject && c2->slotObj->compare(slot)) {
|
if (c2->receiver == receiver && c2->isSlotObject && c2->slotObj->compare(slot)) {
|
||||||
@ -4909,14 +4879,14 @@ bool QObject::disconnect(const QMetaObject::Connection &connection)
|
|||||||
{
|
{
|
||||||
QOrderedMutexLocker locker(senderMutex, receiverMutex);
|
QOrderedMutexLocker locker(senderMutex, receiverMutex);
|
||||||
|
|
||||||
QObjectConnectionListVector *connectionLists = QObjectPrivate::get(c->sender)->connectionLists;
|
QObjectPrivate::ConnectionData *connections = QObjectPrivate::get(c->sender)->connections.load();
|
||||||
Q_ASSERT(connectionLists);
|
Q_ASSERT(connections);
|
||||||
connectionLists->dirty = true;
|
connections->dirty = true;
|
||||||
|
|
||||||
*c->prev = c->next;
|
*c->prev = c->next;
|
||||||
if (c->next)
|
if (c->next)
|
||||||
c->next->prev = c->prev;
|
c->next->prev = c->prev;
|
||||||
c->receiver = 0;
|
c->receiver = nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
// destroy the QSlotObject, if possible
|
// destroy the QSlotObject, if possible
|
||||||
@ -4928,7 +4898,7 @@ bool QObject::disconnect(const QMetaObject::Connection &connection)
|
|||||||
c->sender->disconnectNotify(QMetaObjectPrivate::signal(c->sender->metaObject(),
|
c->sender->disconnectNotify(QMetaObjectPrivate::signal(c->sender->metaObject(),
|
||||||
c->signal_index));
|
c->signal_index));
|
||||||
|
|
||||||
const_cast<QMetaObject::Connection &>(connection).d_ptr = 0;
|
const_cast<QMetaObject::Connection &>(connection).d_ptr = nullptr;
|
||||||
c->deref(); // has been removed from the QMetaObject::Connection object
|
c->deref(); // has been removed from the QMetaObject::Connection object
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
|
@ -171,14 +171,15 @@ public:
|
|||||||
: receiver(receiver), sender(sender), signal(signal)
|
: receiver(receiver), sender(sender), signal(signal)
|
||||||
{
|
{
|
||||||
if (receiver) {
|
if (receiver) {
|
||||||
previous = receiver->d_func()->currentSender;
|
ConnectionData *cd = receiver->d_func()->connections.load();
|
||||||
receiver->d_func()->currentSender = this;
|
previous = cd->currentSender;
|
||||||
|
cd->currentSender = this;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
~Sender()
|
~Sender()
|
||||||
{
|
{
|
||||||
if (receiver)
|
if (receiver)
|
||||||
receiver->d_func()->currentSender = previous;
|
receiver->d_func()->connections.load()->currentSender = previous;
|
||||||
}
|
}
|
||||||
void receiverDeleted()
|
void receiverDeleted()
|
||||||
{
|
{
|
||||||
@ -194,6 +195,34 @@ public:
|
|||||||
int signal;
|
int signal;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
This contains the all connections from and to an object.
|
||||||
|
|
||||||
|
The signalVector contains the lists of connections for a given signal. The index in the vector correspond
|
||||||
|
to the signal index. The signal index is the one returned by QObjectPrivate::signalIndex (not
|
||||||
|
QMetaObject::indexOfSignal). allsignals contains a list of special connections that will get invoked on
|
||||||
|
any signal emission. This is done by connecting to signal index -1.
|
||||||
|
|
||||||
|
This vector is protected by the object mutex (signalSlotLock())
|
||||||
|
|
||||||
|
Each Connection is also part of a 'senders' linked list. This one contains all connections connected
|
||||||
|
to a slot in this object. The mutex of the receiver must be locked when touching the pointers of this
|
||||||
|
linked list.
|
||||||
|
*/
|
||||||
|
struct ConnectionData {
|
||||||
|
bool orphaned = false; //the QObject owner of this vector has been destroyed while the vector was inUse
|
||||||
|
bool dirty = false; //some Connection have been disconnected (their receiver is 0) but not removed from the list yet
|
||||||
|
int inUse = 0; //number of functions that are currently accessing this object or its connections
|
||||||
|
ConnectionList allsignals;
|
||||||
|
QVector<ConnectionList> signalVector;
|
||||||
|
Connection *senders = nullptr;
|
||||||
|
Sender *currentSender = nullptr; // object currently activating the object
|
||||||
|
|
||||||
|
ConnectionList &connectionsForSignal(int signal)
|
||||||
|
{
|
||||||
|
return signal < 0 ? allsignals : signalVector[signal];
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
QObjectPrivate(int version = QObjectPrivateVersion);
|
QObjectPrivate(int version = QObjectPrivateVersion);
|
||||||
virtual ~QObjectPrivate();
|
virtual ~QObjectPrivate();
|
||||||
@ -240,14 +269,18 @@ public:
|
|||||||
const int *types, const QMetaObject *senderMetaObject);
|
const int *types, const QMetaObject *senderMetaObject);
|
||||||
static QMetaObject::Connection connect(const QObject *sender, int signal_index, QtPrivate::QSlotObjectBase *slotObj, Qt::ConnectionType type);
|
static QMetaObject::Connection connect(const QObject *sender, int signal_index, QtPrivate::QSlotObjectBase *slotObj, Qt::ConnectionType type);
|
||||||
static bool disconnect(const QObject *sender, int signal_index, void **slot);
|
static bool disconnect(const QObject *sender, int signal_index, void **slot);
|
||||||
|
|
||||||
|
void ensureConnectionData()
|
||||||
|
{
|
||||||
|
if (connections.load())
|
||||||
|
return;
|
||||||
|
connections.store(new ConnectionData);
|
||||||
|
}
|
||||||
public:
|
public:
|
||||||
ExtraData *extraData; // extra data set by the user
|
ExtraData *extraData; // extra data set by the user
|
||||||
QThreadData *threadData; // id of the thread that owns the object
|
QThreadData *threadData; // id of the thread that owns the object
|
||||||
|
|
||||||
QObjectConnectionListVector *connectionLists;
|
QAtomicPointer<ConnectionData> connections;
|
||||||
|
|
||||||
Connection *senders; // linked list of connections connected to this object
|
|
||||||
Sender *currentSender; // object currently activating the object
|
|
||||||
|
|
||||||
union {
|
union {
|
||||||
QObject *currentChildBeingDeleted; // should only be used when QObjectData::isDeletingChildren is set
|
QObject *currentChildBeingDeleted; // should only be used when QObjectData::isDeletingChildren is set
|
||||||
|
@ -126,9 +126,9 @@ void tst_toolsupport::offsets_data()
|
|||||||
#ifdef Q_PROCESSOR_X86
|
#ifdef Q_PROCESSOR_X86
|
||||||
// x86 32-bit has weird alignment rules. Refer to QtPrivate::AlignOf in
|
// x86 32-bit has weird alignment rules. Refer to QtPrivate::AlignOf in
|
||||||
// qglobal.h for more details.
|
// qglobal.h for more details.
|
||||||
data << 160 << 240;
|
data << 152 << 224;
|
||||||
#else
|
#else
|
||||||
data << 164 << 240;
|
data << 156 << 224;
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
Loading…
x
Reference in New Issue
Block a user