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
183 mutable QMutex mutex;
184 QAtomicInt quitLockRef;
185
186 bool running;
187 bool finished;
188 bool isInFinish; //when in QThreadPrivate::finish
189 std::atomic<bool> interruptionRequested;
190
191 bool exited;
192 int returnCode;
193
194 uint stackSize;
195 std::underlying_type_t<QThread::Priority> priority;
196
197#ifdef Q_OS_UNIX
198 QWaitCondition thread_done;
199
200 static void *start(void *arg);
201 static void finish(void *);
202
203#endif // Q_OS_UNIX
204
205#ifdef Q_OS_WIN
206 static unsigned int __stdcall start(void *) noexcept;
207 static void finish(void *, bool lockAnyway = true) noexcept;
208
209 Qt::HANDLE handle;
210 unsigned int id;
211 int waiters;
212 bool terminationEnabled, terminatePending;
213#endif // Q_OS_WIN
214#ifdef Q_OS_WASM
215 static int idealThreadCount;
216#endif
217 QThreadData *data;
218
219 static QAbstractEventDispatcher *createEventDispatcher(QThreadData *data);
220
221 void ref()
222 {
223 quitLockRef.ref();
224 }
225
226 void deref()
227 {
228 if (!quitLockRef.deref() && running) {
229 QCoreApplication::instance()->postEvent(receiver: q_ptr, event: new QEvent(QEvent::Quit));
230 }
231 }
232
233 QBindingStatus *bindingStatus()
234 {
235 return m_statusOrPendingObjects.bindingStatus();
236 }
237
238 /* Returns nullptr if the object has been added, or the binding status
239 if that one has been set in the meantime
240 */
241 QBindingStatus *addObjectWithPendingBindingStatusChange(QObject *obj);
242 void removeObjectWithPendingBindingStatusChange(QObject *obj);
243
244 // manipulating m_statusOrPendingObjects requires mutex to be locked
245 QtPrivate::BindingStatusOrList m_statusOrPendingObjects = {};
246#ifndef Q_OS_INTEGRITY
247private:
248 // Used in QThread(Private)::start to avoid racy access to QObject::objectName,
249 // unset afterwards. On INTEGRITY we set the thread name before starting it.
250 QString objectName;
251#endif
252};
253
254#else // QT_CONFIG(thread)
255
256class QThreadPrivate : public QObjectPrivate
257{
258public:
259 QThreadPrivate(QThreadData *d = nullptr);
260 ~QThreadPrivate();
261
262 mutable QMutex mutex;
263 QThreadData *data;
264 QBindingStatus* m_bindingStatus;
265 bool running = false;
266
267 QBindingStatus* bindingStatus() { return m_bindingStatus; }
268 QBindingStatus *addObjectWithPendingBindingStatusChange(QObject *) { return nullptr; }
269 void removeObjectWithPendingBindingStatusChange(QObject *) {}
270
271 static void setCurrentThread(QThread *) { }
272 static QAbstractEventDispatcher *createEventDispatcher(QThreadData *data);
273
274 void ref() {}
275 void deref() {}
276
277 Q_DECLARE_PUBLIC(QThread)
278};
279
280#endif // QT_CONFIG(thread)
281
282class QThreadData
283{
284public:
285 QThreadData(int initialRefCount = 1);
286 ~QThreadData();
287
288 static Q_AUTOTEST_EXPORT QThreadData *current(bool createIfNecessary = true);
289 static void clearCurrentThreadData();
290 static QThreadData *get2(QThread *thread)
291 { Q_ASSERT_X(thread != nullptr, "QThread", "internal error"); return thread->d_func()->data; }
292
293
294 void ref();
295 void deref();
296 inline bool hasEventDispatcher() const
297 { return eventDispatcher.loadRelaxed() != nullptr; }
298 QAbstractEventDispatcher *createEventDispatcher();
299 QAbstractEventDispatcher *ensureEventDispatcher()
300 {
301 QAbstractEventDispatcher *ed = eventDispatcher.loadRelaxed();
302 if (Q_LIKELY(ed))
303 return ed;
304 return createEventDispatcher();
305 }
306
307 bool canWaitLocked()
308 {
309 QMutexLocker locker(&postEventList.mutex);
310 return canWait;
311 }
312
313private:
314 QAtomicInt _ref;
315
316public:
317 int loopLevel;
318 int scopeLevel;
319
320 QStack<QEventLoop *> eventLoops;
321 QPostEventList postEventList;
322 QAtomicPointer<QThread> thread;
323 QAtomicPointer<void> threadId;
324 QAtomicPointer<QAbstractEventDispatcher> eventDispatcher;
325 QList<void *> tls;
326
327 bool quitNow;
328 bool canWait;
329 bool isAdopted;
330 bool requiresCoreApplication;
331};
332
333class QScopedScopeLevelCounter
334{
335 QThreadData *threadData;
336public:
337 inline QScopedScopeLevelCounter(QThreadData *threadData)
338 : threadData(threadData)
339 { ++threadData->scopeLevel; }
340 inline ~QScopedScopeLevelCounter()
341 { --threadData->scopeLevel; }
342};
343
344// thread wrapper for the main() thread
345class QAdoptedThread : public QThread
346{
347 Q_DECLARE_PRIVATE(QThread)
348
349public:
350 QAdoptedThread(QThreadData *data = nullptr);
351 ~QAdoptedThread();
352 void init();
353
354private:
355#if QT_CONFIG(thread)
356 void run() override;
357#endif
358};
359
360QT_END_NAMESPACE
361
362#endif // QTHREAD_P_H
363

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