1// Copyright (C) 2016 The Qt Company Ltd.
2// Copyright (C) 2016 Intel Corporation.
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 QTHREAD_P_H
6#define QTHREAD_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 purely as an
13// implementation detail. This header file may change from version to
14// version without notice, or even be removed.
15//
16// We mean it.
17//
18//
19
20#include "qplatformdefs.h"
21#include "QtCore/qthread.h"
22#include "QtCore/qmutex.h"
23#include "QtCore/qstack.h"
24#if QT_CONFIG(thread)
25#include "QtCore/qwaitcondition.h"
26#endif
27#include "QtCore/qmap.h"
28#include "QtCore/qcoreapplication.h"
29#include "private/qobject_p.h"
30
31#include <algorithm>
32#include <atomic>
33
34QT_BEGIN_NAMESPACE
35
36class QAbstractEventDispatcher;
37class QEventLoop;
38
39class QPostEvent
40{
41public:
42 QObject *receiver;
43 QEvent *event;
44 int priority;
45 inline QPostEvent()
46 : receiver(nullptr), event(nullptr), priority(0)
47 { }
48 inline QPostEvent(QObject *r, QEvent *e, int p)
49 : receiver(r), event(e), priority(p)
50 { }
51};
52Q_DECLARE_TYPEINFO(QPostEvent, Q_RELOCATABLE_TYPE);
53
54inline bool operator<(const QPostEvent &first, const QPostEvent &second)
55{
56 return first.priority > second.priority;
57}
58
59// This class holds the list of posted events.
60// The list has to be kept sorted by priority
61// It's used in a virtual in QCoreApplication, so ELFVERSION:ignore-next
62class QPostEventList : public QList<QPostEvent>
63{
64public:
65 // recursion == recursion count for sendPostedEvents()
66 int recursion;
67
68 // sendOffset == the current event to start sending
69 qsizetype startOffset;
70 // insertionOffset == set by sendPostedEvents to tell postEvent() where to start insertions
71 qsizetype insertionOffset;
72
73 QMutex mutex;
74
75 inline QPostEventList() : QList<QPostEvent>(), recursion(0), startOffset(0), insertionOffset(0) { }
76
77 void addEvent(const QPostEvent &ev);
78
79private:
80 //hides because they do not keep that list sorted. addEvent must be used
81 using QList<QPostEvent>::append;
82 using QList<QPostEvent>::insert;
83};
84
85namespace QtPrivate {
86
87/* BindingStatusOrList is basically a QBiPointer (as found in declarative)
88 with some helper methods to manipulate the list. BindingStatusOrList starts
89 its life in a null state and supports the following transitions
90
91 0 state (initial)
92 / \
93 / \
94 v v
95 pending object list----------->binding status
96 Note that binding status is the final state, and we never transition away
97 from it
98*/
99class BindingStatusOrList
100{
101 Q_DISABLE_COPY_MOVE(BindingStatusOrList)
102public:
103 using List = std::vector<QObject *>;
104
105 constexpr BindingStatusOrList() noexcept : data(0) {}
106 explicit BindingStatusOrList(QBindingStatus *status) noexcept :
107 data(encodeBindingStatus(status)) {}
108 explicit BindingStatusOrList(List *list) noexcept : data(encodeList(list)) {}
109
110 // requires external synchronization:
111 QBindingStatus *addObjectUnlessAlreadyStatus(QObject *object);
112 void removeObject(QObject *object);
113 void setStatusAndClearList(QBindingStatus *status) noexcept;
114
115
116 static bool isBindingStatus(quintptr data) noexcept
117 {
118 return !isNull(data) && !isList(data);
119 }
120 static bool isList(quintptr data) noexcept { return data & 1; }
121 static bool isNull(quintptr data) noexcept { return data == 0; }
122
123 // thread-safe:
124 QBindingStatus *bindingStatus() const noexcept
125 {
126 // synchronizes-with the store-release in setStatusAndClearList():
127 const auto d = data.load(m: std::memory_order_acquire);
128 if (isBindingStatus(data: d))
129 return reinterpret_cast<QBindingStatus *>(d);
130 else
131 return nullptr;
132 }
133
134 // requires external synchronization:
135 List *list() const noexcept
136 {
137 return decodeList(ptr: data.load(m: std::memory_order_relaxed));
138 }
139
140private:
141 static List *decodeList(quintptr ptr) noexcept
142 {
143 if (isList(data: ptr))
144 return reinterpret_cast<List *>(ptr & ~1);
145 else
146 return nullptr;
147 }
148
149 static quintptr encodeBindingStatus(QBindingStatus *status) noexcept
150 {
151 return quintptr(status);
152 }
153
154 static quintptr encodeList(List *list) noexcept
155 {
156 return quintptr(list) | 1;
157 }
158
159 std::atomic<quintptr> data;
160};
161
162} // namespace QtPrivate
163
164#if QT_CONFIG(thread)
165
166class Q_CORE_EXPORT QDaemonThread : public QThread
167{
168public:
169 QDaemonThread(QObject *parent = nullptr);
170 ~QDaemonThread();
171};
172
173class Q_AUTOTEST_EXPORT QThreadPrivate : public QObjectPrivate
174{
175 Q_DECLARE_PUBLIC(QThread)
176
177public:
178 QThreadPrivate(QThreadData *d = nullptr);
179 ~QThreadPrivate();
180
181 void setPriority(QThread::Priority prio);
182 Qt::HANDLE threadId() const noexcept;
183
184 mutable QMutex mutex;
185 QAtomicInt quitLockRef;
186
187 bool running;
188 bool finished;
189 bool isInFinish; //when in QThreadPrivate::finish
190 std::atomic<bool> interruptionRequested = false;
191#ifdef Q_OS_UNIX
192 bool terminated = false; // when (the first) terminate has been called
193#endif
194
195 bool exited;
196 int returnCode;
197
198 uint stackSize;
199 std::underlying_type_t<QThread::Priority> priority;
200
201 bool wait(QMutexLocker<QMutex> &locker, QDeadlineTimer deadline);
202
203#ifdef Q_OS_UNIX
204 QWaitCondition thread_done;
205
206 static void *start(void *arg);
207 void finish(); // happens early (before thread-local dtors)
208 void cleanup(); // happens late (as a thread-local dtor, if possible)
209#endif // Q_OS_UNIX
210
211#ifdef Q_OS_WIN
212 static unsigned int __stdcall start(void *) noexcept;
213 void finish(bool lockAnyway = true) noexcept;
214
215 Qt::HANDLE handle;
216 unsigned int id;
217 int waiters;
218 bool terminationEnabled, terminatePending;
219#endif // Q_OS_WIN
220#ifdef Q_OS_WASM
221 static int idealThreadCount;
222#endif
223 QThreadData *data;
224
225 static QAbstractEventDispatcher *createEventDispatcher(QThreadData *data);
226
227 void ref()
228 {
229 quitLockRef.ref();
230 }
231
232 void deref()
233 {
234 if (!quitLockRef.deref() && running) {
235 QCoreApplication::instance()->postEvent(receiver: q_ptr, event: new QEvent(QEvent::Quit));
236 }
237 }
238
239 QBindingStatus *bindingStatus()
240 {
241 return m_statusOrPendingObjects.bindingStatus();
242 }
243
244 /* Returns nullptr if the object has been added, or the binding status
245 if that one has been set in the meantime
246 */
247 QBindingStatus *addObjectWithPendingBindingStatusChange(QObject *obj);
248 void removeObjectWithPendingBindingStatusChange(QObject *obj);
249
250 // manipulating m_statusOrPendingObjects requires mutex to be locked
251 QtPrivate::BindingStatusOrList m_statusOrPendingObjects = {};
252#ifndef Q_OS_INTEGRITY
253private:
254 // Used in QThread(Private)::start to avoid racy access to QObject::objectName,
255 // unset afterwards. On INTEGRITY we set the thread name before starting it.
256 QString objectName;
257#endif
258};
259
260#else // QT_CONFIG(thread)
261
262class QThreadPrivate : public QObjectPrivate
263{
264public:
265 QThreadPrivate(QThreadData *d = nullptr);
266 ~QThreadPrivate();
267
268 mutable QMutex mutex;
269 QThreadData *data;
270 QBindingStatus* m_bindingStatus;
271 bool running = false;
272
273 QBindingStatus* bindingStatus() { return m_bindingStatus; }
274 QBindingStatus *addObjectWithPendingBindingStatusChange(QObject *) { return nullptr; }
275 void removeObjectWithPendingBindingStatusChange(QObject *) {}
276
277 static void setCurrentThread(QThread *) { }
278 static QAbstractEventDispatcher *createEventDispatcher(QThreadData *data);
279
280 void ref() {}
281 void deref() {}
282
283 Q_DECLARE_PUBLIC(QThread)
284};
285
286#endif // QT_CONFIG(thread)
287
288class QThreadData
289{
290public:
291 QThreadData(int initialRefCount = 1);
292 ~QThreadData();
293
294 static Q_AUTOTEST_EXPORT QThreadData *current(bool createIfNecessary = true);
295 static void clearCurrentThreadData();
296 static QThreadData *get2(QThread *thread)
297 { Q_ASSERT_X(thread != nullptr, "QThread", "internal error"); return thread->d_func()->data; }
298
299
300 void ref();
301 void deref();
302 inline bool hasEventDispatcher() const
303 { return eventDispatcher.loadRelaxed() != nullptr; }
304 QAbstractEventDispatcher *createEventDispatcher();
305 QAbstractEventDispatcher *ensureEventDispatcher()
306 {
307 QAbstractEventDispatcher *ed = eventDispatcher.loadRelaxed();
308 if (Q_LIKELY(ed))
309 return ed;
310 return createEventDispatcher();
311 }
312
313 bool canWaitLocked()
314 {
315 QMutexLocker locker(&postEventList.mutex);
316 return canWait;
317 }
318
319private:
320 QAtomicInt _ref;
321
322public:
323 int loopLevel;
324 int scopeLevel;
325
326 QStack<QEventLoop *> eventLoops;
327 QPostEventList postEventList;
328 QAtomicPointer<QThread> thread;
329 QAtomicPointer<void> threadId;
330 QAtomicPointer<QAbstractEventDispatcher> eventDispatcher;
331 QList<void *> tls;
332
333 bool quitNow;
334 bool canWait;
335 bool isAdopted;
336 bool requiresCoreApplication;
337};
338
339class QScopedScopeLevelCounter
340{
341 QThreadData *threadData;
342public:
343 QScopedScopeLevelCounter(QThreadData *threadData);
344 ~QScopedScopeLevelCounter();
345};
346
347// thread wrapper for the main() thread
348class QAdoptedThread : public QThread
349{
350 Q_DECLARE_PRIVATE(QThread)
351
352public:
353 QAdoptedThread(QThreadData *data = nullptr);
354 ~QAdoptedThread();
355 void init();
356
357private:
358#if QT_CONFIG(thread)
359 void run() override;
360#endif
361};
362
363QT_END_NAMESPACE
364
365#endif // QTHREAD_P_H
366

Provided by KDAB

Privacy Policy
Learn to use CMake with our Intro Training
Find out more

source code of qtbase/src/corelib/thread/qthread_p.h