From 9df167a013b6db0957e35d3924e331a145d518b2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Morten=20S=C3=B8rvig?= Date: Thu, 19 Dec 2024 15:05:56 +0100 Subject: [PATCH] wasm: introduce qwasmglobal_p.h Global helpers/utils for Q_OS_WASM. Move the thread proxying functions to qwasmglobal_p.h, and update calling code. qtstweb_p.h now contains C++ wrappers for native web API only. The proxying functions will be removed from qeventdispatcher_wasm in a later commit. Change-Id: I08a56c211b08929750970895b63ca48d4a07a0a1 Reviewed-by: Lorn Potter --- src/corelib/CMakeLists.txt | 1 + src/corelib/io/qsettings_wasm.cpp | 13 ++-- src/corelib/platform/wasm/qstdweb.cpp | 2 + src/corelib/platform/wasm/qstdweb_p.h | 42 ------------ src/corelib/platform/wasm/qwasmglobal.cpp | 56 ++++++++++++++++ src/corelib/platform/wasm/qwasmglobal_p.h | 69 ++++++++++++++++++++ src/corelib/platform/wasm/qwasmsocket.cpp | 13 ++-- src/network/access/qnetworkreplywasmimpl.cpp | 4 +- 8 files changed, 144 insertions(+), 56 deletions(-) create mode 100644 src/corelib/platform/wasm/qwasmglobal.cpp create mode 100644 src/corelib/platform/wasm/qwasmglobal_p.h diff --git a/src/corelib/CMakeLists.txt b/src/corelib/CMakeLists.txt index 8d0e60af9b9..68745591d2c 100644 --- a/src/corelib/CMakeLists.txt +++ b/src/corelib/CMakeLists.txt @@ -1440,6 +1440,7 @@ endif() qt_internal_extend_target(Core CONDITION WASM SOURCES + platform/wasm/qwasmglobal.cpp platform/wasm/qwasmglobal_p.h platform/wasm/qstdweb.cpp platform/wasm/qstdweb_p.h platform/wasm/qwasmsocket.cpp platform/wasm/qwasmsocket_p.h platform/wasm/qwasmsuspendresumecontrol.cpp platform/wasm/qwasmsuspendresumecontrol_p.h diff --git a/src/corelib/io/qsettings_wasm.cpp b/src/corelib/io/qsettings_wasm.cpp index 7d80ff82d37..72045a2d519 100644 --- a/src/corelib/io/qsettings_wasm.cpp +++ b/src/corelib/io/qsettings_wasm.cpp @@ -11,6 +11,7 @@ #endif // QT_NO_QOBJECT #include #include +#include #include #include @@ -109,7 +110,7 @@ void QWasmLocalStorageSettingsPrivate::remove(const QString &key) { const std::string removed = QString(m_keyPrefixes.first() + key).toStdString(); - qstdweb::runTaskOnMainThread([this, &removed, &key]() { + qwasmglobal::runTaskOnMainThread([this, &removed, &key]() { std::vector children = { removed }; const int length = val::global("window")["localStorage"]["length"].as(); for (int i = 0; i < length; ++i) { @@ -131,7 +132,7 @@ void QWasmLocalStorageSettingsPrivate::remove(const QString &key) void QWasmLocalStorageSettingsPrivate::set(const QString &key, const QVariant &value) { - qstdweb::runTaskOnMainThread([this, &key, &value]() { + qwasmglobal::runTaskOnMainThread([this, &key, &value]() { const std::string keyString = QString(m_keyPrefixes.first() + key).toStdString(); const std::string valueString = QSettingsPrivate::variantToString(value).toStdString(); val::global("window")["localStorage"].call("setItem", keyString, valueString); @@ -140,7 +141,7 @@ void QWasmLocalStorageSettingsPrivate::set(const QString &key, const QVariant &v std::optional QWasmLocalStorageSettingsPrivate::get(const QString &key) const { - return qstdweb::runTaskOnMainThread>( + return qwasmglobal::runTaskOnMainThread>( [this, &key]() -> std::optional { for (const auto &prefix : m_keyPrefixes) { const std::string keyString = QString(prefix + key).toStdString(); @@ -160,7 +161,7 @@ std::optional QWasmLocalStorageSettingsPrivate::get(const QString &key QStringList QWasmLocalStorageSettingsPrivate::children(const QString &prefix, ChildSpec spec) const { - return qstdweb::runTaskOnMainThread([this, &prefix, &spec]() -> QStringList { + return qwasmglobal::runTaskOnMainThread([this, &prefix, &spec]() -> QStringList { QSet nodes; // Loop through all keys on window.localStorage, return Qt keys belonging to // this application, with the correct prefix, and according to ChildSpec. @@ -192,7 +193,7 @@ QStringList QWasmLocalStorageSettingsPrivate::children(const QString &prefix, Ch void QWasmLocalStorageSettingsPrivate::clear() { - qstdweb::runTaskOnMainThread([this]() { + qwasmglobal::runTaskOnMainThread([this]() { // Get all Qt keys from window.localStorage const int length = val::global("window")["localStorage"]["length"].as(); QStringList keys; @@ -347,7 +348,7 @@ QSettingsPrivate *QSettingsPrivate::create(QSettings::Format format, QSettings:: // Check if cookies are enabled (required for using persistent storage) - const bool cookiesEnabled = qstdweb::runTaskOnMainThread( + const bool cookiesEnabled = qwasmglobal::runTaskOnMainThread( []() { return val::global("navigator")["cookieEnabled"].as(); }); constexpr QLatin1StringView cookiesWarningMessage( diff --git a/src/corelib/platform/wasm/qstdweb.cpp b/src/corelib/platform/wasm/qstdweb.cpp index c51c7e827c2..970e717212d 100644 --- a/src/corelib/platform/wasm/qstdweb.cpp +++ b/src/corelib/platform/wasm/qstdweb.cpp @@ -2,6 +2,8 @@ // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #include "qstdweb_p.h" +#include "qwasmsuspendresumecontrol_p.h" +#include "qwasmglobal_p.h" #include #include diff --git a/src/corelib/platform/wasm/qstdweb_p.h b/src/corelib/platform/wasm/qstdweb_p.h index 5cb5db1f549..d34b86bce73 100644 --- a/src/corelib/platform/wasm/qstdweb_p.h +++ b/src/corelib/platform/wasm/qstdweb_p.h @@ -290,48 +290,6 @@ namespace qstdweb { bool Q_CORE_EXPORT haveAsyncify(); bool Q_CORE_EXPORT haveJspi(); bool canBlockCallingThread(); - - struct CancellationFlag - { - }; - -#if QT_CONFIG(thread) - template - T proxyCall(std::function task, emscripten::ProxyingQueue *queue) - { - T result; - queue->proxySync(emscripten_main_runtime_thread_id(), - [task, result = &result]() { *result = task(); }); - return result; - } - - template<> - inline void proxyCall(std::function task, emscripten::ProxyingQueue *queue) - { - queue->proxySync(emscripten_main_runtime_thread_id(), task); - } - - template - T runTaskOnMainThread(std::function task, emscripten::ProxyingQueue *queue) - { - return emscripten_is_main_runtime_thread() ? task() : proxyCall(std::move(task), queue); - } - - template - T runTaskOnMainThread(std::function task) - { - emscripten::ProxyingQueue singleUseQueue; - return runTaskOnMainThread(task, &singleUseQueue); - } - -#else - template - T runTaskOnMainThread(std::function task) - { - return task(); - } -#endif // QT_CONFIG(thread) - } QT_END_NAMESPACE diff --git a/src/corelib/platform/wasm/qwasmglobal.cpp b/src/corelib/platform/wasm/qwasmglobal.cpp new file mode 100644 index 00000000000..f80db160f08 --- /dev/null +++ b/src/corelib/platform/wasm/qwasmglobal.cpp @@ -0,0 +1,56 @@ +// Copyright (C) 2024 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only + +#include "qwasmglobal_p.h" + +namespace qwasmglobal { + +Q_GLOBAL_STATIC(emscripten::ProxyingQueue, proxyingQueue); + +namespace { + void trampoline(void *context) { + + auto async_fn = [](void *context){ + std::function *fn = reinterpret_cast *>(context); + (*fn)(); + delete fn; + }; + + emscripten_async_call(async_fn, context, 0); + } +} + +void runOnMainThread(std::function fn) +{ +#if QT_CONFIG(thread) + runTaskOnMainThread(fn, proxyingQueue()); +#else + runTaskOnMainThread(fn); +#endif +} + +// Runs a function asynchronously. Main thread only. +void runAsync(std::function fn) +{ + Q_ASSERT(emscripten_is_main_runtime_thread()); + trampoline(new std::function(fn)); +} + +// Runs a function on the main thread. The function always runs asynchronously, +// also if the calling thread is the main thread. +void runOnMainThreadAsync(std::function fn) +{ + void *context = new std::function(fn); +#if QT_CONFIG(thread) + if (!emscripten_is_main_runtime_thread()) { + proxyingQueue()->proxyAsync(emscripten_main_runtime_thread_id(), [context]{ + trampoline(context); + }); + return; + } +#endif + trampoline(context); +} + +} + diff --git a/src/corelib/platform/wasm/qwasmglobal_p.h b/src/corelib/platform/wasm/qwasmglobal_p.h new file mode 100644 index 00000000000..3d80014268d --- /dev/null +++ b/src/corelib/platform/wasm/qwasmglobal_p.h @@ -0,0 +1,69 @@ +// Copyright (C) 2024 The Qt Company Ltd. +// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only + +#ifndef QWASMGLOBAL_P_H +#define QWASMGLOBAL_P_H + +#include +#include + +// +// 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. +// + +QT_BEGIN_NAMESPACE + +namespace qwasmglobal { + +#if QT_CONFIG(thread) + template + T proxyCall(std::function task, emscripten::ProxyingQueue *queue) + { + T result; + queue->proxySync(emscripten_main_runtime_thread_id(), + [task, result = &result]() { *result = task(); }); + return result; + } + + template<> + inline void proxyCall(std::function task, emscripten::ProxyingQueue *queue) + { + queue->proxySync(emscripten_main_runtime_thread_id(), task); + } + + template + T runTaskOnMainThread(std::function task, emscripten::ProxyingQueue *queue) + { + return emscripten_is_main_runtime_thread() ? task() : proxyCall(std::move(task), queue); + } + + template + T runTaskOnMainThread(std::function task) + { + emscripten::ProxyingQueue singleUseQueue; + return runTaskOnMainThread(task, &singleUseQueue); + } + +#else + template + T runTaskOnMainThread(std::function task) + { + return task(); + } +#endif // QT_CONFIG(thread) + + void runAsync(std::function fn); + void runOnMainThread(std::function fn); + void runOnMainThreadAsync(std::function fn); +} + +QT_END_NAMESPACE + +#endif // QWASMGLOBAL_P_H diff --git a/src/corelib/platform/wasm/qwasmsocket.cpp b/src/corelib/platform/wasm/qwasmsocket.cpp index e7901a54fb9..64af09e9871 100644 --- a/src/corelib/platform/wasm/qwasmsocket.cpp +++ b/src/corelib/platform/wasm/qwasmsocket.cpp @@ -2,6 +2,7 @@ // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #include "qwasmsocket_p.h" +#include "qwasmglobal_p.h" #include #include "emscripten.h" @@ -28,7 +29,7 @@ void QWasmSocket::registerSocketNotifier(QSocketNotifier *notifier) bool wasEmpty = g_socketNotifiers.empty(); g_socketNotifiers.insert({notifier->socket(), notifier}); if (wasEmpty) - QEventDispatcherWasm::runOnMainThread([] { setEmscriptenSocketCallbacks(); }); + qwasmglobal::runOnMainThread([] { setEmscriptenSocketCallbacks(); }); int count; ioctl(notifier->socket(), FIONREAD, &count); @@ -52,7 +53,7 @@ void QWasmSocket::unregisterSocketNotifier(QSocketNotifier *notifier) } if (g_socketNotifiers.empty()) - QEventDispatcherWasm::runOnMainThread([] { clearEmscriptenSocketCallbacks(); }); + qwasmglobal::runOnMainThread([] { clearEmscriptenSocketCallbacks(); }); } void QWasmSocket::clearSocketNotifiers() @@ -104,7 +105,7 @@ void QWasmSocket::socketError(int socket, int err, const char* msg, void *contex // the Qt handler. // It is currently unclear if this problem is caused by code in Qt or in Emscripten, or // if this completely fixes the problem. - QEventDispatcherWasm::runAsync([socket](){ + qwasmglobal::runAsync([socket](){ auto notifiersRange = g_socketNotifiers.equal_range(socket); std::vector> notifiers(notifiersRange.first, notifiersRange.second); for (auto [_, notifier]: notifiers) { @@ -118,7 +119,7 @@ void QWasmSocket::socketOpen(int socket, void *context) { Q_UNUSED(context); - QEventDispatcherWasm::runAsync([socket](){ + qwasmglobal::runAsync([socket](){ auto notifiersRange = g_socketNotifiers.equal_range(socket); std::vector> notifiers(notifiersRange.first, notifiersRange.second); for (auto [_, notifier]: notifiers) { @@ -146,7 +147,7 @@ void QWasmSocket::socketMessage(int socket, void *context) { Q_UNUSED(context); - QEventDispatcherWasm::runAsync([socket](){ + qwasmglobal::runAsync([socket](){ auto notifiersRange = g_socketNotifiers.equal_range(socket); std::vector> notifiers(notifiersRange.first, notifiersRange.second); for (auto [_, notifier]: notifiers) { @@ -167,7 +168,7 @@ void QWasmSocket::socketClose(int socket, void *context) if (socket == 0) return; - QEventDispatcherWasm::runAsync([socket](){ + qwasmglobal::runAsync([socket](){ auto notifiersRange = g_socketNotifiers.equal_range(socket); std::vector> notifiers(notifiersRange.first, notifiersRange.second); for (auto [_, notifier]: notifiers) diff --git a/src/network/access/qnetworkreplywasmimpl.cpp b/src/network/access/qnetworkreplywasmimpl.cpp index 7d225c7e862..1bcf32e643a 100644 --- a/src/network/access/qnetworkreplywasmimpl.cpp +++ b/src/network/access/qnetworkreplywasmimpl.cpp @@ -9,7 +9,7 @@ #include #include #include -#include +#include #include #include @@ -281,7 +281,7 @@ void QNetworkReplyWasmImplPrivate::doSendRequest() } } - QEventDispatcherWasm::runOnMainThread([attr, fetchContext = m_fetchContext]() mutable { + qwasmglobal::runOnMainThread([attr, fetchContext = m_fetchContext]() mutable { std::unique_lock lock{ fetchContext->mutex }; if (fetchContext->state == FetchContext::State::CANCELED) { fetchContext->state = FetchContext::State::FINISHED;