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

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