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