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 // Discard all remaining messages.
200 // We don't need the lock anymore because the thread is dead.
202
203 d->m_shutdown = false;
204}
205
207{
208 d->lock();
209}
210
212{
213 d->unlock();
214}
215
217{
218 d->wakeOne();
219}
220
222{
223 d->wait();
224}
225
227{
228 return d->isCurrentThread();
229}
230
231QThread *QQmlThread::thread() const
232{
233 return const_cast<QThread *>(static_cast<const QThread *>(d));
234}
235
236/*!
237 * And object living in the QML thread, in case you want to parent other objects to it.
238 */
240{
241 return &d->m_threadObject;
242}
243
244void QQmlThread::internalCallMethodInThread(Message *message)
245{
246 Q_ASSERT(!isThisThread());
247 d->lock();
248 Q_ASSERT(d->m_mainThreadWaiting == false);
249
250 bool wasEmpty = d->threadList.isEmpty();
251 d->threadList.append(message);
252 if (wasEmpty && d->m_threadProcessing == false)
253 d->triggerThreadEvent();
254
255 d->m_mainThreadWaiting = true;
256
257 do {
258 if (d->mainSync) {
259 QQmlThread::Message *message = d->mainSync;
260 unlock();
261 message->call(this);
262 delete message;
263 lock();
264 d->mainSync = nullptr;
265 wakeOne();
266 } else {
267 d->wait();
268 }
269 } while (d->mainSync || !d->threadList.isEmpty());
270
271 d->m_mainThreadWaiting = false;
272 d->unlock();
273}
274
275/*!
276 \internal
277 \note This method needs to run in the worker/QQmlThread
278
279 This runs \a message in the main thread, and blocks the
280 worker thread until the call has completed
281 */
282void QQmlThread::internalCallMethodInMain(Message *message)
283{
284 Q_ASSERT(isThisThread());
285 d->lock();
286
287 Q_ASSERT(d->mainSync == nullptr);
288 d->mainSync = message;
289
290 if (d->m_mainThreadWaiting) {
291 d->wakeOne();
292 } else if (d->m_mainProcessing) {
293 // Do nothing - it is already looping
294 } else {
295 d->triggerMainEvent();
296 }
297
298 while (d->mainSync) {
299 if (d->m_shutdown) {
300 delete d->mainSync;
301 d->mainSync = nullptr;
302 break;
303 }
304 d->wait();
305 }
306
307 d->unlock();
308}
309
310void QQmlThread::internalPostMethodToThread(Message *message)
311{
312 Q_ASSERT(!isThisThread());
313 d->lock();
314 bool wasEmpty = d->threadList.isEmpty();
315 d->threadList.append(message);
316 if (wasEmpty && d->m_threadProcessing == false)
317 d->triggerThreadEvent();
318 d->unlock();
319}
320
321void QQmlThread::internalPostMethodToMain(Message *message)
322{
323 Q_ASSERT(isThisThread());
324 d->lock();
325 bool wasEmpty = d->mainList.isEmpty();
326 d->mainList.append(message);
327 if (wasEmpty && d->m_mainProcessing == false)
328 d->triggerMainEvent();
329 d->unlock();
330}
331
332/*!
333 \internal
334 \note This method must be called in the main thread
335 \warning This method requires that the lock is held!
336
337 A call to this method will either:
338 - run a message requested to run synchronously on the main thread if there is one
339 (and return afterrwards),
340 - wait for the worker thread to notify it if the worker thread has pending work,
341 - or simply return if neither of the conditions above hold
342 */
344{
345 Q_ASSERT(!isThisThread());
346 Q_ASSERT(d->m_mainThreadWaiting == false);
347
348 d->m_mainThreadWaiting = true;
349
350 if (d->mainSync || !d->threadList.isEmpty()) {
351 if (d->mainSync) {
352 QQmlThread::Message *message = d->mainSync;
353 unlock();
354 message->call(this);
355 delete message;
356 lock();
357 d->mainSync = nullptr;
358 wakeOne();
359 } else {
360 d->wait();
361 }
362 }
363
364 d->m_mainThreadWaiting = false;
365}
366
367/*!
368 \internal
369 \note This method must be called in the main thread
370 \warning This method requires that the lock is held!
371
372 Clear all pending events, for either thread.
373*/
375{
376 Q_ASSERT(!isThisThread());
377 if (Message *mainSync = std::exchange(d->mainSync, nullptr))
378 delete mainSync;
379 while (!d->mainList.isEmpty())
380 delete d->mainList.takeFirst();
381 while (!d->threadList.isEmpty())
382 delete d->threadList.takeFirst();
383}
384
385QT_END_NAMESPACE
QQmlThreadPrivate(QQmlThread *q)
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()
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(itemmodel)