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 | |
34 | QT_BEGIN_NAMESPACE |
35 | |
36 | class QAbstractEventDispatcher; |
37 | class QEventLoop; |
38 | |
39 | class QPostEvent |
40 | { |
41 | public: |
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 | }; |
52 | Q_DECLARE_TYPEINFO(QPostEvent, Q_RELOCATABLE_TYPE); |
53 | |
54 | inline 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 |
62 | class QPostEventList : public QList<QPostEvent> |
63 | { |
64 | public: |
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 | |
79 | private: |
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 | |
85 | namespace 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 | */ |
99 | class BindingStatusOrList |
100 | { |
101 | Q_DISABLE_COPY_MOVE(BindingStatusOrList) |
102 | public: |
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 | |
140 | private: |
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 | |
166 | class Q_CORE_EXPORT QDaemonThread : public QThread |
167 | { |
168 | public: |
169 | QDaemonThread(QObject *parent = nullptr); |
170 | ~QDaemonThread(); |
171 | }; |
172 | |
173 | class Q_AUTOTEST_EXPORT QThreadPrivate : public QObjectPrivate |
174 | { |
175 | Q_DECLARE_PUBLIC(QThread) |
176 | |
177 | public: |
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 |
253 | private: |
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 | |
262 | class QThreadPrivate : public QObjectPrivate |
263 | { |
264 | public: |
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 | |
288 | class QThreadData |
289 | { |
290 | public: |
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 | |
319 | private: |
320 | QAtomicInt _ref; |
321 | |
322 | public: |
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 | |
339 | class QScopedScopeLevelCounter |
340 | { |
341 | QThreadData *threadData; |
342 | public: |
343 | QScopedScopeLevelCounter(QThreadData *threadData); |
344 | ~QScopedScopeLevelCounter(); |
345 | }; |
346 | |
347 | // thread wrapper for the main() thread |
348 | class QAdoptedThread : public QThread |
349 | { |
350 | Q_DECLARE_PRIVATE(QThread) |
351 | |
352 | public: |
353 | QAdoptedThread(QThreadData *data = nullptr); |
354 | ~QAdoptedThread(); |
355 | void init(); |
356 | |
357 | private: |
358 | #if QT_CONFIG(thread) |
359 | void run() override; |
360 | #endif |
361 | }; |
362 | |
363 | QT_END_NAMESPACE |
364 | |
365 | #endif // QTHREAD_P_H |
366 |
Definitions
- QPostEvent
- QPostEvent
- QPostEvent
- operator<
- QPostEventList
- QPostEventList
- BindingStatusOrList
- BindingStatusOrList
- BindingStatusOrList
- BindingStatusOrList
- BindingStatusOrList
- isBindingStatus
- isList
- isNull
- bindingStatus
- list
- decodeList
- encodeBindingStatus
- encodeList
- QDaemonThread
- QThreadPrivate
- ref
- deref
- bindingStatus
- QThreadData
- get2
- hasEventDispatcher
- ensureEventDispatcher
- canWaitLocked
- QScopedScopeLevelCounter
Learn to use CMake with our Intro Training
Find out more