--- src/corelib/kernel/qobject.cpp 2022-07-05 08:22:47.935049200 +0200 +++ /cygdrive/c/Qt/6.4.0/Src/qtbase/src/corelib/kernel/qobject.cpp 2022-05-17 10:19:41.000000000 +0200 @@ -49,6 +49,219 @@ Q_CORE_EXPORT QBasicAtomicPointer qt_signal_spy_callback_set = Q_BASIC_ATOMIC_INITIALIZER(nullptr); +// ConnectionList is a singly-linked list +struct QObjectPrivate::ConnectionList +{ + QAtomicPointer first; + QAtomicPointer last; +}; +static_assert(std::is_trivially_destructible_v); +Q_DECLARE_TYPEINFO(QObjectPrivate::ConnectionList, Q_RELOCATABLE_TYPE); + +struct QObjectPrivate::ConnectionOrSignalVector +{ + union { + // linked list of orphaned connections that need cleaning up + ConnectionOrSignalVector *nextInOrphanList; + // linked list of connections connected to slots in this object + Connection *next; + }; + + static SignalVector *asSignalVector(ConnectionOrSignalVector *c) + { + if (reinterpret_cast(c) & 1) + return reinterpret_cast(reinterpret_cast(c) & ~quintptr(1u)); + return nullptr; + } + static Connection *fromSignalVector(SignalVector *v) { + return reinterpret_cast(reinterpret_cast(v) | quintptr(1u)); + } +}; +static_assert(std::is_trivial_v); + +struct QObjectPrivate::Connection : public ConnectionOrSignalVector +{ + // linked list of connections connected to slots in this object, next is in base class + Connection **prev; + // linked list of connections connected to signals in this object + QAtomicPointer nextConnectionList; + Connection *prevConnectionList; + + QObject *sender; + QAtomicPointer receiver; + QAtomicPointer receiverThreadData; + union { + StaticMetaCallFunction callFunction; + QtPrivate::QSlotObjectBase *slotObj; + }; + QAtomicPointer argumentTypes; + QAtomicInt ref_{2}; //ref_ is 2 for the use in the internal lists, and for the use in QMetaObject::Connection + uint id = 0; + ushort method_offset; + ushort method_relative; + signed int signal_index : 27; // In signal range (see QObjectPrivate::signalIndex()) + ushort connectionType : 2; // 0 == auto, 1 == direct, 2 == queued, 3 == blocking + ushort isSlotObject : 1; + ushort ownArgumentTypes : 1; + ushort isSingleShot : 1; + Connection() : ownArgumentTypes(true) { } + ~Connection(); + int method() const { Q_ASSERT(!isSlotObject); return method_offset + method_relative; } + void ref() { ref_.ref(); } + void freeSlotObject() + { + if (isSlotObject) { + slotObj->destroyIfLastRef(); + isSlotObject = false; + } + } + void deref() + { + if (!ref_.deref()) { + Q_ASSERT(!receiver.loadRelaxed()); + Q_ASSERT(!isSlotObject); + delete this; + } + } +}; +Q_DECLARE_TYPEINFO(QObjectPrivate::Connection, Q_RELOCATABLE_TYPE); + +struct QObjectPrivate::SignalVector : public ConnectionOrSignalVector +{ + quintptr allocated; + // ConnectionList signals[] + ConnectionList &at(int i) + { + return reinterpret_cast(this + 1)[i + 1]; + } + const ConnectionList &at(int i) const + { + return reinterpret_cast(this + 1)[i + 1]; + } + int count() const { return static_cast(allocated); } +}; +static_assert(std::is_trivial_v); // it doesn't need to be, but it helps + +struct QObjectPrivate::ConnectionData +{ + // the id below is used to avoid activating new connections. When the object gets + // deleted it's set to 0, so that signal emission stops + QAtomicInteger currentConnectionId; + QAtomicInt ref; + QAtomicPointer signalVector; + Connection *senders = nullptr; + Sender *currentSender = nullptr; // object currently activating the object + QAtomicPointer orphaned; + + ~ConnectionData() + { + Q_ASSERT(ref.loadRelaxed() == 0); + auto *c = orphaned.fetchAndStoreRelaxed(nullptr); + if (c) + deleteOrphaned(c); + SignalVector *v = signalVector.loadRelaxed(); + if (v) { + v->~SignalVector(); + free(v); + } + } + + // must be called on the senders connection data + // assumes the senders and receivers lock are held + void removeConnection(Connection *c); + enum LockPolicy { + NeedToLock, + // Beware that we need to temporarily release the lock + // and thus calling code must carefully consider whether + // invariants still hold. + AlreadyLockedAndTemporarilyReleasingLock + }; + void cleanOrphanedConnections(QObject *sender, LockPolicy lockPolicy = NeedToLock) + { + if (orphaned.loadRelaxed() && ref.loadAcquire() == 1) + cleanOrphanedConnectionsImpl(sender, lockPolicy); + } + void cleanOrphanedConnectionsImpl(QObject *sender, LockPolicy lockPolicy); + + ConnectionList &connectionsForSignal(int signal) + { + return signalVector.loadRelaxed()->at(signal); + } + + void resizeSignalVector(uint size) + { + SignalVector *vector = this->signalVector.loadRelaxed(); + if (vector && vector->allocated > size) + return; + size = (size + 7) & ~7; + void *ptr = malloc(sizeof(SignalVector) + (size + 1) * sizeof(ConnectionList)); + auto newVector = new (ptr) SignalVector; + + int start = -1; + if (vector) { + // not (yet) existing trait: + //static_assert(std::is_relocatable_v); + //static_assert(std::is_relocatable_v); + memcpy(newVector, vector, sizeof(SignalVector) + (vector->allocated + 1) * sizeof(ConnectionList)); + start = vector->count(); + } + for (int i = start; i < int(size); ++i) + new (&newVector->at(i)) ConnectionList(); + newVector->next = nullptr; + newVector->allocated = size; + + signalVector.storeRelaxed(newVector); + if (vector) { + Connection *o = nullptr; + /* No ABA issue here: When adding a node, we only care about the list head, it doesn't + * matter if the tail changes. + */ + do { + o = orphaned.loadRelaxed(); + vector->nextInOrphanList = o; + } while (!orphaned.testAndSetRelease(o, ConnectionOrSignalVector::fromSignalVector(vector))); + + } + } + int signalVectorCount() const + { + return signalVector.loadAcquire() ? signalVector.loadRelaxed()->count() : -1; + } + + static void deleteOrphaned(ConnectionOrSignalVector *c); +}; + +struct QObjectPrivate::Sender +{ + Sender(QObject *receiver, QObject *sender, int signal) + : receiver(receiver), sender(sender), signal(signal) + { + if (receiver) { + ConnectionData *cd = receiver->d_func()->connections.loadRelaxed(); + previous = cd->currentSender; + cd->currentSender = this; + } + } + ~Sender() + { + if (receiver) + receiver->d_func()->connections.loadRelaxed()->currentSender = previous; + } + void receiverDeleted() + { + Sender *s = this; + while (s) { + s->receiver = nullptr; + s = s->previous; + } + } + Sender *previous; + QObject *receiver; + QObject *sender; + int signal; +}; +Q_DECLARE_TYPEINFO(QObjectPrivate::Sender, Q_RELOCATABLE_TYPE); + void qt_register_signal_spy_callbacks(QSignalSpyCallbackSet *callback_set) { qt_signal_spy_callback_set.storeRelease(callback_set); --- src/corelib/kernel/qobject_p.h 2022-07-05 08:24:21.485399000 +0200 +++ /cygdrive/c/Qt/6.4.0/Src/qtbase/src/corelib/kernel/qobject_p.h 2022-05-17 10:19:41.000000000 +0200 @@ -464,221 +464,6 @@ return &ed->parent->bindingStorage; } -// ConnectionList is a singly-linked list -struct QObjectPrivate::ConnectionList -{ - QAtomicPointer first; - QAtomicPointer last; -}; -static_assert(std::is_trivially_destructible_v); -Q_DECLARE_TYPEINFO(QObjectPrivate::ConnectionList, Q_RELOCATABLE_TYPE); - -struct QObjectPrivate::ConnectionOrSignalVector -{ - union { - // linked list of orphaned connections that need cleaning up - ConnectionOrSignalVector *nextInOrphanList; - // linked list of connections connected to slots in this object - Connection *next; - }; - - static SignalVector *asSignalVector(ConnectionOrSignalVector *c) - { - if (reinterpret_cast(c) & 1) - return reinterpret_cast(reinterpret_cast(c) & ~quintptr(1u)); - return nullptr; - } - static Connection *fromSignalVector(SignalVector *v) { - return reinterpret_cast(reinterpret_cast(v) | quintptr(1u)); - } -}; -static_assert(std::is_trivial_v); - -// needed elsewhere to stay in private API -struct QObjectPrivate::Connection : public ConnectionOrSignalVector -{ - // linked list of connections connected to slots in this object, next is in base class - Connection **prev; - // linked list of connections connected to signals in this object - QAtomicPointer nextConnectionList; - Connection *prevConnectionList; - - QObject *sender; - QAtomicPointer receiver; - QAtomicPointer receiverThreadData; - union { - StaticMetaCallFunction callFunction; - QtPrivate::QSlotObjectBase *slotObj; - }; - QAtomicPointer argumentTypes; - QAtomicInt ref_{2}; //ref_ is 2 for the use in the internal lists, and for the use in QMetaObject::Connection - uint id = 0; - ushort method_offset; - ushort method_relative; - signed int signal_index : 27; // In signal range (see QObjectPrivate::signalIndex()) - ushort connectionType : 2; // 0 == auto, 1 == direct, 2 == queued, 3 == blocking - ushort isSlotObject : 1; - ushort ownArgumentTypes : 1; - ushort isSingleShot : 1; - Connection() : ownArgumentTypes(true) { } - ~Connection(); - int method() const { Q_ASSERT(!isSlotObject); return method_offset + method_relative; } - void ref() { ref_.ref(); } - void freeSlotObject() - { - if (isSlotObject) { - slotObj->destroyIfLastRef(); - isSlotObject = false; - } - } - void deref() - { - if (!ref_.deref()) { - Q_ASSERT(!receiver.loadRelaxed()); - Q_ASSERT(!isSlotObject); - delete this; - } - } -}; -Q_DECLARE_TYPEINFO(QObjectPrivate::Connection, Q_RELOCATABLE_TYPE); - -struct QObjectPrivate::SignalVector : public ConnectionOrSignalVector -{ - quintptr allocated; - // ConnectionList signals[] - ConnectionList &at(int i) - { - return reinterpret_cast(this + 1)[i + 1]; - } - const ConnectionList &at(int i) const - { - return reinterpret_cast(this + 1)[i + 1]; - } - int count() const { return static_cast(allocated); } -}; -static_assert(std::is_trivial_v); // it doesn't need to be, but it helps - -struct QObjectPrivate::ConnectionData -{ - // the id below is used to avoid activating new connections. When the object gets - // deleted it's set to 0, so that signal emission stops - QAtomicInteger currentConnectionId; - QAtomicInt ref; - QAtomicPointer signalVector; - Connection *senders = nullptr; - Sender *currentSender = nullptr; // object currently activating the object - QAtomicPointer orphaned; - - ~ConnectionData() - { - Q_ASSERT(ref.loadRelaxed() == 0); - auto *c = orphaned.fetchAndStoreRelaxed(nullptr); - if (c) - deleteOrphaned(c); - SignalVector *v = signalVector.loadRelaxed(); - if (v) { - v->~SignalVector(); - free(v); - } - } - - // must be called on the senders connection data - // assumes the senders and receivers lock are held - void removeConnection(Connection *c); - enum LockPolicy { - NeedToLock, - // Beware that we need to temporarily release the lock - // and thus calling code must carefully consider whether - // invariants still hold. - AlreadyLockedAndTemporarilyReleasingLock - }; - void cleanOrphanedConnections(QObject *sender, LockPolicy lockPolicy = NeedToLock) - { - if (orphaned.loadRelaxed() && ref.loadAcquire() == 1) - cleanOrphanedConnectionsImpl(sender, lockPolicy); - } - void cleanOrphanedConnectionsImpl(QObject *sender, LockPolicy lockPolicy); - - ConnectionList &connectionsForSignal(int signal) - { - return signalVector.loadRelaxed()->at(signal); - } - - void resizeSignalVector(uint size) - { - SignalVector *vector = this->signalVector.loadRelaxed(); - if (vector && vector->allocated > size) - return; - size = (size + 7) & ~7; - void *ptr = malloc(sizeof(SignalVector) + (size + 1) * sizeof(ConnectionList)); - auto newVector = new (ptr) SignalVector; - - int start = -1; - if (vector) { - // not (yet) existing trait: - //static_assert(std::is_relocatable_v); - //static_assert(std::is_relocatable_v); - memcpy(newVector, vector, sizeof(SignalVector) + (vector->allocated + 1) * sizeof(ConnectionList)); - start = vector->count(); - } - for (int i = start; i < int(size); ++i) - new (&newVector->at(i)) ConnectionList(); - newVector->next = nullptr; - newVector->allocated = size; - - signalVector.storeRelaxed(newVector); - if (vector) { - Connection *o = nullptr; - /* No ABA issue here: When adding a node, we only care about the list head, it doesn't - * matter if the tail changes. - */ - do { - o = orphaned.loadRelaxed(); - vector->nextInOrphanList = o; - } while (!orphaned.testAndSetRelease(o, ConnectionOrSignalVector::fromSignalVector(vector))); - - } - } - int signalVectorCount() const - { - return signalVector.loadAcquire() ? signalVector.loadRelaxed()->count() : -1; - } - - static void deleteOrphaned(ConnectionOrSignalVector *c); -}; - -// needed elsewhere to stay in private API -struct QObjectPrivate::Sender -{ - Sender(QObject *receiver, QObject *sender, int signal) - : receiver(receiver), sender(sender), signal(signal) - { - if (receiver) { - ConnectionData *cd = receiver->d_func()->connections.loadRelaxed(); - previous = cd->currentSender; - cd->currentSender = this; - } - } - ~Sender() - { - if (receiver) - receiver->d_func()->connections.loadRelaxed()->currentSender = previous; - } - void receiverDeleted() - { - Sender *s = this; - while (s) { - s->receiver = nullptr; - s = s->previous; - } - } - Sender *previous; - QObject *receiver; - QObject *sender; - int signal; -}; -Q_DECLARE_TYPEINFO(QObjectPrivate::Sender, Q_RELOCATABLE_TYPE); - QT_END_NAMESPACE #endif // QOBJECT_P_H