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 QAtomicInteger<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 receiver.storeRelaxed(nullptr);
100 receiverThreadData.storeRelaxed(nullptr);
101 }
102 ~Connection();
103 int method() const
104 {
105 Q_ASSERT(!isSlotObject);
106 return method_offset + method_relative;
107 }
108 void ref() { ref_.ref(); }
109 void freeSlotObject()
110 {
111 if (isSlotObject) {
112 slotObj->destroyIfLastRef();
113 isSlotObject = false;
114 }
115 }
116 void deref()
117 {
118 if (!ref_.deref()) {
119 Q_ASSERT(!receiver.loadRelaxed());
120 Q_ASSERT(!isSlotObject);
121 delete this;
122 }
123 }
124};
126
127struct QObjectPrivate::SignalVector : public ConnectionOrSignalVector
128{
129 quintptr allocated;
130 // ConnectionList signals[]
131 ConnectionList &at(int i) { return reinterpret_cast<ConnectionList *>(this + 1)[i + 1]; }
132 const ConnectionList &at(int i) const
133 {
134 return reinterpret_cast<const ConnectionList *>(this + 1)[i + 1];
135 }
136 int count() const { return static_cast<int>(allocated); }
137};
138// it doesn't need to be, but it helps
139static_assert(std::is_trivially_copyable_v<QObjectPrivate::SignalVector>);
140
141struct QObjectPrivate::ConnectionData
142{
143 // the id below is used to avoid activating new connections. When the object gets
144 // deleted it's set to 0, so that signal emission stops
145 QAtomicInteger<uint> currentConnectionId;
146 QAtomicInt ref;
147 QAtomicPointer<SignalVector> signalVector;
148 Connection *senders = nullptr;
149 Sender *currentSender = nullptr; // object currently activating the object
150 std::atomic<TaggedSignalVector> orphaned = {nullptr};
151
152 ~ConnectionData()
153 {
154 Q_ASSERT(ref.loadRelaxed() == 0);
155 TaggedSignalVector c = orphaned.exchange(nullptr, std::memory_order_relaxed);
156 if (c)
157 deleteOrphaned(c);
158 SignalVector *v = signalVector.loadRelaxed();
159 if (v) {
160 const size_t allocSize = sizeof(SignalVector) + (v->allocated + 1) * sizeof(ConnectionList);
161 v->~SignalVector();
162 QtPrivate::sizedFree(v, allocSize);
163 }
164 }
165
166 // must be called on the senders connection data
167 // assumes the senders and receivers lock are held
168 void removeConnection(Connection *c);
169 enum LockPolicy {
170 NeedToLock,
171 // Beware that we need to temporarily release the lock
172 // and thus calling code must carefully consider whether
173 // invariants still hold.
174 AlreadyLockedAndTemporarilyReleasingLock
175 };
176 void cleanOrphanedConnections(QObject *sender, LockPolicy lockPolicy = NeedToLock)
177 {
178 if (orphaned.load(std::memory_order_relaxed) && ref.loadAcquire() == 1)
179 cleanOrphanedConnectionsImpl(sender, lockPolicy);
180 }
181 void cleanOrphanedConnectionsImpl(QObject *sender, LockPolicy lockPolicy);
182
183 ConnectionList &connectionsForSignal(int signal)
184 {
185 return signalVector.loadRelaxed()->at(signal);
186 }
187
188 void resizeSignalVector(size_t size)
189 {
190 SignalVector *vector = this->signalVector.loadRelaxed();
191 if (vector && vector->allocated > size)
192 return;
193 size = (size + 7) & ~7;
194 void *ptr = QtPrivate::fittedMalloc(sizeof(SignalVector), &size, sizeof(ConnectionList), 1);
195 auto newVector = new (ptr) SignalVector;
196
197 int start = -1;
198 if (vector) {
199 for (int i = -1; i < vector->count(); ++i) {
200 new (&newVector->at(i)) ConnectionList{vector->at(i).first.loadRelaxed(),
201 vector->at(i).last.loadRelaxed()};
202 }
203 start = vector->count();
204 }
205 for (int i = start; i < int(size); ++i)
206 new (&newVector->at(i)) ConnectionList();
207 newVector->next = nullptr;
208 newVector->allocated = size;
209
210 signalVector.storeRelease(newVector);
211 if (vector) {
212 TaggedSignalVector o = nullptr;
213 /* No ABA issue here: When adding a node, we only care about the list head, it doesn't
214 * matter if the tail changes.
215 */
216 o = orphaned.load(std::memory_order_acquire);
217 do {
218 vector->nextInOrphanList = o;
219 } while (!orphaned.compare_exchange_strong(o, TaggedSignalVector(vector), std::memory_order_release));
220 }
221 }
222 int signalVectorCount() const
223 {
224 SignalVector *v = signalVector.loadAcquire();
225 return v ? v->count() : -1;
226 }
227
228 static void deleteOrphaned(TaggedSignalVector o);
229};
230
231struct QObjectPrivate::Sender
232{
233 Sender(QObject *receiver, QObject *sender, int signal, ConnectionData *receiverConnections)
234 : receiver(receiver), sender(sender), signal(signal)
235 {
236 if (receiverConnections) {
237 previous = receiverConnections->currentSender;
238 receiverConnections->currentSender = this;
239 }
240 }
241 ~Sender()
242 {
243 if (receiver)
244 receiver->d_func()->connections.loadAcquire()->currentSender = previous;
245 }
246 void receiverDeleted()
247 {
248 Sender *s = this;
249 while (s) {
250 s->receiver = nullptr;
251 s = s->previous;
252 }
253 }
254 Sender *previous = nullptr;
255 QObject *receiver;
256 QObject *sender;
257 int signal;
258};
260
261QT_END_NAMESPACE
262
263#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:3007
static int * queuedConnectionTypes(QSpan< const QArgumentType > argumentTypes)
Definition qobject.cpp:115
static int DIRECT_CONNECTION_ONLY
Definition qobject.cpp:66
static int methodIndexToSignalIndex(const QMetaObject **base, int signal_index)
Definition qobject.cpp:3703
QObject * qt_qFindChild_helper(const QObject *parent, QAnyStringView name, const QMetaObject &mo, Qt::FindChildOptions options)
Definition qobject.cpp:2254
static const char * extract_location(const char *member)
Definition qobject.cpp:2648
ConnectionEnd
Definition qobject.cpp:2722
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:947
static int * queuedConnectionTypes(const QMetaMethod &method)
Definition qobject.cpp:86
static void computeOffsets(const QMetaObject *metaobject, int *signalOffset, int *methodOffset)
Definition qobject.cpp:227
static QBasicMutex * signalSlotLock(const QObject *o)
Definition qobject.cpp:147
static Q_DECL_COLD_FUNCTION void err_method_notfound(const QObject *object, const char *method, const char *func)
Definition qobject.cpp:2704
static Q_DECL_COLD_FUNCTION void connectWarning(const QObject *sender, const QMetaObject *senderMetaObject, const QObject *receiver, const char *message)
Definition qobject.cpp:2748
static bool check_method_code(int code, const QObject *object, const char *method, const char *func)
Definition qobject.cpp:2675
static Q_DECL_COLD_FUNCTION void err_info_about_object(const char *func, const QObject *o, ConnectionEnd end)
Definition qobject.cpp:2724
void qt_register_signal_spy_callbacks(QSignalSpyCallbackSet *callback_set)
Definition qobject.cpp:73
static bool matches_objectName_non_null(QObject *obj, QAnyStringView name)
Definition qobject.cpp:2207
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:2642
static bool check_signal_macro(const QObject *sender, const char *signal, const char *func, const char *op)
Definition qobject.cpp:2659
static Q_DECL_COLD_FUNCTION void err_info_about_objects(const char *func, const QObject *sender, const QObject *receiver)
Definition qobject.cpp:2741
Q_CORE_EXPORT void qt_qFindChildren_helper(const QObject *parent, QAnyStringView name, const QMetaObject &mo, QList< void * > *list, Qt::FindChildOptions options)
Definition qobject.cpp:2217
Q_CORE_EXPORT const char * qFlagLocation(const char *method)
Definition qobject.cpp:2636
SlotObjectGuard()=default