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