Revision control

++ // Copyright (C) 2019 The Qt Company Ltd.
++ // Copyright (C) 2013 Olivier Goffart <ogoffart@woboq.com>
++ // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
++ // Qt-Security score:significant reason:default
++
++ #ifndef QOBJECT_P_P_H
++ #define QOBJECT_P_P_H
++
++ //
++ // W A R N I N G
++ // -------------
++ //
++ // This file is not part of the Qt API. It exists for the convenience
++ // of qobject.cpp. This header file may change from version to version
++ // without notice, or even be removed.
++ //
++ // We mean it.
++ //
++
++ // Even though this file is only used by qobject.cpp, the only reason this
++ // code lives here is that some special apps/libraries for e.g., QtJambi,
++ // Gammaray need access to the structs in this file.
++
++ #include <QtCore/qalloc.h>
++ #include <QtCore/qobject.h>
++ #include <QtCore/private/qobject_p.h>
++
++ QT_BEGIN_NAMESPACE
++
++ // ConnectionList is a singly-linked list
++ struct QObjectPrivate::ConnectionList
++ {
++ QAtomicPointer<Connection> first;
++ QAtomicPointer<Connection> last;
++ };
++ static_assert(std::is_trivially_destructible_v<QObjectPrivate::ConnectionList>);
++ Q_DECLARE_TYPEINFO(QObjectPrivate::ConnectionList, Q_RELOCATABLE_TYPE);
++
++ struct QObjectPrivate::TaggedSignalVector
++ {
++ quintptr c;
++
++ TaggedSignalVector() = default;
++ TaggedSignalVector(std::nullptr_t) noexcept : c(0) {}
++ TaggedSignalVector(Connection *v) noexcept : c(reinterpret_cast<quintptr>(v)) { Q_ASSERT(v && (reinterpret_cast<quintptr>(v) & 0x1) == 0); }
++ TaggedSignalVector(SignalVector *v) noexcept : c(reinterpret_cast<quintptr>(v) | quintptr(1u)) { Q_ASSERT(v); }
++ explicit operator SignalVector *() const noexcept
++ {
++ if (c & 0x1)
++ return reinterpret_cast<SignalVector *>(c & ~quintptr(1u));
++ return nullptr;
++ }
++ explicit operator Connection *() const noexcept
++ {
++ return reinterpret_cast<Connection *>(c);
++ }
++ operator uintptr_t() const noexcept { return c; }
++ };
++
++ struct QObjectPrivate::ConnectionOrSignalVector
++ {
++ union {
++ // linked list of orphaned connections that need cleaning up
++ TaggedSignalVector nextInOrphanList;
++ // linked list of connections connected to slots in this object
++ Connection *next;
++ };
++ };
++ static_assert(std::is_trivially_copyable_v<QObjectPrivate::ConnectionOrSignalVector>);
++
++ 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<Connection> nextConnectionList;
++ Connection *prevConnectionList;
++
++ QObject *sender;
++ QAtomicPointer<QObject> receiver;
++ QAtomicPointer<QThreadData> receiverThreadData;
++ union {
++ StaticMetaCallFunction callFunction;
++ QtPrivate::QSlotObjectBase *slotObj;
++ };
++ QAtomicPointer<const int> argumentTypes;
++ QAtomicInt ref_{
++ 2
++ }; // ref_ is 2 for the use in the internal lists, and for the use in QMetaObject::Connection
++ QAtomicInteger<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) {
++ receiver.storeRelaxed(nullptr);
++ receiverThreadData.storeRelaxed(nullptr);
++ }
++ ~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<ConnectionList *>(this + 1)[i + 1]; }
++ const ConnectionList &at(int i) const
++ {
++ return reinterpret_cast<const ConnectionList *>(this + 1)[i + 1];
++ }
++ int count() const { return static_cast<int>(allocated); }
++ };
++ // it doesn't need to be, but it helps
++ static_assert(std::is_trivially_copyable_v<QObjectPrivate::SignalVector>);
++
++ 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<uint> currentConnectionId;
++ QAtomicInt ref;
++ QAtomicPointer<SignalVector> signalVector;
++ Connection *senders = nullptr;
++ Sender *currentSender = nullptr; // object currently activating the object
++ std::atomic<TaggedSignalVector> orphaned = {nullptr};
++
++ ~ConnectionData()
++ {
++ Q_ASSERT(ref.loadRelaxed() == 0);
++ TaggedSignalVector c = orphaned.exchange(nullptr, std::memory_order_relaxed);
++ if (c)
++ deleteOrphaned(c);
++ SignalVector *v = signalVector.loadRelaxed();
++ if (v) {
++ const size_t allocSize = sizeof(SignalVector) + (v->allocated + 1) * sizeof(ConnectionList);
++ v->~SignalVector();
++ QtPrivate::sizedFree(v, allocSize);
++ }
++ }
++
++ // 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.load(std::memory_order_relaxed) && ref.loadAcquire() == 1)
++ cleanOrphanedConnectionsImpl(sender, lockPolicy);
++ }
++ void cleanOrphanedConnectionsImpl(QObject *sender, LockPolicy lockPolicy);
++
++ ConnectionList &connectionsForSignal(int signal)
++ {
++ return signalVector.loadRelaxed()->at(signal);
++ }
++
++ void resizeSignalVector(size_t size)
++ {
++ SignalVector *vector = this->signalVector.loadRelaxed();
++ if (vector && vector->allocated > size)
++ return;
++ size = (size + 7) & ~7;
++ void *ptr = QtPrivate::fittedMalloc(sizeof(SignalVector), &size, sizeof(ConnectionList), 1);
++ auto newVector = new (ptr) SignalVector;
++
++ int start = -1;
++ if (vector) {
++ for (int i = -1; i < vector->count(); ++i) {
++ new (&newVector->at(i)) ConnectionList{vector->at(i).first.loadRelaxed(),
++ vector->at(i).last.loadRelaxed()};
++ }
++ start = vector->count();
++ }
++ for (int i = start; i < int(size); ++i)
++ new (&newVector->at(i)) ConnectionList();
++ newVector->next = nullptr;
++ newVector->allocated = size;
++
++ signalVector.storeRelease(newVector);
++ if (vector) {
++ TaggedSignalVector 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.
++ */
++ o = orphaned.load(std::memory_order_acquire);
++ do {
++ vector->nextInOrphanList = o;
++ } while (!orphaned.compare_exchange_strong(o, TaggedSignalVector(vector), std::memory_order_release));
++ }
++ }
++ int signalVectorCount() const
++ {
++ SignalVector *v = signalVector.loadAcquire();
++ return v ? v->count() : -1;
++ }
++
++ static void deleteOrphaned(TaggedSignalVector o);
++ };
++
++ struct QObjectPrivate::Sender
++ {
++ Sender(QObject *receiver, QObject *sender, int signal, ConnectionData *receiverConnections)
++ : receiver(receiver), sender(sender), signal(signal)
++ {
++ if (receiverConnections) {
++ previous = receiverConnections->currentSender;
++ receiverConnections->currentSender = this;
++ }
++ }
++ ~Sender()
++ {
++ if (receiver)
++ receiver->d_func()->connections.loadAcquire()->currentSender = previous;
++ }
++ void receiverDeleted()
++ {
++ Sender *s = this;
++ while (s) {
++ s->receiver = nullptr;
++ s = s->previous;
++ }
++ }
++ Sender *previous = nullptr;
++ QObject *receiver;
++ QObject *sender;
++ int signal;
++ };
++ Q_DECLARE_TYPEINFO(QObjectPrivate::Sender, Q_RELOCATABLE_TYPE);
++
++ QT_END_NAMESPACE
++
++ #endif