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 d->lock();
191
192 // The type loader thread may have multiple messages that ask for a callback into the main
193 // thread. Simply deleting mainSync once is not enough to stop that. We need to explicitly
194 // tell the type loader thread not to wait for the main thread anymore. Therefore m_shutdown.
195 Q_ASSERT(!d->m_shutdown);
196 d->m_shutdown = true;
197
198 d->quit();
199
200 if (d->mainSync)
201 d->wakeOne();
202
203 d->unlock();
204 d->QThread::wait();
205
206 d->m_shutdown = false;
207}
208
210{
211 d->lock();
212}
213
215{
216 d->unlock();
217}
218
220{
221 d->wakeOne();
222}
223
225{
226 d->wait();
227}
228
230{
231 return d->isCurrentThread();
232}
233
235{
236 // The thread() of the QQmlThread is its parent thread.
237 return d->thread()->isCurrentThread();
238}
239
240QThread *QQmlThread::thread() const
241{
242 return const_cast<QThread *>(static_cast<const QThread *>(d));
243}
244
245/*!
246 \internal
247 An object living in the QML thread, in case you want to parent
248 other objects to it.
249*/
251{
252 return &d->m_threadObject;
253}
254
255void QQmlThread::internalCallMethodInThread(Message *message)
256{
257 Q_ASSERT(!isThisThread());
258 d->lock();
259 Q_ASSERT(d->m_mainThreadWaiting == false);
260
261 bool wasEmpty = d->threadList.isEmpty();
262 d->threadList.append(message);
263 if (wasEmpty && d->m_threadProcessing == false)
264 d->triggerThreadEvent();
265
266 d->m_mainThreadWaiting = true;
267
268 do {
269 if (d->mainSync) {
270 QQmlThread::Message *message = d->mainSync;
271 unlock();
272 message->call(this);
273 delete message;
274 lock();
275 d->mainSync = nullptr;
276 wakeOne();
277 } else {
278 d->wait();
279 }
280 } while (d->mainSync || !d->threadList.isEmpty());
281
282 d->m_mainThreadWaiting = false;
283 d->unlock();
284}
285
286/*!
287 \internal
288 \note This method needs to run in the worker/QQmlThread
289
290 This runs \a message in the main thread, and blocks the
291 worker thread until the call has completed
292 */
293void QQmlThread::internalCallMethodInMain(Message *message)
294{
295 Q_ASSERT(isThisThread());
296 d->lock();
297
298 Q_ASSERT(d->mainSync == nullptr);
299 d->mainSync = message;
300
301 if (d->m_mainThreadWaiting) {
302 d->wakeOne();
303 } else if (d->m_mainProcessing) {
304 // Do nothing - it is already looping
305 } else {
306 d->triggerMainEvent();
307 }
308
309 while (d->mainSync) {
310 if (d->m_shutdown) {
311 delete d->mainSync;
312 d->mainSync = nullptr;
313 break;
314 }
315 d->wait();
316 }
317
318 d->unlock();
319}
320
321void QQmlThread::internalPostMethodToThread(Message *message)
322{
323 Q_ASSERT(!isThisThread());
324 d->lock();
325 bool wasEmpty = d->threadList.isEmpty();
326 d->threadList.append(message);
327 if (wasEmpty && d->m_threadProcessing == false)
328 d->triggerThreadEvent();
329 d->unlock();
330}
331
332void QQmlThread::internalPostMethodToMain(Message *message)
333{
334 Q_ASSERT(isThisThread());
335 d->lock();
336 bool wasEmpty = d->mainList.isEmpty();
337 d->mainList.append(message);
338 if (wasEmpty && d->m_mainProcessing == false)
339 d->triggerMainEvent();
340 d->unlock();
341}
342
343/*!
344 \internal
345 \note This method must be called in the main thread
346 \warning This method requires that the lock is held!
347
348 A call to this method will either:
349 - run a message requested to run synchronously on the main thread if there is one
350 (and return afterrwards),
351 - wait for the worker thread to notify it if the worker thread has pending work,
352 - or simply return if neither of the conditions above hold
353 */
355{
356 Q_ASSERT(!isThisThread());
357 Q_ASSERT(d->m_mainThreadWaiting == false);
358
359 d->m_mainThreadWaiting = true;
360
361 if (d->mainSync || !d->threadList.isEmpty()) {
362 if (d->mainSync) {
363 QQmlThread::Message *message = d->mainSync;
364 unlock();
365 message->call(this);
366 delete message;
367 lock();
368 d->mainSync = nullptr;
369 wakeOne();
370 } else {
371 d->wait();
372 }
373 }
374
375 d->m_mainThreadWaiting = false;
376}
377
378/*!
379 \internal
380 \note This method must be called in the main thread
381 \warning This method requires that the lock is held!
382
383 Clear all pending events, for either thread.
384*/
386{
387 Q_ASSERT(!isThisThread());
388 if (Message *mainSync = std::exchange(d->mainSync, nullptr))
389 delete mainSync;
390 while (!d->mainList.isEmpty())
391 delete d->mainList.takeFirst();
392 while (!d->threadList.isEmpty())
393 delete d->threadList.takeFirst();
394}
395
396QT_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 isThisThread() const
void waitForNextMessage()
bool isParentThread() const
virtual ~QQmlThread()
QObject * threadObject() const
QT_REQUIRE_CONFIG(liburing)