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