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

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