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