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 QtCore 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 "qeventloop.h"
41
42#include "qabstracteventdispatcher.h"
43#include "qcoreapplication.h"
44#include "qcoreapplication_p.h"
45#include "qelapsedtimer.h"
46
47#include "qobject_p.h"
48#include "qeventloop_p.h"
49#include <private/qthread_p.h>
50
51#ifdef Q_OS_WASM
52#include <emscripten.h>
53#if QT_CONFIG(thread)
54#include <emscripten/threading.h>
55#endif
56#endif
57
58QT_BEGIN_NAMESPACE
59
60/*!
61 \class QEventLoop
62 \inmodule QtCore
63 \brief The QEventLoop class provides a means of entering and leaving an event loop.
64
65 At any time, you can create a QEventLoop object and call exec()
66 on it to start a local event loop. From within the event loop,
67 calling exit() will force exec() to return.
68
69 \sa QAbstractEventDispatcher
70*/
71
72/*!
73 \enum QEventLoop::ProcessEventsFlag
74
75 This enum controls the types of events processed by the
76 processEvents() functions.
77
78 \value AllEvents All events. Note that
79 \l{QEvent::DeferredDelete}{DeferredDelete} events are processed
80 specially. See QObject::deleteLater() for more details.
81
82 \value ExcludeUserInputEvents Do not process user input events,
83 such as ButtonPress and KeyPress. Note that the events are not
84 discarded; they will be delivered the next time processEvents() is
85 called without the ExcludeUserInputEvents flag.
86
87 \value ExcludeSocketNotifiers Do not process socket notifier
88 events. Note that the events are not discarded; they will be
89 delivered the next time processEvents() is called without the
90 ExcludeSocketNotifiers flag.
91
92 \value WaitForMoreEvents Wait for events if no pending events are
93 available.
94
95 \omitvalue X11ExcludeTimers
96 \omitvalue EventLoopExec
97 \omitvalue DialogExec
98
99 \sa processEvents()
100*/
101
102/*!
103 Constructs an event loop object with the given \a parent.
104*/
105QEventLoop::QEventLoop(QObject *parent)
106 : QObject(*new QEventLoopPrivate, parent)
107{
108 Q_D(QEventLoop);
109 if (!QCoreApplication::instance() && QCoreApplicationPrivate::threadRequiresCoreApplication()) {
110 qWarning(msg: "QEventLoop: Cannot be used without QApplication");
111 } else {
112 d->threadData.loadRelaxed()->ensureEventDispatcher();
113 }
114}
115
116/*!
117 Destroys the event loop object.
118*/
119QEventLoop::~QEventLoop()
120{ }
121
122
123/*!
124 Processes some pending events that match \a flags.
125 Returns \c true if pending events were handled;
126 otherwise returns \c false.
127
128 This function is especially useful if you have a long running
129 operation and want to show its progress without allowing user
130 input; i.e. by using the \l ExcludeUserInputEvents flag.
131
132 This function is simply a wrapper for
133 QAbstractEventDispatcher::processEvents(). See the documentation
134 for that function for details.
135*/
136bool QEventLoop::processEvents(ProcessEventsFlags flags)
137{
138 Q_D(QEventLoop);
139 auto threadData = d->threadData.loadRelaxed();
140 if (!threadData->hasEventDispatcher())
141 return false;
142 return threadData->eventDispatcher.loadRelaxed()->processEvents(flags);
143}
144
145/*!
146 Enters the main event loop and waits until exit() is called.
147 Returns the value that was passed to exit().
148
149 If \a flags are specified, only events of the types allowed by
150 the \a flags will be processed.
151
152 It is necessary to call this function to start event handling. The
153 main event loop receives events from the window system and
154 dispatches these to the application widgets.
155
156 Generally speaking, no user interaction can take place before
157 calling exec(). As a special case, modal widgets like QMessageBox
158 can be used before calling exec(), because modal widgets
159 use their own local event loop.
160
161 To make your application perform idle processing (i.e. executing a
162 special function whenever there are no pending events), use a
163 QTimer with 0 timeout. More sophisticated idle processing schemes
164 can be achieved using processEvents().
165
166 \sa QCoreApplication::quit(), exit(), processEvents()
167*/
168int QEventLoop::exec(ProcessEventsFlags flags)
169{
170 Q_D(QEventLoop);
171 auto threadData = d->threadData.loadRelaxed();
172
173 //we need to protect from race condition with QThread::exit
174 QMutexLocker locker(&static_cast<QThreadPrivate *>(QObjectPrivate::get(o: threadData->thread.loadAcquire()))->mutex);
175 if (threadData->quitNow)
176 return -1;
177
178 if (d->inExec) {
179 qWarning(msg: "QEventLoop::exec: instance %p has already called exec()", this);
180 return -1;
181 }
182
183 struct LoopReference {
184 QEventLoopPrivate *d;
185 QMutexLocker &locker;
186
187 bool exceptionCaught;
188 LoopReference(QEventLoopPrivate *d, QMutexLocker &locker) : d(d), locker(locker), exceptionCaught(true)
189 {
190 d->inExec = true;
191 d->exit.storeRelease(newValue: false);
192
193 auto threadData = d->threadData.loadRelaxed();
194 ++threadData->loopLevel;
195 threadData->eventLoops.push(t: d->q_func());
196
197 locker.unlock();
198 }
199
200 ~LoopReference()
201 {
202 if (exceptionCaught) {
203 qWarning(msg: "Qt has caught an exception thrown from an event handler. Throwing\n"
204 "exceptions from an event handler is not supported in Qt.\n"
205 "You must not let any exception whatsoever propagate through Qt code.\n"
206 "If that is not possible, in Qt 5 you must at least reimplement\n"
207 "QCoreApplication::notify() and catch all exceptions there.\n");
208 }
209 locker.relock();
210 auto threadData = d->threadData.loadRelaxed();
211 QEventLoop *eventLoop = threadData->eventLoops.pop();
212 Q_ASSERT_X(eventLoop == d->q_func(), "QEventLoop::exec()", "internal error");
213 Q_UNUSED(eventLoop); // --release warning
214 d->inExec = false;
215 --threadData->loopLevel;
216 }
217 };
218 LoopReference ref(d, locker);
219
220 // remove posted quit events when entering a new event loop
221 QCoreApplication *app = QCoreApplication::instance();
222 if (app && app->thread() == thread())
223 QCoreApplication::removePostedEvents(receiver: app, eventType: QEvent::Quit);
224
225#ifdef Q_OS_WASM
226 // Partial support for nested event loops: Make the runtime throw a JavaSrcript
227 // exception, which returns control to the browser while preserving the C++ stack.
228 // Event processing then continues as normal. The sleep call below never returns.
229 // QTBUG-70185
230 if (threadData->loopLevel > 1)
231 emscripten_sleep(1);
232#endif
233
234 while (!d->exit.loadAcquire())
235 processEvents(flags: flags | WaitForMoreEvents | EventLoopExec);
236
237 ref.exceptionCaught = false;
238 return d->returnCode.loadRelaxed();
239}
240
241/*!
242 Process pending events that match \a flags for a maximum of \a
243 maxTime milliseconds, or until there are no more events to
244 process, whichever is shorter.
245 This function is especially useful if you have a long running
246 operation and want to show its progress without allowing user
247 input, i.e. by using the \l ExcludeUserInputEvents flag.
248
249 \b{Notes:}
250 \list
251 \li This function does not process events continuously; it
252 returns after all available events are processed.
253 \li Specifying the \l WaitForMoreEvents flag makes no sense
254 and will be ignored.
255 \endlist
256*/
257void QEventLoop::processEvents(ProcessEventsFlags flags, int maxTime)
258{
259 Q_D(QEventLoop);
260 if (!d->threadData.loadRelaxed()->hasEventDispatcher())
261 return;
262
263 QElapsedTimer start;
264 start.start();
265 while (processEvents(flags: flags & ~WaitForMoreEvents)) {
266 if (start.elapsed() > maxTime)
267 break;
268 }
269}
270
271/*!
272 Tells the event loop to exit with a return code.
273
274 After this function has been called, the event loop returns from
275 the call to exec(). The exec() function returns \a returnCode.
276
277 By convention, a \a returnCode of 0 means success, and any non-zero
278 value indicates an error.
279
280 Note that unlike the C library function of the same name, this
281 function \e does return to the caller -- it is event processing that
282 stops.
283
284 \sa QCoreApplication::quit(), quit(), exec()
285*/
286void QEventLoop::exit(int returnCode)
287{
288 Q_D(QEventLoop);
289 auto threadData = d->threadData.loadAcquire();
290 if (!threadData->hasEventDispatcher())
291 return;
292
293 d->returnCode.storeRelaxed(newValue: returnCode);
294 d->exit.storeRelease(newValue: true);
295 threadData->eventDispatcher.loadRelaxed()->interrupt();
296
297#ifdef Q_OS_WASM
298 // QEventLoop::exec() never returns in emscripten. We implement approximate behavior here.
299 // QTBUG-70185
300 if (threadData->loopLevel == 1) {
301#if QT_CONFIG(thread)
302 if (emscripten_is_main_browser_thread())
303#endif
304 emscripten_force_exit(returnCode);
305 } else {
306 d->inExec = false;
307 --threadData->loopLevel;
308 }
309#endif
310}
311
312/*!
313 Returns \c true if the event loop is running; otherwise returns
314 false. The event loop is considered running from the time when
315 exec() is called until exit() is called.
316
317 \sa exec(), exit()
318 */
319bool QEventLoop::isRunning() const
320{
321 Q_D(const QEventLoop);
322 return !d->exit.loadAcquire();
323}
324
325/*!
326 Wakes up the event loop.
327
328 \sa QAbstractEventDispatcher::wakeUp()
329*/
330void QEventLoop::wakeUp()
331{
332 Q_D(QEventLoop);
333 auto threadData = d->threadData.loadAcquire();
334 if (!threadData->hasEventDispatcher())
335 return;
336 threadData->eventDispatcher.loadRelaxed()->wakeUp();
337}
338
339
340/*!
341 \reimp
342*/
343bool QEventLoop::event(QEvent *event)
344{
345 if (event->type() == QEvent::Quit) {
346 quit();
347 return true;
348 } else {
349 return QObject::event(event);
350 }
351}
352
353/*!
354 Tells the event loop to exit normally.
355
356 Same as exit(0).
357
358 \sa QCoreApplication::quit(), exit()
359*/
360void QEventLoop::quit()
361{ exit(returnCode: 0); }
362
363
364class QEventLoopLockerPrivate
365{
366public:
367 explicit QEventLoopLockerPrivate(QEventLoopPrivate *loop)
368 : loop(loop), type(EventLoop)
369 {
370 loop->ref();
371 }
372
373 explicit QEventLoopLockerPrivate(QThreadPrivate *thread)
374 : thread(thread), type(Thread)
375 {
376 thread->ref();
377 }
378
379 explicit QEventLoopLockerPrivate(QCoreApplicationPrivate *app)
380 : app(app), type(Application)
381 {
382 app->ref();
383 }
384
385 ~QEventLoopLockerPrivate()
386 {
387 switch (type)
388 {
389 case EventLoop:
390 loop->deref();
391 break;
392 case Thread:
393 thread->deref();
394 break;
395 default:
396 app->deref();
397 break;
398 }
399 }
400
401private:
402 union {
403 QEventLoopPrivate * loop;
404 QThreadPrivate * thread;
405 QCoreApplicationPrivate * app;
406 };
407 enum Type {
408 EventLoop,
409 Thread,
410 Application
411 };
412 const Type type;
413};
414
415/*!
416 \class QEventLoopLocker
417 \inmodule QtCore
418 \brief The QEventLoopLocker class provides a means to quit an event loop when it is no longer needed.
419 \since 5.0
420
421 The QEventLoopLocker operates on particular objects - either a QCoreApplication
422 instance, a QEventLoop instance or a QThread instance.
423
424 This makes it possible to, for example, run a batch of jobs with an event loop
425 and exit that event loop after the last job is finished. That is accomplished
426 by keeping a QEventLoopLocker with each job instance.
427
428 The variant which operates on QCoreApplication makes it possible to finish
429 asynchronously running jobs after the last gui window has been closed. This
430 can be useful for example for running a job which uploads data to a network.
431
432 \sa QEventLoop, QCoreApplication
433*/
434
435/*!
436 Creates an event locker operating on the QCoreApplication.
437
438 The application will quit when there are no more QEventLoopLockers operating on it.
439
440 \sa QCoreApplication::quit(), QCoreApplication::isQuitLockEnabled()
441 */
442QEventLoopLocker::QEventLoopLocker()
443 : d_ptr(new QEventLoopLockerPrivate(static_cast<QCoreApplicationPrivate*>(QObjectPrivate::get(o: QCoreApplication::instance()))))
444{
445
446}
447
448/*!
449 Creates an event locker operating on the \a loop.
450
451 This particular QEventLoop will quit when there are no more QEventLoopLockers operating on it.
452
453 \sa QEventLoop::quit()
454 */
455QEventLoopLocker::QEventLoopLocker(QEventLoop *loop)
456 : d_ptr(new QEventLoopLockerPrivate(static_cast<QEventLoopPrivate*>(QObjectPrivate::get(o: loop))))
457{
458
459}
460
461/*!
462 Creates an event locker operating on the \a thread.
463
464 This particular QThread will quit when there are no more QEventLoopLockers operating on it.
465
466 \sa QThread::quit()
467 */
468QEventLoopLocker::QEventLoopLocker(QThread *thread)
469 : d_ptr(new QEventLoopLockerPrivate(static_cast<QThreadPrivate*>(QObjectPrivate::get(o: thread))))
470{
471
472}
473
474/*!
475 Destroys this event loop locker object
476 */
477QEventLoopLocker::~QEventLoopLocker()
478{
479 delete d_ptr;
480}
481
482QT_END_NAMESPACE
483
484#include "moc_qeventloop.cpp"
485

source code of qtbase/src/corelib/kernel/qeventloop.cpp