1 /****************************************************************************
2 **
3 ** Copyright (C) 2015 The Qt Company Ltd.
4 ** Contact: http://www.qt.io/licensing/
5 **
6 ** This file is part of the QtCore module of the Qt Toolkit.
7 **
8 ** $QT_BEGIN_LICENSE:LGPL$
9 ** Commercial License Usage
10 ** Licensees holding valid commercial Qt licenses may use this file in
11 ** accordance with the commercial license agreement provided with the
12 ** Software or, alternatively, in accordance with the terms contained in
13 ** a written agreement between you and The Qt Company. For licensing terms
14 ** and conditions see http://www.qt.io/terms-conditions. For further
15 ** information use the contact form at http://www.qt.io/contact-us.
16 **
17 ** GNU Lesser General Public License Usage
18 ** Alternatively, this file may be used under the terms of the GNU Lesser
19 ** General Public License version 2.1 or version 3 as published by the Free
20 ** Software Foundation and appearing in the file LICENSE.LGPLv21 and
21 ** LICENSE.LGPLv3 included in the packaging of this file. Please review the
22 ** following information to ensure the GNU Lesser General Public License
23 ** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
24 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
25 **
26 ** As a special exception, The Qt Company gives you certain additional
27 ** rights. These rights are described in The Qt Company LGPL Exception
28 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
29 **
30 ** GNU General Public License Usage
31 ** Alternatively, this file may be used under the terms of the GNU
32 ** General Public License version 3.0 as published by the Free Software
33 ** Foundation and appearing in the file LICENSE.GPL included in the
34 ** packaging of this file.  Please review the following information to
35 ** ensure the GNU General Public License version 3.0 requirements will be
36 ** met: http://www.gnu.org/copyleft/gpl.html.
37 **
38 ** $QT_END_LICENSE$
39 **
40 ****************************************************************************/
41 
42 #include "qeventloop.h"
43 
44 #include "qabstracteventdispatcher.h"
45 #include "qcoreapplication.h"
46 #include "qelapsedtimer.h"
47 
48 #include "qobject_p.h"
49 #include <private/qthread_p.h>
50 
51 QT_BEGIN_NAMESPACE
52 
53 class QEventLoopPrivate : public QObjectPrivate
54 {
55     Q_DECLARE_PUBLIC(QEventLoop)
56 public:
QEventLoopPrivate()57     inline QEventLoopPrivate()
58         : exit(true), inExec(false), returnCode(-1)
59     { }
60     bool exit, inExec;
61     int returnCode;
62 };
63 
64 /*!
65     \class QEventLoop
66     \brief The QEventLoop class provides a means of entering and leaving an event loop.
67 
68     At any time, you can create a QEventLoop object and call exec()
69     on it to start a local event loop. From within the event loop,
70     calling exit() will force exec() to return.
71 
72     \sa QAbstractEventDispatcher
73 */
74 
75 /*!
76     \enum QEventLoop::ProcessEventsFlag
77 
78     This enum controls the types of events processed by the
79     processEvents() functions.
80 
81     \value AllEvents All events. Note that
82     \l{QEvent::DeferredDelete}{DeferredDelete} events are processed
83     specially. See QObject::deleteLater() for more details.
84 
85     \value ExcludeUserInputEvents Do not process user input events,
86     such as ButtonPress and KeyPress. Note that the events are not
87     discarded; they will be delivered the next time processEvents() is
88     called without the ExcludeUserInputEvents flag.
89 
90     \value ExcludeSocketNotifiers Do not process socket notifier
91     events. Note that the events are not discarded; they will be
92     delivered the next time processEvents() is called without the
93     ExcludeSocketNotifiers flag.
94 
95     \value WaitForMoreEvents Wait for events if no pending events are
96     available.
97 
98     \omitvalue X11ExcludeTimers
99     \omitvalue ExcludeUserInput
100     \omitvalue WaitForMore
101     \omitvalue EventLoopExec
102     \omitvalue DialogExec
103     \value DeferredDeletion deprecated - do not use.
104 
105     \sa processEvents()
106 */
107 
108 /*!
109     Constructs an event loop object with the given \a parent.
110 */
QEventLoop(QObject * parent)111 QEventLoop::QEventLoop(QObject *parent)
112     : QObject(*new QEventLoopPrivate, parent)
113 {
114     Q_D(QEventLoop);
115     if (!QCoreApplication::instance()) {
116         qWarning("QEventLoop: Cannot be used without QApplication");
117     } else if (!d->threadData->eventDispatcher) {
118         QThreadPrivate::createEventDispatcher(d->threadData);
119     }
120 }
121 
122 /*!
123     Destroys the event loop object.
124 */
~QEventLoop()125 QEventLoop::~QEventLoop()
126 { }
127 
128 
129 /*!
130     Processes pending events that match \a flags until there are no
131     more events to process. Returns true if pending events were handled;
132     otherwise returns false.
133 
134     This function is especially useful if you have a long running
135     operation and want to show its progress without allowing user
136     input; i.e. by using the \l ExcludeUserInputEvents flag.
137 
138     This function is simply a wrapper for
139     QAbstractEventDispatcher::processEvents(). See the documentation
140     for that function for details.
141 */
processEvents(ProcessEventsFlags flags)142 bool QEventLoop::processEvents(ProcessEventsFlags flags)
143 {
144     Q_D(QEventLoop);
145     if (!d->threadData->eventDispatcher)
146         return false;
147     if (flags & DeferredDeletion)
148         QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete);
149     return d->threadData->eventDispatcher->processEvents(flags);
150 }
151 
152 /*!
153     Enters the main event loop and waits until exit() is called.
154     Returns the value that was passed to exit().
155 
156     If \a flags are specified, only events of the types allowed by
157     the \a flags will be processed.
158 
159     It is necessary to call this function to start event handling. The
160     main event loop receives events from the window system and
161     dispatches these to the application widgets.
162 
163     Generally speaking, no user interaction can take place before
164     calling exec(). As a special case, modal widgets like QMessageBox
165     can be used before calling exec(), because modal widgets
166     use their own local event loop.
167 
168     To make your application perform idle processing (i.e. executing a
169     special function whenever there are no pending events), use a
170     QTimer with 0 timeout. More sophisticated idle processing schemes
171     can be achieved using processEvents().
172 
173     \sa QApplication::quit(), exit(), processEvents()
174 */
exec(ProcessEventsFlags flags)175 int QEventLoop::exec(ProcessEventsFlags flags)
176 {
177     Q_D(QEventLoop);
178     //we need to protect from race condition with QThread::exit
179     QMutexLocker locker(&static_cast<QThreadPrivate *>(QObjectPrivate::get(d->threadData->thread))->mutex);
180     if (d->threadData->quitNow)
181         return -1;
182 
183     if (d->inExec) {
184         qWarning("QEventLoop::exec: instance %p has already called exec()", this);
185         return -1;
186     }
187     d->inExec = true;
188     d->exit = false;
189     ++d->threadData->loopLevel;
190     d->threadData->eventLoops.push(this);
191     locker.unlock();
192 
193     // remove posted quit events when entering a new event loop
194     QCoreApplication *app = QCoreApplication::instance();
195     if (app && app->thread() == thread())
196         QCoreApplication::removePostedEvents(app, QEvent::Quit);
197 
198 #if defined(QT_NO_EXCEPTIONS)
199     while (!d->exit)
200         processEvents(flags | WaitForMoreEvents | EventLoopExec);
201 #else
202     try {
203         while (!d->exit)
204             processEvents(flags | WaitForMoreEvents | EventLoopExec);
205     } catch (...) {
206         qWarning("Qt has caught an exception thrown from an event handler. Throwing\n"
207                  "exceptions from an event handler is not supported in Qt. You must\n"
208                  "reimplement QApplication::notify() and catch all exceptions there.\n");
209 
210         // copied from below
211         locker.relock();
212         QEventLoop *eventLoop = d->threadData->eventLoops.pop();
213         Q_ASSERT_X(eventLoop == this, "QEventLoop::exec()", "internal error");
214         Q_UNUSED(eventLoop); // --release warning
215         d->inExec = false;
216         --d->threadData->loopLevel;
217 
218         throw;
219     }
220 #endif
221 
222     // copied above
223     locker.relock();
224     QEventLoop *eventLoop = d->threadData->eventLoops.pop();
225     Q_ASSERT_X(eventLoop == this, "QEventLoop::exec()", "internal error");
226     Q_UNUSED(eventLoop); // --release warning
227     d->inExec = false;
228     --d->threadData->loopLevel;
229 
230     return d->returnCode;
231 }
232 
233 /*!
234     Process pending events that match \a flags for a maximum of \a
235     maxTime milliseconds, or until there are no more events to
236     process, whichever is shorter.
237     This function is especially useful if you have a long running
238     operation and want to show its progress without allowing user
239     input, i.e. by using the \l ExcludeUserInputEvents flag.
240 
241     \bold{Notes:}
242     \list
243     \o This function does not process events continuously; it
244        returns after all available events are processed.
245     \o Specifying the \l WaitForMoreEvents flag makes no sense
246        and will be ignored.
247     \endlist
248 */
processEvents(ProcessEventsFlags flags,int maxTime)249 void QEventLoop::processEvents(ProcessEventsFlags flags, int maxTime)
250 {
251     Q_D(QEventLoop);
252     if (!d->threadData->eventDispatcher)
253         return;
254 
255     QElapsedTimer start;
256     start.start();
257     if (flags & DeferredDeletion)
258         QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete);
259     while (processEvents(flags & ~WaitForMoreEvents)) {
260         if (start.elapsed() > maxTime)
261             break;
262         if (flags & DeferredDeletion)
263             QCoreApplication::sendPostedEvents(0, QEvent::DeferredDelete);
264     }
265 }
266 
267 /*!
268     Tells the event loop to exit with a return code.
269 
270     After this function has been called, the event loop returns from
271     the call to exec(). The exec() function returns \a returnCode.
272 
273     By convention, a \a returnCode of 0 means success, and any non-zero
274     value indicates an error.
275 
276     Note that unlike the C library function of the same name, this
277     function \e does return to the caller -- it is event processing that
278     stops.
279 
280     \sa QCoreApplication::quit(), quit(), exec()
281 */
exit(int returnCode)282 void QEventLoop::exit(int returnCode)
283 {
284     Q_D(QEventLoop);
285     if (!d->threadData->eventDispatcher)
286         return;
287 
288     d->returnCode = returnCode;
289     d->exit = true;
290     d->threadData->eventDispatcher->interrupt();
291 }
292 
293 /*!
294     Returns true if the event loop is running; otherwise returns
295     false. The event loop is considered running from the time when
296     exec() is called until exit() is called.
297 
298     \sa exec() exit()
299  */
isRunning() const300 bool QEventLoop::isRunning() const
301 {
302     Q_D(const QEventLoop);
303     return !d->exit;
304 }
305 
306 /*!
307     Wakes up the event loop.
308 
309     \sa QAbstractEventDispatcher::wakeUp()
310 */
wakeUp()311 void QEventLoop::wakeUp()
312 {
313     Q_D(QEventLoop);
314     if (!d->threadData->eventDispatcher)
315         return;
316     d->threadData->eventDispatcher->wakeUp();
317 }
318 
319 /*!
320     Tells the event loop to exit normally.
321 
322     Same as exit(0).
323 
324     \sa QCoreApplication::quit(), exit()
325 */
quit()326 void QEventLoop::quit()
327 { exit(0); }
328 
329 QT_END_NAMESPACE
330