summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorMorten Johan Sørvig <morten.sorvig@qt.io>2019-09-09 13:04:27 +0200
committerMorten Johan Sørvig <morten.sorvig@qt.io>2021-08-27 15:45:25 +0200
commitecb92aacab2820ecbd4237f0db0fa2311fec32d2 (patch)
treee5d8dcfc21799783ef4fc0c43697208b8537faaa /src
parent82f14a95b5d2b41a47bc1ba65c2c9b880dd4accb (diff)
wasm: add new event dispatcher implementation
Add QEventDispatcherWasm to QtCore. The event dispatcher supports managing event queue wakeups and timers, both for the main thread or for secondary threads. Blocking in processEvents() (using QEventLoop::WaitForMoreEvents) is supported when running on a secondary thread, or on the main thread when Qt is built with Emscripten’s asyncify support. Code is shared for all both modes as far as possible, with breakout functions which handle main and secondary thread as well as asyncify specifics,. Some functions like wakeUp() can be called from any thread, and needs to take the calling thread into consideration as well. The current asyncify implementation in Emscripten is restricted to one level of suspend, and this restriction carries over to Qt as well. In practice this means we support one level of exec()-like API. Note that this commit does not _enable_ use of the new event dispatcher. This will be done in separate commits. Task-number: QTBUG-76007 Task-number: QTBUG-64020 Change-Id: I77dc9ba34bcff59ef05dd23a46dbf1873cbe6780 Reviewed-by: Tor Arne Vestbø <tor.arne.vestbo@qt.io>
Diffstat (limited to 'src')
-rw-r--r--src/corelib/CMakeLists.txt1
-rw-r--r--src/corelib/kernel/qeventdispatcher_wasm.cpp545
-rw-r--r--src/corelib/kernel/qeventdispatcher_wasm_p.h125
3 files changed, 671 insertions, 0 deletions
diff --git a/src/corelib/CMakeLists.txt b/src/corelib/CMakeLists.txt
index 055b191baa..9665600f94 100644
--- a/src/corelib/CMakeLists.txt
+++ b/src/corelib/CMakeLists.txt
@@ -1289,6 +1289,7 @@ endif()
qt_internal_extend_target(Core CONDITION WASM
SOURCES
platform/wasm/qstdweb.cpp platform/wasm/qstdweb_p.h
+ kernel/qeventdispatcher_wasm.cpp kernel/qeventdispatcher_wasm_p.h
)
# special case begin
diff --git a/src/corelib/kernel/qeventdispatcher_wasm.cpp b/src/corelib/kernel/qeventdispatcher_wasm.cpp
new file mode 100644
index 0000000000..14e384b98d
--- /dev/null
+++ b/src/corelib/kernel/qeventdispatcher_wasm.cpp
@@ -0,0 +1,545 @@
+/****************************************************************************
+**
+** Copyright (C) 2021 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 "qeventdispatcher_wasm_p.h"
+
+#include <QtCore/qcoreapplication.h>
+#include <QtCore/qthread.h>
+
+#include "emscripten.h"
+#include <emscripten/html5.h>
+#include <emscripten/threading.h>
+
+QT_BEGIN_NAMESPACE
+
+// using namespace emscripten;
+extern int qGlobalPostedEventsCount(); // from qapplication.cpp
+
+Q_LOGGING_CATEGORY(lcEventDispatcher, "qt.eventdispatcher");
+Q_LOGGING_CATEGORY(lcEventDispatcherTimers, "qt.eventdispatcher.timers");
+
+#ifdef QT_HAVE_EMSCRIPTEN_ASYNCIFY
+
+// Enable/disable JavaScript-side debugging
+#if 0
+ #define QT_ASYNCIFY_DEBUG(X) out(X)
+#else
+ #define QT_ASYNCIFY_DEBUG(X)
+#endif
+
+// Emscripten asyncify currently supports one level of suspend -
+// recursion is not permitted. We track the suspend state here
+// on order to fail (more) gracefully, but we can of course only
+// track Qts own usage of asyncify.
+static bool g_is_asyncify_suspended = false;
+
+EM_JS(void, qt_asyncify_suspend_js, (), {
+ QT_ASYNCIFY_DEBUG("qt_asyncify_suspend_js");
+ let sleepFn = (wakeUp) = >
+ {
+ QT_ASYNCIFY_DEBUG("setting Module.qtAsyncifyWakeUp")
+ Module.qtAsyncifyWakeUp = wakeUp; // ### not "Module" any more
+ };
+ return Asyncify.handleSleep(sleepFn);
+});
+
+EM_JS(void, qt_asyncify_resume_js, (), {
+ QT_ASYNCIFY_DEBUG("qt_asyncify_resume_js");
+ let wakeUp = Module.qtAsyncifyWakeUp;
+ if (wakeUp == = undefined) {
+ QT_ASYNCIFY_DEBUG("qt_asyncify_resume_js no wakeup fn set - did not wake");
+ return;
+ }
+ Module.qtAsyncifyWakeUp = undefined;
+
+ // Delayed wakeup with zero-timer. Workaround/fix for
+ // https://github.com/emscripten-core/emscripten/issues/10515
+ setTimeout(wakeUp);
+ QT_ASYNCIFY_DEBUG("qt_asyncify_resume_js done");
+});
+
+// Suspends the main thread until qt_asyncify_resume() is called. Returns
+// false immediately if Qt has already suspended the main thread (recursive
+// suspend is not supported by Emscripten). Returns true (after resuming),
+// if the thread was suspended.
+bool qt_asyncify_suspend()
+{
+ if (g_is_asyncify_suspended)
+ return false;
+ g_is_asyncify_suspended = true;
+ qt_asyncify_suspend_js();
+ return true;
+}
+
+// Wakes any currently suspended main thread. Returns true if the main
+// thread was suspended, in which case it will now be asynchonously woken.
+bool qt_asyncify_resume()
+{
+ if (!g_is_asyncify_suspended)
+ return false;
+ g_is_asyncify_suspended = false;
+ qt_asyncify_resume_js();
+ return true;
+}
+
+// Yields control to the browser, so that it can process events. Must
+// be called on the main thread. Returns false immediately if Qt has
+// already suspended the main thread. Returns true after yielding.
+bool qt_asyncify_yield()
+{
+ if (g_is_asyncify_suspended)
+ return false;
+ emscripten_sleep(0);
+ return true;
+}
+
+#endif // QT_HAVE_EMSCRIPTEN_ASYNCIFY
+
+QEventDispatcherWasm *QEventDispatcherWasm::g_mainThreadEventDispatcher = nullptr;
+#if QT_CONFIG(thread)
+QVector<QEventDispatcherWasm *> QEventDispatcherWasm::g_secondaryThreadEventDispatchers;
+std::mutex QEventDispatcherWasm::g_secondaryThreadEventDispatchersMutex;
+#endif
+
+QEventDispatcherWasm::QEventDispatcherWasm()
+ : QAbstractEventDispatcher()
+{
+ // QEventDispatcherWasm operates in two main modes:
+ // - On the main thread:
+ // The event dispatcher can process native events but can't
+ // block and wait for new events, unless asyncify is used.
+ // - On a secondary thread:
+ // The event dispatcher can't process native events but can
+ // block and wait for new events.
+ //
+ // Which mode is determined by the calling thread: construct
+ // the event dispatcher object on the thread where it will live.
+
+ qCDebug(lcEventDispatcher) << "Creating QEventDispatcherWasm instance" << this
+ << "is main thread" << emscripten_is_main_runtime_thread();
+
+ if (emscripten_is_main_runtime_thread()) {
+ // There can be only one main thread event dispatcher at a time; in
+ // addition the main instance is used by the secondary thread event
+ // dispatchers so we set a global pointer to it.
+ Q_ASSERT(g_mainThreadEventDispatcher == nullptr);
+ g_mainThreadEventDispatcher = this;
+ } else {
+#if QT_CONFIG(thread)
+ std::lock_guard<std::mutex> lock(g_secondaryThreadEventDispatchersMutex);
+ g_secondaryThreadEventDispatchers.append(this);
+#endif
+ }
+}
+
+QEventDispatcherWasm::~QEventDispatcherWasm()
+{
+ qCDebug(lcEventDispatcher) << "Detroying QEventDispatcherWasm instance" << this;
+
+ delete m_timerInfo;
+
+#if QT_CONFIG(thread)
+ if (isSecondaryThreadEventDispatcher()) {
+ std::lock_guard<std::mutex> lock(g_secondaryThreadEventDispatchersMutex);
+ g_secondaryThreadEventDispatchers.remove(g_secondaryThreadEventDispatchers.indexOf(this));
+ } else
+#endif
+ {
+ if (m_timerId > 0)
+ emscripten_clear_timeout(m_timerId);
+ g_mainThreadEventDispatcher = nullptr;
+ }
+}
+
+bool QEventDispatcherWasm::isMainThreadEventDispatcher()
+{
+ return this == g_mainThreadEventDispatcher;
+}
+
+bool QEventDispatcherWasm::isSecondaryThreadEventDispatcher()
+{
+ return this != g_mainThreadEventDispatcher;
+}
+
+bool QEventDispatcherWasm::processEvents(QEventLoop::ProcessEventsFlags flags)
+{
+ emit awake();
+
+ bool hasPendingEvents = qGlobalPostedEventsCount() > 0;
+
+ qCDebug(lcEventDispatcher) << "QEventDispatcherWasm::processEvents flags" << flags
+ << "pending events" << hasPendingEvents;
+
+ if (!(flags & QEventLoop::ExcludeUserInputEvents))
+ pollForNativeEvents();
+
+ hasPendingEvents = qGlobalPostedEventsCount() > 0;
+
+ if (!hasPendingEvents && (flags & QEventLoop::WaitForMoreEvents))
+ waitForForEvents();
+
+ if (m_interrupted) {
+ m_interrupted = false;
+ return false;
+ }
+
+ if (m_processTimers) {
+ m_processTimers = false;
+ processTimers();
+ }
+
+ hasPendingEvents = qGlobalPostedEventsCount() > 0;
+ QCoreApplication::sendPostedEvents();
+ return hasPendingEvents;
+}
+
+void QEventDispatcherWasm::registerSocketNotifier(QSocketNotifier *notifier)
+{
+ Q_UNUSED(notifier);
+ qWarning("QEventDispatcherWasm::registerSocketNotifier: socket notifiers are not supported");
+}
+
+void QEventDispatcherWasm::unregisterSocketNotifier(QSocketNotifier *notifier)
+{
+ Q_UNUSED(notifier);
+ qWarning("QEventDispatcherWasm::unregisterSocketNotifier: socket notifiers are not supported");
+}
+
+void QEventDispatcherWasm::registerTimer(int timerId, qint64 interval, Qt::TimerType timerType, QObject *object)
+{
+#ifndef QT_NO_DEBUG
+ if (timerId < 1 || interval < 0 || !object) {
+ qWarning("QEventDispatcherWasm::registerTimer: invalid arguments");
+ return;
+ } else if (object->thread() != thread() || thread() != QThread::currentThread()) {
+ qWarning("QEventDispatcherWasm::registerTimer: timers cannot be started from another "
+ "thread");
+ return;
+ }
+#endif
+ qCDebug(lcEventDispatcherTimers) << "registerTimer" << timerId << interval << timerType << object;
+
+ m_timerInfo->registerTimer(timerId, interval, timerType, object);
+ updateNativeTimer();
+}
+
+bool QEventDispatcherWasm::unregisterTimer(int timerId)
+{
+#ifndef QT_NO_DEBUG
+ if (timerId < 1) {
+ qWarning("QEventDispatcherWasm::unregisterTimer: invalid argument");
+ return false;
+ } else if (thread() != QThread::currentThread()) {
+ qWarning("QEventDispatcherWasm::unregisterTimer: timers cannot be stopped from another "
+ "thread");
+ return false;
+ }
+#endif
+
+ qCDebug(lcEventDispatcherTimers) << "unregisterTimer" << timerId;
+
+ bool ans = m_timerInfo->unregisterTimer(timerId);
+ updateNativeTimer();
+ return ans;
+}
+
+bool QEventDispatcherWasm::unregisterTimers(QObject *object)
+{
+#ifndef QT_NO_DEBUG
+ if (!object) {
+ qWarning("QEventDispatcherWasm::unregisterTimers: invalid argument");
+ return false;
+ } else if (object->thread() != thread() || thread() != QThread::currentThread()) {
+ qWarning("QEventDispatcherWasm::unregisterTimers: timers cannot be stopped from another "
+ "thread");
+ return false;
+ }
+#endif
+
+ qCDebug(lcEventDispatcherTimers) << "registerTimer" << object;
+
+ bool ans = m_timerInfo->unregisterTimers(object);
+ updateNativeTimer();
+ return ans;
+}
+
+QList<QAbstractEventDispatcher::TimerInfo>
+QEventDispatcherWasm::registeredTimers(QObject *object) const
+{
+#ifndef QT_NO_DEBUG
+ if (!object) {
+ qWarning("QEventDispatcherWasm:registeredTimers: invalid argument");
+ return QList<TimerInfo>();
+ }
+#endif
+
+ return m_timerInfo->registeredTimers(object);
+}
+
+int QEventDispatcherWasm::remainingTime(int timerId)
+{
+ return m_timerInfo->timerRemainingTime(timerId);
+}
+
+void QEventDispatcherWasm::interrupt()
+{
+ m_interrupted = true;
+ wakeUp();
+}
+
+void QEventDispatcherWasm::wakeUp()
+{
+#if QT_CONFIG(thread)
+ if (isSecondaryThreadEventDispatcher()) {
+ std::lock_guard<std::mutex> lock(m_mutex);
+ m_wakeUpCalled = true;
+ m_moreEvents.notify_one();
+ return;
+ }
+#endif
+
+#ifdef QT_HAVE_EMSCRIPTEN_ASYNCIFY
+ // The main thread may be asyncify-blocked in processEvents(). If so resume it.
+ if (qt_asyncify_resume()) // ### safe to call from secondary thread?
+ return;
+#endif
+
+ {
+#if QT_CONFIG(thread)
+ // This function can be called from any thread (via wakeUp()),
+ // so we need to lock access to m_pendingProcessEvents.
+ std::lock_guard<std::mutex> lock(m_mutex);
+#endif
+ if (m_pendingProcessEvents)
+ return;
+ m_pendingProcessEvents = true;
+ }
+
+#if QT_CONFIG(thread)
+ if (!emscripten_is_main_runtime_thread()) {
+ runOnMainThread([this](){
+ QEventDispatcherWasm::callProcessEvents(this);
+ });
+ } else
+#endif
+ emscripten_async_call(&QEventDispatcherWasm::callProcessEvents, this, 0);
+}
+
+void QEventDispatcherWasm::pollForNativeEvents()
+{
+ // Secondary thread event dispatchers do not support native events
+ if (isSecondaryThreadEventDispatcher())
+ return;
+
+#if HAVE_EMSCRIPTEN_ASYNCIFY
+ // Asyncify allows us to yield to the browser and have it process native events -
+ // but this will fail if we are recursing and are already in a yield.
+ bool didYield = qt_asyncify_yield();
+ if (!didYield)
+ qWarning("QEventDispatcherWasm::processEvents() did not asyncify process native events");
+#endif
+}
+
+// Waits for more events. This is possible in two cases:
+// - On a secondary thread
+// - On the main thread iff asyncify is used
+// Returns true if waiting was possible (at which point it
+// has already happened).
+bool QEventDispatcherWasm::waitForForEvents()
+{
+#if QT_CONFIG(thread)
+ if (isSecondaryThreadEventDispatcher()) {
+ std::unique_lock<std::mutex> lock(m_mutex);
+ m_moreEvents.wait(lock, [=] { return m_wakeUpCalled; });
+ m_wakeUpCalled = false;
+ return true;
+ }
+#endif
+
+ Q_ASSERT(emscripten_is_main_runtime_thread());
+
+#if QT_HAVE_EMSCRIPTEN_ASYNCIFY
+ // We can block on the main thread using asyncify:
+ bool didSuspend = qt_asyncify_suspend();
+ if (!didSuspend)
+ qWarning("QEventDispatcherWasm: current thread is already suspended; could not asyncify wait for events");
+ return didSuspend;
+#else
+ qWarning("QEventLoop::WaitForMoreEvents is not supported on the main thread without asyncify");
+ return false;
+#endif
+}
+
+// Process event activation callbacks for the main thread event dispatcher.
+// Must be called on the main thread.
+void QEventDispatcherWasm::callProcessEvents(void *context)
+{
+ Q_ASSERT(emscripten_is_main_runtime_thread());
+
+ // Bail out if Qt has been shut down.
+ if (!g_mainThreadEventDispatcher)
+ return;
+
+ // In the unlikely event that we get a callProcessEvents() call for
+ // a previous main thread event dispatcher (i.e. the QApplication
+ // object was deleted and crated again): just ignore it and return.
+ if (context != g_mainThreadEventDispatcher)
+ return;
+
+ {
+#if QT_CONFIG(thread)
+ std::lock_guard<std::mutex> lock(g_mainThreadEventDispatcher->m_mutex);
+#endif
+ g_mainThreadEventDispatcher->m_pendingProcessEvents = false;
+ }
+ g_mainThreadEventDispatcher->processEvents(QEventLoop::AllEvents);
+}
+
+void QEventDispatcherWasm::processTimers()
+{
+ m_timerInfo->activateTimers();
+ updateNativeTimer(); // schedule next native timer, if any
+}
+
+// Updates the native timer based on currently registered Qt timers.
+// Must be called on the event dispatcher thread.
+void QEventDispatcherWasm::updateNativeTimer()
+{
+#if QT_CONFIG(thread)
+ Q_ASSERT(QThread::currentThread() == thread());
+#endif
+
+ // Multiplex Qt timers down to a single native timer, maintained
+ // to have a timeout corresponding to the shortest Qt timer. This
+ // is done in two steps: first determine the target wakeup time
+ // on the event dispatcher thread (since this thread has exclusive
+ // access to m_timerInfo), and then call native API to set the new
+ // wakeup time on the main thread.
+
+ auto timespecToNanosec = [](timespec ts) -> uint64_t {
+ return ts.tv_sec * 1000 + ts.tv_nsec / (1000 * 1000);
+ };
+ timespec toWait;
+ m_timerInfo->timerWait(toWait);
+ uint64_t currentTime = timespecToNanosec(m_timerInfo->currentTime);
+ uint64_t toWaitDuration = timespecToNanosec(toWait);
+ uint64_t newTargetTime = currentTime + toWaitDuration;
+
+ auto maintainNativeTimer = [this, toWaitDuration, newTargetTime]() {
+ Q_ASSERT(emscripten_is_main_runtime_thread());
+
+ if (m_timerTargetTime != 0 && newTargetTime >= m_timerTargetTime)
+ return; // existing timer is good
+
+ qCDebug(lcEventDispatcherTimers)
+ << "Created new native timer with wait" << toWaitDuration << "timeout" << newTargetTime;
+ emscripten_clear_timeout(m_timerId);
+ m_timerId = emscripten_set_timeout(&QEventDispatcherWasm::callProcessTimers, toWaitDuration, this);
+ m_timerTargetTime = newTargetTime;
+ };
+
+ // Update the native timer for this thread/dispatcher. This must be
+ // done on the main thread where we have access to native API.
+
+#if QT_CONFIG(thread)
+ if (isSecondaryThreadEventDispatcher()) {
+ runOnMainThread([this, maintainNativeTimer]() {
+ Q_ASSERT(emscripten_is_main_runtime_thread());
+
+ // "this" may have been deleted, or may be about to be deleted.
+ // Check if the pointer we have is still a valid event dispatcher,
+ // and keep the mutex locked while updating the native timer to
+ // prevent it from being deleted.
+ std::lock_guard<std::mutex> lock(g_secondaryThreadEventDispatchersMutex);
+ if (g_secondaryThreadEventDispatchers.contains(this))
+ maintainNativeTimer();
+ });
+ } else
+#endif
+ maintainNativeTimer();
+}
+
+// Static timer activation callback. Must be called on the main thread
+// and will then either process timers on the main thrad or wake and
+// process timers on a secondary thread.
+void QEventDispatcherWasm::callProcessTimers(void *context)
+{
+ Q_ASSERT(emscripten_is_main_runtime_thread());
+
+ // Bail out if Qt has been shut down
+ if (!g_mainThreadEventDispatcher)
+ return;
+
+ // Note: "context" may be a stale pointer here,
+ // take care before casting and dereferencing!
+
+ // Process timers on this thread if this is the main event dispatcher
+ if (reinterpret_cast<QEventDispatcherWasm *>(context) == g_mainThreadEventDispatcher) {
+ g_mainThreadEventDispatcher->m_timerTargetTime = 0;
+ g_mainThreadEventDispatcher->processTimers();
+ return;
+ }
+
+ // Wake and process timers on the secondary thread if this a secondary thread dispatcher
+#if QT_CONFIG(thread)
+ std::lock_guard<std::mutex> lock(g_secondaryThreadEventDispatchersMutex);
+ if (g_secondaryThreadEventDispatchers.contains(context)) {
+ QEventDispatcherWasm *eventDispatcher = reinterpret_cast<QEventDispatcherWasm *>(context);
+ eventDispatcher->m_timerTargetTime = 0;
+ eventDispatcher->m_processTimers = true;
+ eventDispatcher->wakeUp();
+ }
+#endif
+}
+
+#if QT_CONFIG(thread)
+// Runs a function on the main thread
+void QEventDispatcherWasm::runOnMainThread(std::function<void(void)> fn)
+{
+ static auto trampoline = [](void *context) {
+ std::function<void(void)> *fn = reinterpret_cast<std::function<void(void)> *>(context);
+ (*fn)();
+ delete fn;
+ };
+ void *context = new std::function<void(void)>(fn);
+ emscripten_async_run_in_main_runtime_thread_(EM_FUNC_SIG_VI, reinterpret_cast<void *>(&trampoline), context);
+}
+#endif
+
+QT_END_NAMESPACE
diff --git a/src/corelib/kernel/qeventdispatcher_wasm_p.h b/src/corelib/kernel/qeventdispatcher_wasm_p.h
new file mode 100644
index 0000000000..2fcd512773
--- /dev/null
+++ b/src/corelib/kernel/qeventdispatcher_wasm_p.h
@@ -0,0 +1,125 @@
+/****************************************************************************
+**
+** Copyright (C) 2021 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$
+**
+****************************************************************************/
+
+#ifndef QEVENTDISPATCHER_WASM_P_H
+#define QEVENTDISPATCHER_WASM_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include "qabstracteventdispatcher.h"
+#include "private/qtimerinfo_unix_p.h"
+#include <QtCore/qloggingcategory.h>
+#include <QtCore/qwaitcondition.h>
+
+#include <mutex>
+#include <optional>
+#include <tuple>
+
+QT_BEGIN_NAMESPACE
+
+Q_DECLARE_LOGGING_CATEGORY(lcEventDispatcher);
+Q_DECLARE_LOGGING_CATEGORY(lcEventDispatcherTimers)
+
+class Q_CORE_EXPORT QEventDispatcherWasm : public QAbstractEventDispatcher
+{
+ Q_OBJECT
+public:
+ QEventDispatcherWasm();
+ ~QEventDispatcherWasm();
+
+ bool processEvents(QEventLoop::ProcessEventsFlags flags) override;
+
+ void registerSocketNotifier(QSocketNotifier *notifier) override;
+ void unregisterSocketNotifier(QSocketNotifier *notifier) override;
+
+ void registerTimer(int timerId, qint64 interval, Qt::TimerType timerType, QObject *object) override;
+ bool unregisterTimer(int timerId) override;
+ bool unregisterTimers(QObject *object) override;
+ QList<QAbstractEventDispatcher::TimerInfo> registeredTimers(QObject *object) const override;
+ int remainingTime(int timerId) override;
+
+ void interrupt() override;
+ void wakeUp() override;
+
+private:
+ bool isMainThreadEventDispatcher();
+ bool isSecondaryThreadEventDispatcher();
+
+ void pollForNativeEvents();
+ bool waitForForEvents();
+ static void callProcessEvents(void *eventDispatcher);
+
+ void processTimers();
+ void updateNativeTimer();
+ static void callProcessTimers(void *eventDispatcher);
+
+#if QT_CONFIG(thread)
+ void runOnMainThread(std::function<void(void)> fn);
+#endif
+
+ static QEventDispatcherWasm *g_mainThreadEventDispatcher;
+
+ bool m_interrupted = false;
+ bool m_processTimers = false;
+ bool m_pendingProcessEvents = false;
+
+ QTimerInfoList *m_timerInfo = new QTimerInfoList();
+ long m_timerId = 0;
+ uint64_t m_timerTargetTime = 0;
+
+#if QT_CONFIG(thread)
+ std::mutex m_mutex;
+ bool m_wakeUpCalled = false;
+ std::condition_variable m_moreEvents;
+
+ static QVector<QEventDispatcherWasm *> g_secondaryThreadEventDispatchers;
+ static std::mutex g_secondaryThreadEventDispatchersMutex;
+#endif
+};
+
+#endif // QEVENTDISPATCHER_WASM_P_H