1/****************************************************************************
2**
3** Copyright (C) 2016 The Qt Company Ltd.
4** Contact: https://www.qt.io/licensing/
5**
6** This file is part of the QtQml module of the Qt Toolkit.
7**
8** $QT_BEGIN_LICENSE:LGPL$
9** Commercial License Usage
10** Licensees holding valid commercial Qt licenses may use this file in
11** accordance with the commercial license agreement provided with the
12** Software or, alternatively, in accordance with the terms contained in
13** a written agreement between you and The Qt Company. For licensing terms
14** and conditions see https://www.qt.io/terms-conditions. For further
15** information use the contact form at https://www.qt.io/contact-us.
16**
17** GNU Lesser General Public License Usage
18** Alternatively, this file may be used under the terms of the GNU Lesser
19** General Public License version 3 as published by the Free Software
20** Foundation and appearing in the file LICENSE.LGPL3 included in the
21** packaging of this file. Please review the following information to
22** ensure the GNU Lesser General Public License version 3 requirements
23** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
24**
25** GNU General Public License Usage
26** Alternatively, this file may be used under the terms of the GNU
27** General Public License version 2.0 or (at your option) the GNU General
28** Public license version 3 or any later version approved by the KDE Free
29** Qt Foundation. The licenses are as published by the Free Software
30** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
31** included in the packaging of this file. Please review the following
32** information to ensure the GNU General Public License requirements will
33** be met: https://www.gnu.org/licenses/gpl-2.0.html and
34** https://www.gnu.org/licenses/gpl-3.0.html.
35**
36** $QT_END_LICENSE$
37**
38****************************************************************************/
39
40#include "qqmlthread_p.h"
41
42#include <private/qfieldlist_p.h>
43
44#include <QtCore/qmutex.h>
45#include <QtCore/qthread.h>
46#include <QtCore/qcoreevent.h>
47#include <QtCore/qwaitcondition.h>
48#include <QtCore/qcoreapplication.h>
49
50QT_BEGIN_NAMESPACE
51
52class QQmlThreadPrivate : public QThread
53{
54public:
55 QQmlThreadPrivate(QQmlThread *);
56 QQmlThread *q;
57
58 void run() override;
59
60 inline QMutex &mutex() { return _mutex; }
61 inline void lock() { _mutex.lock(); }
62 inline void unlock() { _mutex.unlock(); }
63 inline void wait() { _wait.wait(lockedMutex: &_mutex); }
64 inline void wakeOne() { _wait.wakeOne(); }
65 inline void wakeAll() { _wait.wakeAll(); }
66
67 quint32 m_threadProcessing:1; // Set when the thread is processing messages
68 quint32 m_mainProcessing:1; // Set when the main thread is processing messages
69 quint32 m_shutdown:1; // Set by main thread to request a shutdown
70 quint32 m_mainThreadWaiting:1; // Set by main thread if it is waiting for the message queue to empty
71
72 typedef QFieldList<QQmlThread::Message, &QQmlThread::Message::next> MessageList;
73 MessageList threadList;
74 MessageList mainList;
75
76 QQmlThread::Message *mainSync;
77
78 void triggerMainEvent();
79 void triggerThreadEvent();
80
81 void mainEvent();
82 void threadEvent();
83
84protected:
85 bool event(QEvent *) override;
86
87private:
88 struct MainObject : public QObject {
89 MainObject(QQmlThreadPrivate *p);
90 bool event(QEvent *e) override;
91 QQmlThreadPrivate *p;
92 };
93 MainObject m_mainObject;
94
95 QMutex _mutex;
96 QWaitCondition _wait;
97};
98
99QQmlThreadPrivate::MainObject::MainObject(QQmlThreadPrivate *p)
100: p(p)
101{
102}
103
104// Trigger mainEvent in main thread. Must be called from thread.
105void QQmlThreadPrivate::triggerMainEvent()
106{
107#if QT_CONFIG(thread)
108 Q_ASSERT(q->isThisThread());
109#endif
110 QCoreApplication::postEvent(receiver: &m_mainObject, event: new QEvent(QEvent::User));
111}
112
113// Trigger even in thread. Must be called from main thread.
114void QQmlThreadPrivate::triggerThreadEvent()
115{
116#if QT_CONFIG(thread)
117 Q_ASSERT(!q->isThisThread());
118#endif
119 QCoreApplication::postEvent(receiver: this, event: new QEvent(QEvent::User));
120}
121
122bool QQmlThreadPrivate::MainObject::event(QEvent *e)
123{
124 if (e->type() == QEvent::User)
125 p->mainEvent();
126 return QObject::event(event: e);
127}
128
129QQmlThreadPrivate::QQmlThreadPrivate(QQmlThread *q)
130: q(q), m_threadProcessing(false), m_mainProcessing(false), m_shutdown(false),
131 m_mainThreadWaiting(false), mainSync(nullptr), m_mainObject(this)
132{
133 setObjectName(QStringLiteral("QQmlThread"));
134 // This size is aligned with the recursion depth limits in the parser/codegen. In case of
135 // absurd content we want to hit the recursion checks instead of running out of stack.
136 setStackSize(8 * 1024 * 1024);
137}
138
139bool QQmlThreadPrivate::event(QEvent *e)
140{
141 if (e->type() == QEvent::User)
142 threadEvent();
143 return QThread::event(event: e);
144}
145
146void QQmlThreadPrivate::run()
147{
148 lock();
149
150 wakeOne();
151
152 unlock();
153
154 q->startupThread();
155 exec();
156 q->shutdownThread();
157}
158
159void QQmlThreadPrivate::mainEvent()
160{
161 lock();
162
163 m_mainProcessing = true;
164
165 while (!mainList.isEmpty() || mainSync) {
166 bool isSync = mainSync != nullptr;
167 QQmlThread::Message *message = isSync?mainSync:mainList.takeFirst();
168 unlock();
169
170 message->call(q);
171 delete message;
172
173 lock();
174
175 if (isSync) {
176 mainSync = nullptr;
177 wakeOne();
178 }
179 }
180
181 m_mainProcessing = false;
182
183 unlock();
184}
185
186void QQmlThreadPrivate::threadEvent()
187{
188 lock();
189
190 for (;;) {
191 if (!threadList.isEmpty()) {
192 m_threadProcessing = true;
193
194 QQmlThread::Message *message = threadList.first();
195
196 unlock();
197
198 message->call(q);
199
200 lock();
201
202 delete threadList.takeFirst();
203 } else if (m_shutdown) {
204 quit();
205 wakeOne();
206 unlock();
207
208 return;
209 } else {
210 wakeOne();
211
212 m_threadProcessing = false;
213
214 unlock();
215
216 return;
217 }
218 }
219}
220
221QQmlThread::QQmlThread()
222: d(new QQmlThreadPrivate(this))
223{
224}
225
226QQmlThread::~QQmlThread()
227{
228 delete d;
229}
230
231void QQmlThread::startup()
232{
233 d->lock();
234 d->start();
235 d->wait();
236 d->unlock();
237 d->moveToThread(thread: d);
238}
239
240void QQmlThread::shutdown()
241{
242 d->lock();
243 Q_ASSERT(!d->m_shutdown);
244
245 d->m_shutdown = true;
246 for (;;) {
247 if (d->mainSync || !d->mainList.isEmpty()) {
248 d->unlock();
249 d->mainEvent();
250 d->lock();
251 } else if (!d->threadList.isEmpty()) {
252 d->wait();
253 } else {
254 break;
255 }
256 }
257
258 if (QCoreApplication::closingDown())
259 d->quit();
260 else
261 d->triggerThreadEvent();
262
263 d->unlock();
264 d->QThread::wait();
265}
266
267bool QQmlThread::isShutdown() const
268{
269 return d->m_shutdown;
270}
271
272QMutex &QQmlThread::mutex()
273{
274 return d->mutex();
275}
276
277void QQmlThread::lock()
278{
279 d->lock();
280}
281
282void QQmlThread::unlock()
283{
284 d->unlock();
285}
286
287void QQmlThread::wakeOne()
288{
289 d->wakeOne();
290}
291
292void QQmlThread::wakeAll()
293{
294 d->wakeAll();
295}
296
297void QQmlThread::wait()
298{
299 d->wait();
300}
301
302bool QQmlThread::isThisThread() const
303{
304 return QThread::currentThread() == d;
305}
306
307QThread *QQmlThread::thread() const
308{
309 return const_cast<QThread *>(static_cast<const QThread *>(d));
310}
311
312// Called when the thread starts. Do startup stuff in here.
313void QQmlThread::startupThread()
314{
315}
316
317// Called when the thread shuts down. Do cleanup in here.
318void QQmlThread::shutdownThread()
319{
320}
321
322void QQmlThread::internalCallMethodInThread(Message *message)
323{
324#if !QT_CONFIG(thread)
325 message->call(this);
326 delete message;
327 return;
328#endif
329
330 Q_ASSERT(!isThisThread());
331 d->lock();
332 Q_ASSERT(d->m_mainThreadWaiting == false);
333
334 bool wasEmpty = d->threadList.isEmpty();
335 d->threadList.append(v: message);
336 if (wasEmpty && d->m_threadProcessing == false)
337 d->triggerThreadEvent();
338
339 d->m_mainThreadWaiting = true;
340
341 do {
342 if (d->mainSync) {
343 QQmlThread::Message *message = d->mainSync;
344 unlock();
345 message->call(this);
346 delete message;
347 lock();
348 d->mainSync = nullptr;
349 wakeOne();
350 } else {
351 d->wait();
352 }
353 } while (d->mainSync || !d->threadList.isEmpty());
354
355 d->m_mainThreadWaiting = false;
356 d->unlock();
357}
358
359void QQmlThread::internalCallMethodInMain(Message *message)
360{
361#if !QT_CONFIG(thread)
362 message->call(this);
363 delete message;
364 return;
365#endif
366
367 Q_ASSERT(isThisThread());
368
369 d->lock();
370
371 Q_ASSERT(d->mainSync == nullptr);
372 d->mainSync = message;
373
374 if (d->m_mainThreadWaiting) {
375 d->wakeOne();
376 } else if (d->m_mainProcessing) {
377 // Do nothing - it is already looping
378 } else {
379 d->triggerMainEvent();
380 }
381
382 while (d->mainSync) {
383 if (d->m_shutdown) {
384 delete d->mainSync;
385 d->mainSync = nullptr;
386 break;
387 }
388 d->wait();
389 }
390
391 d->unlock();
392}
393
394void QQmlThread::internalPostMethodToThread(Message *message)
395{
396#if !QT_CONFIG(thread)
397 internalPostMethodToMain(message);
398 return;
399#endif
400 Q_ASSERT(!isThisThread());
401 d->lock();
402 bool wasEmpty = d->threadList.isEmpty();
403 d->threadList.append(v: message);
404 if (wasEmpty && d->m_threadProcessing == false)
405 d->triggerThreadEvent();
406 d->unlock();
407}
408
409void QQmlThread::internalPostMethodToMain(Message *message)
410{
411#if QT_CONFIG(thread)
412 Q_ASSERT(isThisThread());
413#endif
414 d->lock();
415 bool wasEmpty = d->mainList.isEmpty();
416 d->mainList.append(v: message);
417 if (wasEmpty && d->m_mainProcessing == false)
418 d->triggerMainEvent();
419 d->unlock();
420}
421
422void QQmlThread::waitForNextMessage()
423{
424#if QT_CONFIG(thread)
425 Q_ASSERT(!isThisThread());
426#endif
427 d->lock();
428 Q_ASSERT(d->m_mainThreadWaiting == false);
429
430 d->m_mainThreadWaiting = true;
431
432 if (d->mainSync || !d->threadList.isEmpty()) {
433 if (d->mainSync) {
434 QQmlThread::Message *message = d->mainSync;
435 unlock();
436 message->call(this);
437 delete message;
438 lock();
439 d->mainSync = nullptr;
440 wakeOne();
441 } else {
442 d->wait();
443 }
444 }
445
446 d->m_mainThreadWaiting = false;
447 d->unlock();
448}
449
450
451QT_END_NAMESPACE
452

source code of qtdeclarative/src/qml/qml/ftw/qqmlthread.cpp