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

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