summaryrefslogtreecommitdiffstats
path: root/src/corelib/kernel/qobject_p_p.h
blob: 3569e9071552b9de4d597503ec9b9b4e240b5053 (plain)
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259
// 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#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 liststructQObjectPrivate::ConnectionList { QAtomicPointer<Connection> first; QAtomicPointer<Connection> last;};static_assert(std::is_trivially_destructible_v<QObjectPrivate::ConnectionList>);Q_DECLARE_TYPEINFO(QObjectPrivate::ConnectionList, Q_RELOCATABLE_TYPE);structQObjectPrivate::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));returnnullptr;}explicit operator Connection *()const noexcept {return reinterpret_cast<Connection *>(c);}operatoruintptr_t()const noexcept {return c; }};structQObjectPrivate::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_trivial_v<QObjectPrivate::ConnectionOrSignalVector>);structQObjectPrivate::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 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();intmethod()const{Q_ASSERT(!isSlotObject);return method_offset + method_relative;}voidref() { ref_.ref(); }voidfreeSlotObject(){if(isSlotObject) { slotObj->destroyIfLastRef(); isSlotObject =false;}}voidderef(){if(!ref_.deref()) {Q_ASSERT(!receiver.loadRelaxed());Q_ASSERT(!isSlotObject);delete this;}}};Q_DECLARE_TYPEINFO(QObjectPrivate::Connection, Q_RELOCATABLE_TYPE);structQObjectPrivate::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];}intcount()const{return static_cast<int>(allocated); }};static_assert(std::is_trivial_v<QObjectPrivate::SignalVector>);// it doesn't need to be, but it helpsstructQObjectPrivate::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 objectstd::atomic<TaggedSignalVector> orphaned = {};~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 heldvoidremoveConnection(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 };voidcleanOrphanedConnections(QObject *sender, LockPolicy lockPolicy = NeedToLock){if(orphaned.load(std::memory_order_relaxed) && ref.loadAcquire() ==1)cleanOrphanedConnectionsImpl(sender, lockPolicy);}voidcleanOrphanedConnectionsImpl(QObject *sender, LockPolicy lockPolicy); ConnectionList &connectionsForSignal(int signal){return signalVector.loadRelaxed()->at(signal);}voidresizeSignalVector(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) {// not (yet) existing trait:// static_assert(std::is_relocatable_v<SignalVector>);// static_assert(std::is_relocatable_v<ConnectionList>);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) { 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));}}intsignalVectorCount()const{return signalVector.loadAcquire() ? signalVector.loadRelaxed()->count() : -1;}static voiddeleteOrphaned(TaggedSignalVector o);};structQObjectPrivate::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;}voidreceiverDeleted(){ 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
close