summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMorten Johan Sørvig <morten.sorvig@qt.io>2018-05-11 14:25:59 +0200
committerMorten Johan Sørvig <morten.sorvig@qt.io>2018-06-01 11:21:02 +0000
commit7b194ce3bdecc88afae8e73d9a738ca3e021b0c9 (patch)
tree87747666f95693a03d7ac0915fbefc3f787f01e2
parente0265030e500a785bd06b0dfa44e43afcfe26e50 (diff)
wasm: move event dispatcher implementation
Move the majority of the implementation from from qeventloop.cpp to qhtml5eventdispatcher.cpp. Keep the parts that handle nested event loops and app exit since they require access to QEventLoop internals. Change-Id: Ica344fdf7b3b7777985f7217e0c781580ec5e5b4 Reviewed-by: Lorn Potter <lorn.potter@gmail.com>
-rw-r--r--src/corelib/kernel/qeventloop.cpp87
-rw-r--r--src/corelib/kernel/qeventloop.h7
-rw-r--r--src/plugins/platforms/html5/qhtml5eventdispatcher.cpp62
-rw-r--r--src/plugins/platforms/html5/qhtml5eventdispatcher.h11
4 files changed, 60 insertions, 107 deletions
diff --git a/src/corelib/kernel/qeventloop.cpp b/src/corelib/kernel/qeventloop.cpp
index 14d9df801a..6e4c2552c7 100644
--- a/src/corelib/kernel/qeventloop.cpp
+++ b/src/corelib/kernel/qeventloop.cpp
@@ -49,7 +49,6 @@
#include <private/qthread_p.h>
#include <QDebug>
-
#ifdef Q_OS_HTML5
#include <emscripten.h>
#endif
@@ -176,7 +175,6 @@ int QEventLoop::exec(ProcessEventsFlags flags)
return -1;
}
-#ifndef Q_OS_HTML5
//we need to protect from race condition with QThread::exit
#ifndef QT_NO_THREAD
QMutexLocker locker(&static_cast<QThreadPrivate *>(QObjectPrivate::get(d->threadData->thread))->mutex);
@@ -220,61 +218,22 @@ int QEventLoop::exec(ProcessEventsFlags flags)
if (app && app->thread() == thread())
QCoreApplication::removePostedEvents(app, QEvent::Quit);
+#ifdef Q_OS_HTML5
+ // 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 newer returns.
+ if (d->threadData->loopLevel > 1)
+ emscripten_sleep(1);
+#endif
+
while (!d->exit.loadAcquire())
processEvents(flags | WaitForMoreEvents | EventLoopExec);
ref.exceptionCaught = false;
-#else // Q_OS_HTML5
- Q_UNUSED(flags)
- d->inExec = true;
- d->exit.storeRelease(false);
- int oldLoopLevel = d->threadData->loopLevel;
- ++d->threadData->loopLevel;
- d->threadData->eventLoops.push(d->q_func());
-
- // remove posted quit events when entering a new event loop
- QCoreApplication *app = QCoreApplication::instance();
- if (app && app->thread() == thread())
- QCoreApplication::removePostedEvents(app, QEvent::Quit);
-
- if (oldLoopLevel == 0 && d->threadData->loopLevel == 1) {
- // main loop. can be only one
- emscripten_set_main_loop_arg(QEventLoop::processEvents, (void*)this, 0, 1);
- } else {
- // child loops
- while (!d->exit.loadAcquire()) {
- emscripten_sleep(10);
- processEvents((void *)this);
- }
- }
-
- 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;
-#endif // Q_OS_HTML5
-
return d->returnCode.load();
}
-#ifdef Q_OS_HTML5
-void QEventLoop::processEvents(void *eventloop)
-{
- QEventLoop *currentEventloop = (QEventLoop*)eventloop;
- currentEventloop->processEvents_emscripten();
-}
-
-void QEventLoop::processEvents_emscripten()
-{
- Q_D(QEventLoop);
- if (!d->threadData->eventDispatcher.load())
- return;
- d->threadData->eventDispatcher.load()->processEvents(EventLoopExec);
-}
-#endif
-
/*!
Process pending events that match \a flags for a maximum of \a
maxTime milliseconds, or until there are no more events to
@@ -331,41 +290,17 @@ void QEventLoop::exit(int returnCode)
d->threadData->eventDispatcher.load()->interrupt();
#ifdef Q_OS_HTML5
-
+ // QEventLoop::exec() never returns. We implement opproximate behavior here.
if (d->threadData->loopLevel == 1) {
- emscripten_cancel_main_loop();
emscripten_force_exit(returnCode);
} else {
- cleanup();
- d->threadData->eventLoops.at(0)->switchLoop_emscripten( d->threadData->eventLoops.at(0));
+ d->inExec = false;
+ --d->threadData->loopLevel;
}
#endif
-
}
-#ifdef Q_OS_HTML5
-void QEventLoop::switchLoop_emscripten(void *userData)
-{
- emscripten_cancel_main_loop();
- emscripten_set_main_loop_arg(QEventLoop::processEvents, userData, 0, 1);
-}
-
-void QEventLoop::cleanup()
-{
- Q_D(QEventLoop);
- if (!d->threadData->eventDispatcher.load())
- return;
-
- 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;
-}
-#endif
-
-
/*!
Returns \c true if the event loop is running; otherwise returns
false. The event loop is considered running from the time when
diff --git a/src/corelib/kernel/qeventloop.h b/src/corelib/kernel/qeventloop.h
index 62b89e7d34..6f3968ed83 100644
--- a/src/corelib/kernel/qeventloop.h
+++ b/src/corelib/kernel/qeventloop.h
@@ -71,13 +71,6 @@ public:
void processEvents(ProcessEventsFlags flags, int maximumTime);
-#ifdef Q_OS_HTML5
- void processEvents_emscripten();
- static void processEvents(void *eventloop);
- bool firstRun;
- void cleanup();
- void switchLoop_emscripten(void *userData);
-#endif
int exec(ProcessEventsFlags flags = AllEvents);
void exit(int returnCode = 0);
bool isRunning() const;
diff --git a/src/plugins/platforms/html5/qhtml5eventdispatcher.cpp b/src/plugins/platforms/html5/qhtml5eventdispatcher.cpp
index defdd86914..c1aaa19dba 100644
--- a/src/plugins/platforms/html5/qhtml5eventdispatcher.cpp
+++ b/src/plugins/platforms/html5/qhtml5eventdispatcher.cpp
@@ -32,9 +32,10 @@
#include <QtCore/QDebug>
#include <QtCore/QCoreApplication>
+#include <emscripten.h>
+
QHtml5EventDispatcher::QHtml5EventDispatcher(QObject *parent)
: QUnixEventDispatcherQPA(parent)
- , m_hasPendingProcessEvents(false)
{
}
@@ -42,20 +43,49 @@ QHtml5EventDispatcher::~QHtml5EventDispatcher() {}
bool QHtml5EventDispatcher::processEvents(QEventLoop::ProcessEventsFlags flags)
{
- bool processed = false;
-
- // We need to give the control back to the browser due to lack of PTHREADS
- // Limit the number of events that may be processed at the time
- int maxProcessedEvents = 10;
- int processedCount = 0;
- do {
- processed = QUnixEventDispatcherQPA::processEvents(flags);
- processedCount += 1;
- } while (processed && hasPendingEvents() && processedCount < maxProcessedEvents);
- return true;
-}
+ // WaitForMoreEvents is not supported (except for in combination with EventLoopExec below),
+ // and we don't want the unix event dispatcher base class to attempt to wait either.
+ flags &= ~QEventLoop::WaitForMoreEvents;
-bool QHtml5EventDispatcher::hasPendingEvents()
-{
- return QUnixEventDispatcherQPA::hasPendingEvents();
+ // Handle normal processEvents.
+ if (!(flags & QEventLoop::EventLoopExec)) {
+ bool processed = false;
+
+ // We need to give the control back to the browser due to lack of PTHREADS
+ // Limit the number of events that may be processed at the time
+ int maxProcessedEvents = 10;
+ int processedCount = 0;
+ do {
+ processed = QUnixEventDispatcherQPA::processEvents(flags);
+ processedCount += 1;
+ } while (processed && hasPendingEvents() && processedCount < maxProcessedEvents);
+ return true;
+ }
+
+ // Handle processEvents from QEventLoop::exec():
+ //
+ // At this point the application has created its root objects on
+ // the stack and has called app.exec() which has called into this
+ // function via QEventLoop.
+ //
+ // The application now expects that exec() will not return until
+ // app exit time. However, the browser expects that we return
+ // control to it periodically, also after initial setup in main().
+
+ // EventLoopExec for nested event loops is not supported.
+ Q_ASSERT(!m_hasMainLoop);
+ m_hasMainLoop = true;
+
+ // Call emscripten_set_main_loop_arg() with a callback which processes
+ // events. Also set simulateInfiniteLoop to true which makes emscripten
+ // return control to the browser without unwinding the C++ stack.
+ auto callback = [](void *eventDispatcher) {
+ static_cast<QHtml5EventDispatcher *>(eventDispatcher)->processEvents(QEventLoop::AllEvents);
+ };
+ int fps = 0; // update using requestAnimationFrame
+ int simulateInfiniteLoop = 1;
+ emscripten_set_main_loop_arg(callback, this, fps, simulateInfiniteLoop);
+
+ // Note: the above call never returns, not even at app exit
+ return false;
}
diff --git a/src/plugins/platforms/html5/qhtml5eventdispatcher.h b/src/plugins/platforms/html5/qhtml5eventdispatcher.h
index 081e675595..0a7cc63a19 100644
--- a/src/plugins/platforms/html5/qhtml5eventdispatcher.h
+++ b/src/plugins/platforms/html5/qhtml5eventdispatcher.h
@@ -41,16 +41,11 @@ class QHtml5EventDispatcher : public QUnixEventDispatcherQPA
public:
explicit QHtml5EventDispatcher(QObject *parent = 0);
~QHtml5EventDispatcher();
- void processEvents_emscripten();
- static void processEvents(void *eventloop);
-protected:
-
- bool processEvents(QEventLoop::ProcessEventsFlags flags
- = QEventLoop::EventLoopExec) override;
- bool hasPendingEvents() override;
+protected:
+ bool processEvents(QEventLoop::ProcessEventsFlags flags) override;
private:
- bool m_hasPendingProcessEvents;
+ bool m_hasMainLoop = false;
};
QT_END_NAMESPACE