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 qCDebug(lcDeleteLater) << "Increased" << threadData->thread
158 << "loop level to" << threadData->loopLevel
159 << "with leaf loop now" << threadData->eventLoops.last();
160
161 locker.unlock();
162 }
163
164 ~LoopReference()
165 {
166 if (exceptionCaught) {
167 qWarning(msg: "Qt has caught an exception thrown from an event handler. Throwing\n"
168 "exceptions from an event handler is not supported in Qt.\n"
169 "You must not let any exception whatsoever propagate through Qt code.");
170 }
171 locker.relock();
172 auto threadData = d->threadData.loadRelaxed();
173 QEventLoop *eventLoop = threadData->eventLoops.pop();
174 Q_ASSERT_X(eventLoop == d->q_func(), "QEventLoop::exec()", "internal error");
175 Q_UNUSED(eventLoop); // --release warning
176 d->inExec = false;
177 --threadData->loopLevel;
178
179 qCDebug(lcDeleteLater) << "Decreased" << threadData->thread
180 << "loop level to" << threadData->loopLevel
181 << "with leaf loop now" << (threadData->eventLoops.isEmpty()
182 ? nullptr : threadData->eventLoops.last());
183
184 }
185 };
186 LoopReference ref(d, locker);
187
188 // remove posted quit events when entering a new event loop
189 QCoreApplication *app = QCoreApplication::instance();
190 if (app && app->thread() == thread())
191 QCoreApplication::removePostedEvents(receiver: app, eventType: QEvent::Quit);
192
193 while (!d->exit.loadAcquire())
194 processEvents(flags: flags | WaitForMoreEvents | EventLoopExec);
195
196 ref.exceptionCaught = false;
197 return d->returnCode.loadRelaxed();
198}
199
200/*!
201 \overload
202
203 Process pending events that match \a flags for a maximum of \a
204 maxTime milliseconds, or until there are no more events to
205 process, whichever is shorter.
206
207 Equivalent to calling:
208 \code
209 processEvents(flags, QDeadlineTimer(maxTime));
210 \endcode
211*/
212void QEventLoop::processEvents(ProcessEventsFlags flags, int maxTime)
213{
214 processEvents(flags, deadline: QDeadlineTimer(maxTime));
215}
216
217/*!
218 \since 6.7
219
220 Process pending events that match \a flags until \a deadline has expired,
221 or until there are no more events to process, whichever happens first.
222 This function is especially useful if you have a long running
223 operation and want to show its progress without allowing user
224 input, i.e. by using the \l ExcludeUserInputEvents flag.
225
226 \b{Notes:}
227 \list
228 \li This function does not process events continuously; it
229 returns after all available events are processed.
230 \li Specifying the \l WaitForMoreEvents flag makes no sense
231 and will be ignored.
232 \endlist
233*/
234void QEventLoop::processEvents(ProcessEventsFlags flags, QDeadlineTimer deadline)
235{
236 Q_D(QEventLoop);
237 if (!d->threadData.loadRelaxed()->hasEventDispatcher())
238 return;
239
240 while (processEvents(flags: flags & ~WaitForMoreEvents)) {
241 if (deadline.hasExpired())
242 break;
243 }
244}
245
246/*!
247 Tells the event loop to exit with a return code.
248
249 After this function has been called, the event loop returns from
250 the call to exec(). The exec() function returns \a returnCode.
251
252 By convention, a \a returnCode of 0 means success, and any non-zero
253 value indicates an error.
254
255 Note that unlike the C library function of the same name, this
256 function \e does return to the caller -- it is event processing that
257 stops.
258
259 \sa QCoreApplication::quit(), quit(), exec()
260*/
261void QEventLoop::exit(int returnCode)
262{
263 Q_D(QEventLoop);
264 auto threadData = d->threadData.loadAcquire();
265 if (!threadData->hasEventDispatcher())
266 return;
267
268 d->returnCode.storeRelaxed(newValue: returnCode);
269 d->exit.storeRelease(newValue: true);
270 threadData->eventDispatcher.loadRelaxed()->interrupt();
271}
272
273/*!
274 Returns \c true if the event loop is running; otherwise returns
275 false. The event loop is considered running from the time when
276 exec() is called until exit() is called.
277
278 \sa exec(), exit()
279 */
280bool QEventLoop::isRunning() const
281{
282 Q_D(const QEventLoop);
283 return !d->exit.loadAcquire();
284}
285
286/*!
287 Wakes up the event loop.
288
289 \sa QAbstractEventDispatcher::wakeUp()
290*/
291void QEventLoop::wakeUp()
292{
293 Q_D(QEventLoop);
294 auto threadData = d->threadData.loadAcquire();
295 if (!threadData->hasEventDispatcher())
296 return;
297 threadData->eventDispatcher.loadRelaxed()->wakeUp();
298}
299
300
301/*!
302 \reimp
303*/
304bool QEventLoop::event(QEvent *event)
305{
306 if (event->type() == QEvent::Quit) {
307 quit();
308 return true;
309 } else {
310 return QObject::event(event);
311 }
312}
313
314/*!
315 Tells the event loop to exit normally.
316
317 Same as exit(0).
318
319 \sa QCoreApplication::quit(), exit()
320*/
321void QEventLoop::quit()
322{ exit(returnCode: 0); }
323
324// If any of these trigger, the Type bits will interfere with the pointer values:
325static_assert(alignof(QEventLoop) >= 4);
326static_assert(alignof(QThread) >= 4);
327static_assert(alignof(QCoreApplication) >= 4);
328
329/*!
330 \class QEventLoopLocker
331 \inmodule QtCore
332 \brief The QEventLoopLocker class provides a means to quit an event loop when it is no longer needed.
333 \since 5.0
334
335 The QEventLoopLocker operates on particular objects - either a QCoreApplication
336 instance, a QEventLoop instance or a QThread instance.
337
338 This makes it possible to, for example, run a batch of jobs with an event loop
339 and exit that event loop after the last job is finished. That is accomplished
340 by keeping a QEventLoopLocker with each job instance.
341
342 The variant which operates on QCoreApplication makes it possible to finish
343 asynchronously running jobs after the last gui window has been closed. This
344 can be useful for example for running a job which uploads data to a network.
345
346 \sa QEventLoop, QCoreApplication
347*/
348
349/*!
350 Creates an event locker operating on the QCoreApplication.
351
352 The application will attempt to quit when there are no more QEventLoopLockers
353 operating on it, as long as QCoreApplication::isQuitLockEnabled() is \c true.
354
355 Note that attempting a quit may not necessarily result in the application quitting,
356 if there for example are open windows, or the QEvent::Quit event is ignored.
357
358 \sa QCoreApplication::quit(), QCoreApplication::isQuitLockEnabled()
359 */
360QEventLoopLocker::QEventLoopLocker() noexcept
361 : QEventLoopLocker{QCoreApplication::instance(), Type::Application}
362{
363
364}
365
366/*!
367 Creates an event locker operating on the \a loop.
368
369 This particular QEventLoop will quit when there are no more QEventLoopLockers operating on it.
370
371 \sa QEventLoop::quit()
372 */
373QEventLoopLocker::QEventLoopLocker(QEventLoop *loop) noexcept
374 : QEventLoopLocker{loop, Type::EventLoop}
375{
376
377}
378
379/*!
380 Creates an event locker operating on the \a thread.
381
382 This particular QThread will quit when there are no more QEventLoopLockers operating on it.
383
384 \sa QThread::quit()
385 */
386QEventLoopLocker::QEventLoopLocker(QThread *thread) noexcept
387 : QEventLoopLocker{thread, Type::Thread}
388{
389
390}
391
392/*!
393 \fn QEventLoopLocker::QEventLoopLocker(QEventLoopLocker &&other)
394 \since 6.7
395
396 Move-constructs an event-loop locker from \a other. \a other will have a
397 no-op destructor, while responsibility for preventing the
398 QEventLoop/QThread/QCoreApplication from quitting is transferred to the new
399 object.
400*/
401
402/*!
403 \fn QEventLoopLocker &QEventLoopLocker::operator=(QEventLoopLocker &&other)
404 \since 6.7
405
406 Move-assigns this event-loop locker from \a other. \a other will have a
407 no-op destructor, while responsibility for preventing the
408 QEventLoop/QThread/QCoreApplication from quitting is transferred to this
409 object.
410*/
411
412/*!
413 \fn QEventLoopLocker::swap(QEventLoopLocker &other)
414 \since 6.7
415
416 Swaps the object and the state of this QEventLoopLocker with \a other.
417 This operation is very fast and never fails.
418*/
419
420/*!
421 \fn QEventLoopLocker::swap(QEventLoopLocker &lhs, QEventLoopLocker &rhs)
422 \since 6.7
423
424 Swaps the object and the state of \a lhs with \a rhs.
425 This operation is very fast and never fails.
426*/
427
428/*!
429 Destroys this event loop locker object
430 */
431QEventLoopLocker::~QEventLoopLocker()
432{
433 visit(f: [](auto p) { p->d_func()->deref(); });
434}
435
436/*!
437 \internal
438*/
439QEventLoopLocker::QEventLoopLocker(void *ptr, Type t) noexcept
440 : p{quintptr(ptr) | quintptr(t)}
441{
442 visit(f: [](auto p) { p->d_func()->ref(); });
443}
444
445/*!
446 \internal
447*/
448template <typename Func>
449void QEventLoopLocker::visit(Func f) const
450{
451 const auto ptr = pointer();
452 if (!ptr)
453 return;
454 switch (type()) {
455 case Type::EventLoop: return f(static_cast<QEventLoop *>(ptr));
456 case Type::Thread: return f(static_cast<QThread *>(ptr));
457 case Type::Application: return f(static_cast<QCoreApplication *>(ptr));
458 }
459 Q_UNREACHABLE();
460}
461
462QT_END_NAMESPACE
463
464#include "moc_qeventloop.cpp"
465

Provided by KDAB

Privacy Policy
Learn to use CMake with our Intro Training
Find out more

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