Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Loading...
Searching...
No Matches
qobject_p_p.h
Go to the documentation of this file.
1// Copyright (C) 2019 The Qt Company Ltd.
2// Copyright (C) 2013 Olivier Goffart <ogoffart@woboq.com>
3// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
4
5#ifndef QOBJECT_P_P_H
6#define QOBJECT_P_P_H
7
8//
9// W A R N I N G
10// -------------
11//
12// This file is not part of the Qt API. It exists for the convenience
13// of qobject.cpp. This header file may change from version to version
14// without notice, or even be removed.
15//
16// We mean it.
17//
18
19// Even though this file is only used by qobject.cpp, the only reason this
20// code lives here is that some special apps/libraries for e.g., QtJambi,
21// Gammaray need access to the structs in this file.
22
23#include <QtCore/qalloc.h>
24#include <QtCore/qobject.h>
25#include <QtCore/private/qobject_p.h>
26
28
29// ConnectionList is a singly-linked list
30struct QObjectPrivate::ConnectionList
31{
32 QAtomicPointer<Connection> first;
33 QAtomicPointer<Connection> last;
34};
35static_assert(std::is_trivially_destructible_v<QObjectPrivate::ConnectionList>);
37
38struct QObjectPrivate::TaggedSignalVector
39{
40 quintptr c;
41
42 TaggedSignalVector() = default;
43 TaggedSignalVector(std::nullptr_t) noexcept : c(0) {}
44 TaggedSignalVector(Connection *v) noexcept : c(reinterpret_cast<quintptr>(v)) { Q_ASSERT(v && (reinterpret_cast<quintptr>(v) & 0x1) == 0); }
45 TaggedSignalVector(SignalVector *v) noexcept : c(reinterpret_cast<quintptr>(v) | quintptr(1u)) { Q_ASSERT(v); }
46 explicit operator SignalVector *() const noexcept
47 {
48 if (c & 0x1)
49 return reinterpret_cast<SignalVector *>(c & ~quintptr(1u));
50 return nullptr;
51 }
52 explicit operator Connection *() const noexcept
53 {
54 return reinterpret_cast<Connection *>(c);
55 }
56 operator uintptr_t() const noexcept { return c; }
57};
58
59struct QObjectPrivate::ConnectionOrSignalVector
60{
61 union {
62 // linked list of orphaned connections that need cleaning up
63 TaggedSignalVector nextInOrphanList;
64 // linked list of connections connected to slots in this object
65 Connection *next;
66 };
67};
68static_assert(std::is_trivially_copyable_v<QObjectPrivate::ConnectionOrSignalVector>);
69
70struct QObjectPrivate::Connection : public ConnectionOrSignalVector
71{
72 // linked list of connections connected to slots in this object, next is in base class
73 Connection **prev;
74 // linked list of connections connected to signals in this object
75 QAtomicPointer<Connection> nextConnectionList;
76 Connection *prevConnectionList;
77
78 QObject *sender;
79 QAtomicPointer<QObject> receiver;
80 QAtomicPointer<QThreadData> receiverThreadData;
81 union {
82 StaticMetaCallFunction callFunction;
83 QtPrivate::QSlotObjectBase *slotObj;
84 };
85 QAtomicPointer<const int> argumentTypes;
86 QAtomicInt ref_{
87 2
88 }; // ref_ is 2 for the use in the internal lists, and for the use in QMetaObject::Connection
89 uint id = 0;
90 ushort method_offset;
91 ushort method_relative;
92 signed int signal_index : 27; // In signal range (see QObjectPrivate::signalIndex())
93 ushort connectionType : 2; // 0 == auto, 1 == direct, 2 == queued, 3 == blocking
94 ushort isSlotObject : 1;
95 ushort ownArgumentTypes : 1;
96 ushort isSingleShot : 1;
97 Connection() : ownArgumentTypes(true) { }
98 ~Connection();
99 int method() const
100 {
101 Q_ASSERT(!isSlotObject);
102 return method_offset + method_relative;
103 }
104 void ref() { ref_.ref(); }
105 void freeSlotObject()
106 {
107 if (isSlotObject) {
108 slotObj->destroyIfLastRef();
109 isSlotObject = false;
110 }
111 }
112 void deref()
113 {
114 if (!ref_.deref()) {
115 Q_ASSERT(!receiver.loadRelaxed());
116 Q_ASSERT(!isSlotObject);
117 delete this;
118 }
119 }
120};
122
123struct QObjectPrivate::SignalVector : public ConnectionOrSignalVector
124{
125 quintptr allocated;
126 // ConnectionList signals[]
127 ConnectionList &at(int i) { return reinterpret_cast<ConnectionList *>(this + 1)[i + 1]; }
128 const ConnectionList &at(int i) const
129 {
130 return reinterpret_cast<const ConnectionList *>(this + 1)[i + 1];
131 }
132 int count() const { return static_cast<int>(allocated); }
133};
134// it doesn't need to be, but it helps
135static_assert(std::is_trivially_copyable_v<QObjectPrivate::SignalVector>);
136
137struct QObjectPrivate::ConnectionData
138{
139 // the id below is used to avoid activating new connections. When the object gets
140 // deleted it's set to 0, so that signal emission stops
141 QAtomicInteger<uint> currentConnectionId;
142 QAtomicInt ref;
143 QAtomicPointer<SignalVector> signalVector;
144 Connection *senders = nullptr;
145 Sender *currentSender = nullptr; // object currently activating the object
146 std::atomic<TaggedSignalVector> orphaned = {nullptr};
147
148 ~ConnectionData()
149 {
150 Q_ASSERT(ref.loadRelaxed() == 0);
151 TaggedSignalVector c = orphaned.exchange(nullptr, std::memory_order_relaxed);
152 if (c)
153 deleteOrphaned(c);
154 SignalVector *v = signalVector.loadRelaxed();
155 if (v) {
156 const size_t allocSize = sizeof(SignalVector) + (v->allocated + 1) * sizeof(ConnectionList);
157 v->~SignalVector();
158 QtPrivate::sizedFree(v, allocSize);
159 }
160 }
161
162 // must be called on the senders connection data
163 // assumes the senders and receivers lock are held
164 void removeConnection(Connection *c);
165 enum LockPolicy {
166 NeedToLock,
167 // Beware that we need to temporarily release the lock
168 // and thus calling code must carefully consider whether
169 // invariants still hold.
170 AlreadyLockedAndTemporarilyReleasingLock
171 };
172 void cleanOrphanedConnections(QObject *sender, LockPolicy lockPolicy = NeedToLock)
173 {
174 if (orphaned.load(std::memory_order_relaxed) && ref.loadAcquire() == 1)
175 cleanOrphanedConnectionsImpl(sender, lockPolicy);
176 }
177 void cleanOrphanedConnectionsImpl(QObject *sender, LockPolicy lockPolicy);
178
179 ConnectionList &connectionsForSignal(int signal)
180 {
181 return signalVector.loadRelaxed()->at(signal);
182 }
183
184 void resizeSignalVector(size_t size)
185 {
186 SignalVector *vector = this->signalVector.loadRelaxed();
187 if (vector && vector->allocated > size)
188 return;
189 size = (size + 7) & ~7;
190 void *ptr = QtPrivate::fittedMalloc(sizeof(SignalVector), &size, sizeof(ConnectionList), 1);
191 auto newVector = new (ptr) SignalVector;
192
193 int start = -1;
194 if (vector) {
195 // not (yet) existing trait:
196 // static_assert(std::is_relocatable_v<SignalVector>);
197 // static_assert(std::is_relocatable_v<ConnectionList>);
198 memcpy(newVector, vector,
199 sizeof(SignalVector) + (vector->allocated + 1) * sizeof(ConnectionList));
200 start = vector->count();
201 }
202 for (int i = start; i < int(size); ++i)
203 new (&newVector->at(i)) ConnectionList();
204 newVector->next = nullptr;
205 newVector->allocated = size;
206
207 signalVector.storeRelaxed(newVector);
208 if (vector) {
209 TaggedSignalVector o = nullptr;
210 /* No ABA issue here: When adding a node, we only care about the list head, it doesn't
211 * matter if the tail changes.
212 */
213 o = orphaned.load(std::memory_order_acquire);
214 do {
215 vector->nextInOrphanList = o;
216 } while (!orphaned.compare_exchange_strong(o, TaggedSignalVector(vector), std::memory_order_release));
217 }
218 }
219 int signalVectorCount() const
220 {
221 return signalVector.loadAcquire() ? signalVector.loadRelaxed()->count() : -1;
222 }
223
224 static void deleteOrphaned(TaggedSignalVector o);
225};
226
227struct QObjectPrivate::Sender
228{
229 Sender(QObject *receiver, QObject *sender, int signal, ConnectionData *receiverConnections)
230 : receiver(receiver), sender(sender), signal(signal)
231 {
232 if (receiverConnections) {
233 previous = receiverConnections->currentSender;
234 receiverConnections->currentSender = this;
235 }
236 }
237 ~Sender()
238 {
239 if (receiver)
240 receiver->d_func()->connections.loadAcquire()->currentSender = previous;
241 }
242 void receiverDeleted()
243 {
244 Sender *s = this;
245 while (s) {
246 s->receiver = nullptr;
247 s = s->previous;
248 }
249 }
250 Sender *previous = nullptr;
251 QObject *receiver;
252 QObject *sender;
253 int signal;
254};
256
257QT_END_NAMESPACE
258
259#endif
Q_TRACE_POINT(qtcore, QCoreApplication_postEvent_exit)
#define qCWarning(category,...)
#define qCDebug(category,...)
#define Q_STATIC_LOGGING_CATEGORY(name,...)
static void check_and_warn_compat(const QMetaObject *sender, const QMetaMethod &signal, const QMetaObject *receiver, const QMetaMethod &method)
Definition qobject.cpp:2964
static int * queuedConnectionTypes(QSpan< const QArgumentType > argumentTypes)
Definition qobject.cpp:107
static int DIRECT_CONNECTION_ONLY
Definition qobject.cpp:58
static int methodIndexToSignalIndex(const QMetaObject **base, int signal_index)
Definition qobject.cpp:3660
QObject * qt_qFindChild_helper(const QObject *parent, QAnyStringView name, const QMetaObject &mo, Qt::FindChildOptions options)
Definition qobject.cpp:2240
static const char * extract_location(const char *member)
Definition qobject.cpp:2634
Q_TRACE_POINT(qtcore, QMetaObject_activate_slot_functor_entry, void *slotObject)
static bool check_parent_thread(QObject *parent, QThreadData *parentThreadData, QThreadData *currentThreadData)
Definition qobject.cpp:939
static int * queuedConnectionTypes(const QMetaMethod &method)
Definition qobject.cpp:78
static void computeOffsets(const QMetaObject *metaobject, int *signalOffset, int *methodOffset)
Definition qobject.cpp:219
static QBasicMutex * signalSlotLock(const QObject *o)
Definition qobject.cpp:139
static Q_DECL_COLD_FUNCTION void err_method_notfound(const QObject *object, const char *method, const char *func)
Definition qobject.cpp:2690
static bool check_method_code(int code, const QObject *object, const char *method, const char *func)
Definition qobject.cpp:2661
void qt_register_signal_spy_callbacks(QSignalSpyCallbackSet *callback_set)
Definition qobject.cpp:65
static bool matches_objectName_non_null(QObject *obj, QAnyStringView name)
Definition qobject.cpp:2193
Q_TRACE_POINT(qtcore, QObject_dtor, QObject *object)
Q_TRACE_POINT(qtcore, QMetaObject_activate_entry, QObject *sender, int signalIndex)
static int extract_code(const char *member)
Definition qobject.cpp:2628
static bool check_signal_macro(const QObject *sender, const char *signal, const char *func, const char *op)
Definition qobject.cpp:2645
static Q_DECL_COLD_FUNCTION void err_info_about_objects(const char *func, const QObject *sender, const QObject *receiver)
Definition qobject.cpp:2708
Q_CORE_EXPORT void qt_qFindChildren_helper(const QObject *parent, QAnyStringView name, const QMetaObject &mo, QList< void * > *list, Qt::FindChildOptions options)
Definition qobject.cpp:2203
Q_DECLARE_TYPEINFO(QObjectPrivate::ConnectionList, Q_RELOCATABLE_TYPE)
Q_CORE_EXPORT const char * qFlagLocation(const char *method)
Definition qobject.cpp:2622
SlotObjectGuard()=default