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
50 QT_BEGIN_NAMESPACE
51
52 class QQmlThreadPrivate : public QThread
53 {
54 public:
55 QQmlThreadPrivate(QQmlThread *);
56 QQmlThread *q;
57
58 void run() override;
59
mutex()60 inline QMutex &mutex() { return _mutex; }
lock()61 inline void lock() { _mutex.lock(); }
unlock()62 inline void unlock() { _mutex.unlock(); }
wait()63 inline void wait() { _wait.wait(&_mutex); }
wakeOne()64 inline void wakeOne() { _wait.wakeOne(); }
wakeAll()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
84 protected:
85 bool event(QEvent *) override;
86
87 private:
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
MainObject(QQmlThreadPrivate * p)99 QQmlThreadPrivate::MainObject::MainObject(QQmlThreadPrivate *p)
100 : p(p)
101 {
102 }
103
104 // Trigger mainEvent in main thread. Must be called from thread.
triggerMainEvent()105 void QQmlThreadPrivate::triggerMainEvent()
106 {
107 #if QT_CONFIG(thread)
108 Q_ASSERT(q->isThisThread());
109 #endif
110 QCoreApplication::postEvent(&m_mainObject, new QEvent(QEvent::User));
111 }
112
113 // Trigger even in thread. Must be called from main thread.
triggerThreadEvent()114 void QQmlThreadPrivate::triggerThreadEvent()
115 {
116 #if QT_CONFIG(thread)
117 Q_ASSERT(!q->isThisThread());
118 #endif
119 QCoreApplication::postEvent(this, new QEvent(QEvent::User));
120 }
121
event(QEvent * e)122 bool QQmlThreadPrivate::MainObject::event(QEvent *e)
123 {
124 if (e->type() == QEvent::User)
125 p->mainEvent();
126 return QObject::event(e);
127 }
128
QQmlThreadPrivate(QQmlThread * q)129 QQmlThreadPrivate::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
event(QEvent * e)139 bool QQmlThreadPrivate::event(QEvent *e)
140 {
141 if (e->type() == QEvent::User)
142 threadEvent();
143 return QThread::event(e);
144 }
145
run()146 void QQmlThreadPrivate::run()
147 {
148 lock();
149
150 wakeOne();
151
152 unlock();
153
154 q->startupThread();
155 exec();
156 q->shutdownThread();
157 }
158
mainEvent()159 void 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
threadEvent()186 void 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
QQmlThread()221 QQmlThread::QQmlThread()
222 : d(new QQmlThreadPrivate(this))
223 {
224 }
225
~QQmlThread()226 QQmlThread::~QQmlThread()
227 {
228 delete d;
229 }
230
startup()231 void QQmlThread::startup()
232 {
233 d->lock();
234 d->start();
235 d->wait();
236 d->unlock();
237 d->moveToThread(d);
238 }
239
shutdown()240 void 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
isShutdown() const267 bool QQmlThread::isShutdown() const
268 {
269 return d->m_shutdown;
270 }
271
mutex()272 QMutex &QQmlThread::mutex()
273 {
274 return d->mutex();
275 }
276
lock()277 void QQmlThread::lock()
278 {
279 d->lock();
280 }
281
unlock()282 void QQmlThread::unlock()
283 {
284 d->unlock();
285 }
286
wakeOne()287 void QQmlThread::wakeOne()
288 {
289 d->wakeOne();
290 }
291
wakeAll()292 void QQmlThread::wakeAll()
293 {
294 d->wakeAll();
295 }
296
wait()297 void QQmlThread::wait()
298 {
299 d->wait();
300 }
301
isThisThread() const302 bool QQmlThread::isThisThread() const
303 {
304 return QThread::currentThread() == d;
305 }
306
thread() const307 QThread *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.
startupThread()313 void QQmlThread::startupThread()
314 {
315 }
316
317 // Called when the thread shuts down. Do cleanup in here.
shutdownThread()318 void QQmlThread::shutdownThread()
319 {
320 }
321
internalCallMethodInThread(Message * message)322 void 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(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
internalCallMethodInMain(Message * message)359 void 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
internalPostMethodToThread(Message * message)394 void 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(message);
404 if (wasEmpty && d->m_threadProcessing == false)
405 d->triggerThreadEvent();
406 d->unlock();
407 }
408
internalPostMethodToMain(Message * message)409 void 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(message);
417 if (wasEmpty && d->m_mainProcessing == false)
418 d->triggerMainEvent();
419 d->unlock();
420 }
421
waitForNextMessage()422 void 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
451 QT_END_NAMESPACE
452