Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Loading...
Searching...
No Matches
qqmlthread_impl.cpp
Go to the documentation of this file.
1// Copyright (C) 2016 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3// Qt-Security score:significant
4
5#include "qqmlthread_p.h"
6
7#include <private/qfieldlist_p.h>
8#include <private/qtqmlglobal_p.h>
9
10#include <QtCore/qmutex.h>
11#include <QtCore/qthread.h>
12#include <QtCore/qcoreevent.h>
13#include <QtCore/qwaitcondition.h>
14#include <QtCore/qcoreapplication.h>
15
17
18QT_BEGIN_NAMESPACE
19
20/*!
21 \class QQmlThread
22 \inmodule QtQml
23 \internal
24*/
25
26class QQmlThreadPrivate : public QThread
27{
28public:
29 struct ThreadObject : public QObject
30 {
31 ThreadObject(QQmlThreadPrivate *p);
32 bool event(QEvent *e) override;
33 QQmlThreadPrivate *p;
34 };
35
36 QQmlThreadPrivate(QQmlThread *);
37 QQmlThread *q;
38
39 inline void lock() { _mutex.lock(); }
40 inline void unlock() { _mutex.unlock(); }
41 inline void wait() { _wait.wait(&_mutex); }
42 inline void wakeOne() { _wait.wakeOne(); }
43
44 bool m_threadProcessing = false; // Set when the thread is processing messages
45 bool m_mainProcessing = false; // Set when the main thread is processing messages
46 bool m_mainThreadWaiting = false; // Set by main thread if it is waiting for the message queue to empty
47 bool m_shutdown = false; // Set by main thread to announce shutdown in progress
48
49 typedef QFieldList<QQmlThread::Message, &QQmlThread::Message::next> MessageList;
50 MessageList threadList;
51 MessageList mainList;
52
53 QQmlThread::Message *mainSync = nullptr;
54 ThreadObject m_threadObject;
55
56 void triggerMainEvent();
57 void triggerThreadEvent();
58
59 void mainEvent();
60 void threadEvent();
61
62protected:
63 bool event(QEvent *) override;
64
65private:
66 QMutex _mutex;
67 QWaitCondition _wait;
68};
69
70QQmlThreadPrivate::ThreadObject::ThreadObject(QQmlThreadPrivate *p)
71: p(p)
72{
73}
74
75// Trigger mainEvent in main thread. Must be called from thread.
76void QQmlThreadPrivate::triggerMainEvent()
77{
78 Q_ASSERT(q->isThisThread());
79 QCoreApplication::postEvent(this, new QEvent(QEvent::User));
80}
81
82// Trigger even in thread. Must be called from main thread.
83void QQmlThreadPrivate::triggerThreadEvent()
84{
85 Q_ASSERT(!q->isThisThread());
86 QCoreApplication::postEvent(&m_threadObject, new QEvent(QEvent::User));
87}
88
89bool QQmlThreadPrivate::ThreadObject::event(QEvent *e)
90{
91 if (e->type() == QEvent::User)
92 p->threadEvent();
93 return QObject::event(e);
94}
95
96QQmlThreadPrivate::QQmlThreadPrivate(QQmlThread *q) : q(q), m_threadObject(this)
97{
98 setObjectName(QStringLiteral("QQmlThread"));
99 // This size is aligned with the recursion depth limits in the parser/codegen. In case of
100 // absurd content we want to hit the recursion checks instead of running out of stack.
101 setStackSize(8 * 1024 * 1024);
102}
103
104bool QQmlThreadPrivate::event(QEvent *e)
105{
106 if (e->type() == QEvent::User)
107 mainEvent();
108 return QThread::event(e);
109}
110
111void QQmlThreadPrivate::mainEvent()
112{
113 lock();
114
115 m_mainProcessing = true;
116
117 while (!mainList.isEmpty() || mainSync) {
118 bool isSync = mainSync != nullptr;
119 QQmlThread::Message *message = isSync?mainSync:mainList.takeFirst();
120 unlock();
121
122 message->call(q);
123 delete message;
124
125 lock();
126
127 if (isSync) {
128 mainSync = nullptr;
129 wakeOne();
130 }
131 }
132
133 m_mainProcessing = false;
134
135 unlock();
136}
137
138void QQmlThreadPrivate::threadEvent()
139{
140 lock();
141
142 for (;;) {
143 if (!threadList.isEmpty()) {
144 m_threadProcessing = true;
145
146 QQmlThread::Message *message = threadList.first();
147
148 unlock();
149
150 message->call(q);
151
152 lock();
153
154 delete threadList.takeFirst();
155 } else {
156 wakeOne();
157
158 m_threadProcessing = false;
159
160 unlock();
161
162 return;
163 }
164 }
165}
166
168: d(new QQmlThreadPrivate(this))
169{
170}
171
173{
174 delete d;
175}
176
177/*!
178 \internal
179 Starts the actual worker thread.
180 */
182{
183 Q_ASSERT(!d->m_shutdown);
184 d->start();
185 d->m_threadObject.moveToThread(d);
186}
187
189{
190 startup();
191
192 lock();
193
194 // Send a trigger event even though we are on the main thread.
195 // We don't want to process messages from inside e.g. addImportPath().
196 // This can lead to strange recursion behavior.
197 if (!d->mainList.isEmpty() || d->mainSync)
198 QCoreApplication::postEvent(d, new QEvent(QEvent::User));
199
200 if (!d->threadList.isEmpty())
201 d->triggerThreadEvent();
202
203 unlock();
204}
205
207{
208 d->lock();
209
210 // The type loader thread may have multiple messages that ask for a callback into the main
211 // thread. Simply deleting mainSync once is not enough to stop that. We need to explicitly
212 // tell the type loader thread not to wait for the main thread anymore. Therefore m_shutdown.
213 Q_ASSERT(!d->m_shutdown);
214 d->m_shutdown = true;
215
216 d->quit();
217
218 if (d->mainSync)
219 d->wakeOne();
220
221 d->unlock();
222 d->QThread::wait();
223
224 d->m_shutdown = false;
225}
226
228{
229 return d->isRunning();
230}
231
233{
234 d->lock();
235}
236
238{
239 d->unlock();
240}
241
243{
244 d->wakeOne();
245}
246
248{
249 d->wait();
250}
251
253{
254 return d->isCurrentThread();
255}
256
258{
259 // The thread() of the QQmlThread is its parent thread.
260 return d->thread()->isCurrentThread();
261}
262
263QThread *QQmlThread::thread() const
264{
265 return const_cast<QThread *>(static_cast<const QThread *>(d));
266}
267
268/*!
269 \internal
270 An object living in the QML thread, in case you want to parent
271 other objects to it.
272*/
274{
275 return &d->m_threadObject;
276}
277
278void QQmlThread::internalCallMethodInThread(Message *message)
279{
280 Q_ASSERT(!isThisThread());
281 d->lock();
282 Q_ASSERT(d->m_mainThreadWaiting == false);
283
284 bool wasEmpty = d->threadList.isEmpty();
285 d->threadList.append(message);
286 if (wasEmpty && d->m_threadProcessing == false)
287 d->triggerThreadEvent();
288
289 d->m_mainThreadWaiting = true;
290
291 do {
292 if (d->mainSync) {
293 QQmlThread::Message *message = d->mainSync;
294 unlock();
295 message->call(this);
296 delete message;
297 lock();
298 d->mainSync = nullptr;
299 wakeOne();
300 } else {
301 d->wait();
302 }
303 } while (d->mainSync || !d->threadList.isEmpty());
304
305 d->m_mainThreadWaiting = false;
306 d->unlock();
307}
308
309/*!
310 \internal
311 \note This method needs to run in the worker/QQmlThread
312
313 This runs \a message in the main thread, and blocks the
314 worker thread until the call has completed
315 */
316void QQmlThread::internalCallMethodInMain(Message *message)
317{
318 Q_ASSERT(isThisThread());
319 d->lock();
320
321 Q_ASSERT(d->mainSync == nullptr);
322 d->mainSync = message;
323
324 if (d->m_mainThreadWaiting) {
325 d->wakeOne();
326 } else if (d->m_mainProcessing) {
327 // Do nothing - it is already looping
328 } else {
329 d->triggerMainEvent();
330 }
331
332 while (d->mainSync) {
333 if (d->m_shutdown) {
334 delete d->mainSync;
335 d->mainSync = nullptr;
336 break;
337 }
338 d->wait();
339 }
340
341 d->unlock();
342}
343
344void QQmlThread::internalPostMethodToThread(Message *message)
345{
346 Q_ASSERT(!isThisThread());
347 d->lock();
348 bool wasEmpty = d->threadList.isEmpty();
349 d->threadList.append(message);
350 if (wasEmpty && d->m_threadProcessing == false)
351 d->triggerThreadEvent();
352 d->unlock();
353}
354
355void QQmlThread::internalPostMethodToMain(Message *message)
356{
357 Q_ASSERT(isThisThread());
358 d->lock();
359 bool wasEmpty = d->mainList.isEmpty();
360 d->mainList.append(message);
361 if (wasEmpty && d->m_mainProcessing == false)
362 d->triggerMainEvent();
363 d->unlock();
364}
365
366/*!
367 \internal
368 \note This method must be called in the main thread
369 \warning This method requires that the lock is held!
370
371 A call to this method will either:
372 - run a message requested to run synchronously on the main thread if there is one
373 (and return afterrwards),
374 - wait for the worker thread to notify it if the worker thread has pending work,
375 - or simply return if neither of the conditions above hold
376 */
378{
379 Q_ASSERT(!isThisThread());
380 Q_ASSERT(d->m_mainThreadWaiting == false);
381
382 d->m_mainThreadWaiting = true;
383
384 if (d->mainSync || !d->threadList.isEmpty()) {
385 if (d->mainSync) {
386 QQmlThread::Message *message = d->mainSync;
387 unlock();
388 message->call(this);
389 delete message;
390 lock();
391 d->mainSync = nullptr;
392 wakeOne();
393 } else {
394 d->wait();
395 }
396 }
397
398 d->m_mainThreadWaiting = false;
399}
400
401/*!
402 \internal
403 \note This method must be called in the main thread
404 \warning This method requires that the lock is held!
405
406 Clear all pending events, for either thread.
407*/
409{
410 Q_ASSERT(!isThisThread());
411 if (Message *mainSync = std::exchange(d->mainSync, nullptr))
412 delete mainSync;
413 while (!d->mainList.isEmpty())
414 delete d->mainList.takeFirst();
415 while (!d->threadList.isEmpty())
416 delete d->threadList.takeFirst();
417}
418
419QT_END_NAMESPACE
QQmlThreadPrivate(QQmlThread *q)
QFieldList< QQmlThread::Message, &QQmlThread::Message::next > MessageList
bool event(QEvent *e) override
This virtual function receives events to an object and should return true if the event e was recogniz...
\inmodule QtQml
QThread * thread() const
bool isRunning() const
bool isThisThread() const
void waitForNextMessage()
bool isParentThread() const
virtual ~QQmlThread()
QObject * threadObject() const
QT_REQUIRE_CONFIG(liburing)