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