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