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 <lorn.potter@qt.io>
This commit is contained in:
Morten Sørvig 2024-12-19 15:05:56 +01:00
parent b80deb2628
commit 9df167a013
8 changed files with 144 additions and 56 deletions

View File

@ -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

View File

@ -11,6 +11,7 @@
#endif // QT_NO_QOBJECT
#include <QDebug>
#include <QtCore/private/qstdweb_p.h>
#include <QtCore/private/qwasmglobal_p.h>
#include <QFileInfo>
#include <QDir>
@ -109,7 +110,7 @@ void QWasmLocalStorageSettingsPrivate::remove(const QString &key)
{
const std::string removed = QString(m_keyPrefixes.first() + key).toStdString();
qstdweb::runTaskOnMainThread<void>([this, &removed, &key]() {
qwasmglobal::runTaskOnMainThread<void>([this, &removed, &key]() {
std::vector<std::string> children = { removed };
const int length = val::global("window")["localStorage"]["length"].as<int>();
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<void>([this, &key, &value]() {
qwasmglobal::runTaskOnMainThread<void>([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<void>("setItem", keyString, valueString);
@ -140,7 +141,7 @@ void QWasmLocalStorageSettingsPrivate::set(const QString &key, const QVariant &v
std::optional<QVariant> QWasmLocalStorageSettingsPrivate::get(const QString &key) const
{
return qstdweb::runTaskOnMainThread<std::optional<QVariant>>(
return qwasmglobal::runTaskOnMainThread<std::optional<QVariant>>(
[this, &key]() -> std::optional<QVariant> {
for (const auto &prefix : m_keyPrefixes) {
const std::string keyString = QString(prefix + key).toStdString();
@ -160,7 +161,7 @@ std::optional<QVariant> QWasmLocalStorageSettingsPrivate::get(const QString &key
QStringList QWasmLocalStorageSettingsPrivate::children(const QString &prefix, ChildSpec spec) const
{
return qstdweb::runTaskOnMainThread<QStringList>([this, &prefix, &spec]() -> QStringList {
return qwasmglobal::runTaskOnMainThread<QStringList>([this, &prefix, &spec]() -> QStringList {
QSet<QString> 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<void>([this]() {
qwasmglobal::runTaskOnMainThread<void>([this]() {
// Get all Qt keys from window.localStorage
const int length = val::global("window")["localStorage"]["length"].as<int>();
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<bool>(
const bool cookiesEnabled = qwasmglobal::runTaskOnMainThread<bool>(
[]() { return val::global("navigator")["cookieEnabled"].as<bool>(); });
constexpr QLatin1StringView cookiesWarningMessage(

View File

@ -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 <QtCore/qcoreapplication.h>
#include <QtCore/qfile.h>

View File

@ -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<class T>
T proxyCall(std::function<T()> task, emscripten::ProxyingQueue *queue)
{
T result;
queue->proxySync(emscripten_main_runtime_thread_id(),
[task, result = &result]() { *result = task(); });
return result;
}
template<>
inline void proxyCall<void>(std::function<void()> task, emscripten::ProxyingQueue *queue)
{
queue->proxySync(emscripten_main_runtime_thread_id(), task);
}
template<class T>
T runTaskOnMainThread(std::function<T()> task, emscripten::ProxyingQueue *queue)
{
return emscripten_is_main_runtime_thread() ? task() : proxyCall<T>(std::move(task), queue);
}
template<class T>
T runTaskOnMainThread(std::function<T()> task)
{
emscripten::ProxyingQueue singleUseQueue;
return runTaskOnMainThread<T>(task, &singleUseQueue);
}
#else
template<class T>
T runTaskOnMainThread(std::function<T()> task)
{
return task();
}
#endif // QT_CONFIG(thread)
}
QT_END_NAMESPACE

View File

@ -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<void(void)> *fn = reinterpret_cast<std::function<void(void)> *>(context);
(*fn)();
delete fn;
};
emscripten_async_call(async_fn, context, 0);
}
}
void runOnMainThread(std::function<void(void)> fn)
{
#if QT_CONFIG(thread)
runTaskOnMainThread<void>(fn, proxyingQueue());
#else
runTaskOnMainThread<void>(fn);
#endif
}
// Runs a function asynchronously. Main thread only.
void runAsync(std::function<void(void)> fn)
{
Q_ASSERT(emscripten_is_main_runtime_thread());
trampoline(new std::function<void(void)>(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<void(void)> fn)
{
void *context = new std::function<void(void)>(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);
}
}

View File

@ -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 <emscripten/proxying.h>
#include <emscripten/threading.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.
//
QT_BEGIN_NAMESPACE
namespace qwasmglobal {
#if QT_CONFIG(thread)
template<class T>
T proxyCall(std::function<T()> task, emscripten::ProxyingQueue *queue)
{
T result;
queue->proxySync(emscripten_main_runtime_thread_id(),
[task, result = &result]() { *result = task(); });
return result;
}
template<>
inline void proxyCall<void>(std::function<void()> task, emscripten::ProxyingQueue *queue)
{
queue->proxySync(emscripten_main_runtime_thread_id(), task);
}
template<class T>
T runTaskOnMainThread(std::function<T()> task, emscripten::ProxyingQueue *queue)
{
return emscripten_is_main_runtime_thread() ? task() : proxyCall<T>(std::move(task), queue);
}
template<class T>
T runTaskOnMainThread(std::function<T()> task)
{
emscripten::ProxyingQueue singleUseQueue;
return runTaskOnMainThread<T>(task, &singleUseQueue);
}
#else
template<class T>
T runTaskOnMainThread(std::function<T()> task)
{
return task();
}
#endif // QT_CONFIG(thread)
void runAsync(std::function<void(void)> fn);
void runOnMainThread(std::function<void(void)> fn);
void runOnMainThreadAsync(std::function<void(void)> fn);
}
QT_END_NAMESPACE
#endif // QWASMGLOBAL_P_H

View File

@ -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 <QtCore/qsocketnotifier.h>
#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<std::pair<int, QSocketNotifier *>> 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<std::pair<int, QSocketNotifier *>> 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<std::pair<int, QSocketNotifier *>> 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<std::pair<int, QSocketNotifier *>> notifiers(notifiersRange.first, notifiersRange.second);
for (auto [_, notifier]: notifiers)

View File

@ -9,7 +9,7 @@
#include <QtCore/qcoreapplication.h>
#include <QtCore/qfileinfo.h>
#include <QtCore/qthread.h>
#include <QtCore/private/qeventdispatcher_wasm_p.h>
#include <QtCore/private/qwasmglobal_p.h>
#include <QtCore/private/qoffsetstringarray_p.h>
#include <QtCore/private/qtools_p.h>
@ -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;