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