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 | |
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 |
247 | private: |
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 | |
256 | class QThreadPrivate : public QObjectPrivate |
257 | { |
258 | public: |
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 | |
282 | class QThreadData |
283 | { |
284 | public: |
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 | |
313 | private: |
314 | QAtomicInt _ref; |
315 | |
316 | public: |
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 | |
333 | class QScopedScopeLevelCounter |
334 | { |
335 | QThreadData *threadData; |
336 | public: |
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 |
345 | class QAdoptedThread : public QThread |
346 | { |
347 | Q_DECLARE_PRIVATE(QThread) |
348 | |
349 | public: |
350 | QAdoptedThread(QThreadData *data = nullptr); |
351 | ~QAdoptedThread(); |
352 | void init(); |
353 | |
354 | private: |
355 | #if QT_CONFIG(thread) |
356 | void run() override; |
357 | #endif |
358 | }; |
359 | |
360 | QT_END_NAMESPACE |
361 | |
362 | #endif // QTHREAD_P_H |
363 | |