| /**************************************************************************** |
| ** |
| ** Copyright (C) 2016 The Qt Company Ltd. |
| ** Contact: https://www.qt.io/licensing/ |
| ** |
| ** This file is part of the QtCore module of the Qt Toolkit. |
| ** |
| ** $QT_BEGIN_LICENSE:LGPL$ |
| ** Commercial License Usage |
| ** Licensees holding valid commercial Qt licenses may use this file in |
| ** accordance with the commercial license agreement provided with the |
| ** Software or, alternatively, in accordance with the terms contained in |
| ** a written agreement between you and The Qt Company. For licensing terms |
| ** and conditions see https://www.qt.io/terms-conditions. For further |
| ** information use the contact form at https://www.qt.io/contact-us. |
| ** |
| ** GNU Lesser General Public License Usage |
| ** Alternatively, this file may be used under the terms of the GNU Lesser |
| ** General Public License version 3 as published by the Free Software |
| ** Foundation and appearing in the file LICENSE.LGPL3 included in the |
| ** packaging of this file. Please review the following information to |
| ** ensure the GNU Lesser General Public License version 3 requirements |
| ** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. |
| ** |
| ** GNU General Public License Usage |
| ** Alternatively, this file may be used under the terms of the GNU |
| ** General Public License version 2.0 or (at your option) the GNU General |
| ** Public license version 3 or any later version approved by the KDE Free |
| ** Qt Foundation. The licenses are as published by the Free Software |
| ** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 |
| ** included in the packaging of this file. Please review the following |
| ** information to ensure the GNU General Public License requirements will |
| ** be met: https://www.gnu.org/licenses/gpl-2.0.html and |
| ** https://www.gnu.org/licenses/gpl-3.0.html. |
| ** |
| ** $QT_END_LICENSE$ |
| ** |
| ****************************************************************************/ |
| |
| #include "qeventloop.h" |
| |
| #include "qabstracteventdispatcher.h" |
| #include "qcoreapplication.h" |
| #include "qcoreapplication_p.h" |
| #include "qelapsedtimer.h" |
| |
| #include "qobject_p.h" |
| #include "qeventloop_p.h" |
| #include <private/qthread_p.h> |
| |
| #ifdef Q_OS_WASM |
| #include <emscripten.h> |
| #endif |
| |
| QT_BEGIN_NAMESPACE |
| |
| /*! |
| \class QEventLoop |
| \inmodule QtCore |
| \brief The QEventLoop class provides a means of entering and leaving an event loop. |
| |
| At any time, you can create a QEventLoop object and call exec() |
| on it to start a local event loop. From within the event loop, |
| calling exit() will force exec() to return. |
| |
| \sa QAbstractEventDispatcher |
| */ |
| |
| /*! |
| \enum QEventLoop::ProcessEventsFlag |
| |
| This enum controls the types of events processed by the |
| processEvents() functions. |
| |
| \value AllEvents All events. Note that |
| \l{QEvent::DeferredDelete}{DeferredDelete} events are processed |
| specially. See QObject::deleteLater() for more details. |
| |
| \value ExcludeUserInputEvents Do not process user input events, |
| such as ButtonPress and KeyPress. Note that the events are not |
| discarded; they will be delivered the next time processEvents() is |
| called without the ExcludeUserInputEvents flag. |
| |
| \value ExcludeSocketNotifiers Do not process socket notifier |
| events. Note that the events are not discarded; they will be |
| delivered the next time processEvents() is called without the |
| ExcludeSocketNotifiers flag. |
| |
| \value WaitForMoreEvents Wait for events if no pending events are |
| available. |
| |
| \omitvalue X11ExcludeTimers |
| \omitvalue EventLoopExec |
| \omitvalue DialogExec |
| |
| \sa processEvents() |
| */ |
| |
| /*! |
| Constructs an event loop object with the given \a parent. |
| */ |
| QEventLoop::QEventLoop(QObject *parent) |
| : QObject(*new QEventLoopPrivate, parent) |
| { |
| Q_D(QEventLoop); |
| if (!QCoreApplication::instance() && QCoreApplicationPrivate::threadRequiresCoreApplication()) { |
| qWarning("QEventLoop: Cannot be used without QApplication"); |
| } else { |
| d->threadData->ensureEventDispatcher(); |
| } |
| } |
| |
| /*! |
| Destroys the event loop object. |
| */ |
| QEventLoop::~QEventLoop() |
| { } |
| |
| |
| /*! |
| Processes pending events that match \a flags until there are no |
| more events to process. Returns \c true if pending events were handled; |
| otherwise returns \c false. |
| |
| This function is especially useful if you have a long running |
| operation and want to show its progress without allowing user |
| input; i.e. by using the \l ExcludeUserInputEvents flag. |
| |
| This function is simply a wrapper for |
| QAbstractEventDispatcher::processEvents(). See the documentation |
| for that function for details. |
| */ |
| bool QEventLoop::processEvents(ProcessEventsFlags flags) |
| { |
| Q_D(QEventLoop); |
| if (!d->threadData->hasEventDispatcher()) |
| return false; |
| return d->threadData->eventDispatcher.loadRelaxed()->processEvents(flags); |
| } |
| |
| /*! |
| Enters the main event loop and waits until exit() is called. |
| Returns the value that was passed to exit(). |
| |
| If \a flags are specified, only events of the types allowed by |
| the \a flags will be processed. |
| |
| It is necessary to call this function to start event handling. The |
| main event loop receives events from the window system and |
| dispatches these to the application widgets. |
| |
| Generally speaking, no user interaction can take place before |
| calling exec(). As a special case, modal widgets like QMessageBox |
| can be used before calling exec(), because modal widgets |
| use their own local event loop. |
| |
| To make your application perform idle processing (i.e. executing a |
| special function whenever there are no pending events), use a |
| QTimer with 0 timeout. More sophisticated idle processing schemes |
| can be achieved using processEvents(). |
| |
| \sa QCoreApplication::quit(), exit(), processEvents() |
| */ |
| int QEventLoop::exec(ProcessEventsFlags flags) |
| { |
| Q_D(QEventLoop); |
| //we need to protect from race condition with QThread::exit |
| QMutexLocker locker(&static_cast<QThreadPrivate *>(QObjectPrivate::get(d->threadData->thread.loadAcquire()))->mutex); |
| if (d->threadData->quitNow) |
| return -1; |
| |
| if (d->inExec) { |
| qWarning("QEventLoop::exec: instance %p has already called exec()", this); |
| return -1; |
| } |
| |
| struct LoopReference { |
| QEventLoopPrivate *d; |
| QMutexLocker &locker; |
| |
| bool exceptionCaught; |
| LoopReference(QEventLoopPrivate *d, QMutexLocker &locker) : d(d), locker(locker), exceptionCaught(true) |
| { |
| d->inExec = true; |
| d->exit.storeRelease(false); |
| ++d->threadData->loopLevel; |
| d->threadData->eventLoops.push(d->q_func()); |
| locker.unlock(); |
| } |
| |
| ~LoopReference() |
| { |
| if (exceptionCaught) { |
| qWarning("Qt has caught an exception thrown from an event handler. Throwing\n" |
| "exceptions from an event handler is not supported in Qt.\n" |
| "You must not let any exception whatsoever propagate through Qt code.\n" |
| "If that is not possible, in Qt 5 you must at least reimplement\n" |
| "QCoreApplication::notify() and catch all exceptions there.\n"); |
| } |
| locker.relock(); |
| QEventLoop *eventLoop = d->threadData->eventLoops.pop(); |
| Q_ASSERT_X(eventLoop == d->q_func(), "QEventLoop::exec()", "internal error"); |
| Q_UNUSED(eventLoop); // --release warning |
| d->inExec = false; |
| --d->threadData->loopLevel; |
| } |
| }; |
| LoopReference ref(d, locker); |
| |
| // remove posted quit events when entering a new event loop |
| QCoreApplication *app = QCoreApplication::instance(); |
| if (app && app->thread() == thread()) |
| QCoreApplication::removePostedEvents(app, QEvent::Quit); |
| |
| #ifdef Q_OS_WASM |
| // Partial support for nested event loops: Make the runtime throw a JavaSrcript |
| // exception, which returns control to the browser while preserving the C++ stack. |
| // Event processing then continues as normal. The sleep call below never returns. |
| // QTBUG-70185 |
| if (d->threadData->loopLevel > 1) |
| emscripten_sleep(1); |
| #endif |
| |
| while (!d->exit.loadAcquire()) |
| processEvents(flags | WaitForMoreEvents | EventLoopExec); |
| |
| ref.exceptionCaught = false; |
| return d->returnCode.loadRelaxed(); |
| } |
| |
| /*! |
| Process pending events that match \a flags for a maximum of \a |
| maxTime milliseconds, or until there are no more events to |
| process, whichever is shorter. |
| This function is especially useful if you have a long running |
| operation and want to show its progress without allowing user |
| input, i.e. by using the \l ExcludeUserInputEvents flag. |
| |
| \b{Notes:} |
| \list |
| \li This function does not process events continuously; it |
| returns after all available events are processed. |
| \li Specifying the \l WaitForMoreEvents flag makes no sense |
| and will be ignored. |
| \endlist |
| */ |
| void QEventLoop::processEvents(ProcessEventsFlags flags, int maxTime) |
| { |
| Q_D(QEventLoop); |
| if (!d->threadData->hasEventDispatcher()) |
| return; |
| |
| QElapsedTimer start; |
| start.start(); |
| while (processEvents(flags & ~WaitForMoreEvents)) { |
| if (start.elapsed() > maxTime) |
| break; |
| } |
| } |
| |
| /*! |
| Tells the event loop to exit with a return code. |
| |
| After this function has been called, the event loop returns from |
| the call to exec(). The exec() function returns \a returnCode. |
| |
| By convention, a \a returnCode of 0 means success, and any non-zero |
| value indicates an error. |
| |
| Note that unlike the C library function of the same name, this |
| function \e does return to the caller -- it is event processing that |
| stops. |
| |
| \sa QCoreApplication::quit(), quit(), exec() |
| */ |
| void QEventLoop::exit(int returnCode) |
| { |
| Q_D(QEventLoop); |
| if (!d->threadData->hasEventDispatcher()) |
| return; |
| |
| d->returnCode.storeRelaxed(returnCode); |
| d->exit.storeRelease(true); |
| d->threadData->eventDispatcher.loadRelaxed()->interrupt(); |
| |
| #ifdef Q_OS_WASM |
| // QEventLoop::exec() never returns in emscripten. We implement approximate behavior here. |
| // QTBUG-70185 |
| if (d->threadData->loopLevel == 1) { |
| emscripten_force_exit(returnCode); |
| } else { |
| d->inExec = false; |
| --d->threadData->loopLevel; |
| } |
| #endif |
| } |
| |
| /*! |
| Returns \c true if the event loop is running; otherwise returns |
| false. The event loop is considered running from the time when |
| exec() is called until exit() is called. |
| |
| \sa exec(), exit() |
| */ |
| bool QEventLoop::isRunning() const |
| { |
| Q_D(const QEventLoop); |
| return !d->exit.loadAcquire(); |
| } |
| |
| /*! |
| Wakes up the event loop. |
| |
| \sa QAbstractEventDispatcher::wakeUp() |
| */ |
| void QEventLoop::wakeUp() |
| { |
| Q_D(QEventLoop); |
| if (!d->threadData->hasEventDispatcher()) |
| return; |
| d->threadData->eventDispatcher.loadRelaxed()->wakeUp(); |
| } |
| |
| |
| /*! |
| \reimp |
| */ |
| bool QEventLoop::event(QEvent *event) |
| { |
| if (event->type() == QEvent::Quit) { |
| quit(); |
| return true; |
| } else { |
| return QObject::event(event); |
| } |
| } |
| |
| /*! |
| Tells the event loop to exit normally. |
| |
| Same as exit(0). |
| |
| \sa QCoreApplication::quit(), exit() |
| */ |
| void QEventLoop::quit() |
| { exit(0); } |
| |
| |
| class QEventLoopLockerPrivate |
| { |
| public: |
| explicit QEventLoopLockerPrivate(QEventLoopPrivate *loop) |
| : loop(loop), type(EventLoop) |
| { |
| loop->ref(); |
| } |
| |
| explicit QEventLoopLockerPrivate(QThreadPrivate *thread) |
| : thread(thread), type(Thread) |
| { |
| thread->ref(); |
| } |
| |
| explicit QEventLoopLockerPrivate(QCoreApplicationPrivate *app) |
| : app(app), type(Application) |
| { |
| app->ref(); |
| } |
| |
| ~QEventLoopLockerPrivate() |
| { |
| switch (type) |
| { |
| case EventLoop: |
| loop->deref(); |
| break; |
| case Thread: |
| thread->deref(); |
| break; |
| default: |
| app->deref(); |
| break; |
| } |
| } |
| |
| private: |
| union { |
| QEventLoopPrivate * loop; |
| QThreadPrivate * thread; |
| QCoreApplicationPrivate * app; |
| }; |
| enum Type { |
| EventLoop, |
| Thread, |
| Application |
| }; |
| const Type type; |
| }; |
| |
| /*! |
| \class QEventLoopLocker |
| \inmodule QtCore |
| \brief The QEventLoopLocker class provides a means to quit an event loop when it is no longer needed. |
| \since 5.0 |
| |
| The QEventLoopLocker operates on particular objects - either a QCoreApplication |
| instance, a QEventLoop instance or a QThread instance. |
| |
| This makes it possible to, for example, run a batch of jobs with an event loop |
| and exit that event loop after the last job is finished. That is accomplished |
| by keeping a QEventLoopLocker with each job instance. |
| |
| The variant which operates on QCoreApplication makes it possible to finish |
| asynchronously running jobs after the last gui window has been closed. This |
| can be useful for example for running a job which uploads data to a network. |
| |
| \sa QEventLoop, QCoreApplication |
| */ |
| |
| /*! |
| Creates an event locker operating on the QCoreApplication. |
| |
| The application will quit when there are no more QEventLoopLockers operating on it. |
| |
| \sa QCoreApplication::quit(), QCoreApplication::isQuitLockEnabled() |
| */ |
| QEventLoopLocker::QEventLoopLocker() |
| : d_ptr(new QEventLoopLockerPrivate(static_cast<QCoreApplicationPrivate*>(QObjectPrivate::get(QCoreApplication::instance())))) |
| { |
| |
| } |
| |
| /*! |
| Creates an event locker operating on the \a loop. |
| |
| This particular QEventLoop will quit when there are no more QEventLoopLockers operating on it. |
| |
| \sa QEventLoop::quit() |
| */ |
| QEventLoopLocker::QEventLoopLocker(QEventLoop *loop) |
| : d_ptr(new QEventLoopLockerPrivate(static_cast<QEventLoopPrivate*>(QObjectPrivate::get(loop)))) |
| { |
| |
| } |
| |
| /*! |
| Creates an event locker operating on the \a thread. |
| |
| This particular QThread will quit when there are no more QEventLoopLockers operating on it. |
| |
| \sa QThread::quit() |
| */ |
| QEventLoopLocker::QEventLoopLocker(QThread *thread) |
| : d_ptr(new QEventLoopLockerPrivate(static_cast<QThreadPrivate*>(QObjectPrivate::get(thread)))) |
| { |
| |
| } |
| |
| /*! |
| Destroys this event loop locker object |
| */ |
| QEventLoopLocker::~QEventLoopLocker() |
| { |
| delete d_ptr; |
| } |
| |
| QT_END_NAMESPACE |
| |
| #include "moc_qeventloop.cpp" |