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