wasm: enable event dispatcher asyncify support

Misc. fixes, including:

Fix a couple of typos in the JavaScript code. Also, macros-
within-macros don’t work, (without resorting to preprocessor
token pasting), so remove the debug output for now.

Limit the exec() “simulateInfiniteLoop” workaround to
top-level application exec() only. This way, asyncify
can be used for nested QEventLoop::exec() calls. (Emscripten
supports one level of suspend only, so we don’t want
to use that for the top-level exec(), but instead use it
for dialogs and such).

Use the new QEventLoop::ProcessEventsFlag::ApplicationExec
enum value to detect the exec() call type.

Change-Id: Ic702bfc31faf2e9f84ac5d3ccf43d067c5c61bf0
Reviewed-by: Lorn Potter <lorn.potter@gmail.com>
This commit is contained in:
Morten Johan Sørvig 2021-09-17 11:40:14 +02:00
parent 63feecfc11
commit 6d039a5e76
3 changed files with 10 additions and 24 deletions

View File

@ -104,7 +104,7 @@ function (qt_internal_setup_wasm_target_properties wasmTarget)
set(QT_CFLAGS_OPTIMIZE_DEBUG "-Os" CACHE STRING INTERNAL FORCE) set(QT_CFLAGS_OPTIMIZE_DEBUG "-Os" CACHE STRING INTERNAL FORCE)
set(QT_FEATURE_optimize_debug ON CACHE BOOL INTERNAL FORCE) set(QT_FEATURE_optimize_debug ON CACHE BOOL INTERNAL FORCE)
target_link_options("${wasmTarget}" INTERFACE "SHELL:-s ASYNCIFY" "-Os") target_link_options("${wasmTarget}" INTERFACE "SHELL:-s ASYNCIFY" "-Os" "-s" "ASYNCIFY_IMPORTS=[qt_asyncify_suspend_js, qt_asyncify_resume_js]")
target_compile_definitions("${wasmTarget}" INTERFACE QT_HAVE_EMSCRIPTEN_ASYNCIFY) target_compile_definitions("${wasmTarget}" INTERFACE QT_HAVE_EMSCRIPTEN_ASYNCIFY)
endif() endif()
endfunction() endfunction()

View File

@ -56,13 +56,6 @@ Q_LOGGING_CATEGORY(lcEventDispatcherTimers, "qt.eventdispatcher.timers");
#ifdef QT_HAVE_EMSCRIPTEN_ASYNCIFY #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 - // Emscripten asyncify currently supports one level of suspend -
// recursion is not permitted. We track the suspend state here // recursion is not permitted. We track the suspend state here
// on order to fail (more) gracefully, but we can of course only // on order to fail (more) gracefully, but we can of course only
@ -70,28 +63,21 @@ Q_LOGGING_CATEGORY(lcEventDispatcherTimers, "qt.eventdispatcher.timers");
static bool g_is_asyncify_suspended = false; static bool g_is_asyncify_suspended = false;
EM_JS(void, qt_asyncify_suspend_js, (), { EM_JS(void, qt_asyncify_suspend_js, (), {
QT_ASYNCIFY_DEBUG("qt_asyncify_suspend_js"); let sleepFn = (wakeUp) => {
let sleepFn = (wakeUp) = > Module.qtAsyncifyWakeUp = wakeUp;
{
QT_ASYNCIFY_DEBUG("setting Module.qtAsyncifyWakeUp")
Module.qtAsyncifyWakeUp = wakeUp; // ### not "Module" any more
}; };
return Asyncify.handleSleep(sleepFn); return Asyncify.handleSleep(sleepFn);
}); });
EM_JS(void, qt_asyncify_resume_js, (), { EM_JS(void, qt_asyncify_resume_js, (), {
QT_ASYNCIFY_DEBUG("qt_asyncify_resume_js");
let wakeUp = Module.qtAsyncifyWakeUp; let wakeUp = Module.qtAsyncifyWakeUp;
if (wakeUp == = undefined) { if (wakeUp == undefined)
QT_ASYNCIFY_DEBUG("qt_asyncify_resume_js no wakeup fn set - did not wake");
return; return;
}
Module.qtAsyncifyWakeUp = undefined; Module.qtAsyncifyWakeUp = undefined;
// Delayed wakeup with zero-timer. Workaround/fix for // Delayed wakeup with zero-timer. Workaround/fix for
// https://github.com/emscripten-core/emscripten/issues/10515 // https://github.com/emscripten-core/emscripten/issues/10515
setTimeout(wakeUp); setTimeout(wakeUp);
QT_ASYNCIFY_DEBUG("qt_asyncify_resume_js done");
}); });
// Suspends the main thread until qt_asyncify_resume() is called. Returns // Suspends the main thread until qt_asyncify_resume() is called. Returns
@ -209,8 +195,8 @@ bool QEventDispatcherWasm::processEvents(QEventLoop::ProcessEventsFlags flags)
if (isMainThreadEventDispatcher()) { if (isMainThreadEventDispatcher()) {
if (flags & QEventLoop::DialogExec) if (flags & QEventLoop::DialogExec)
handleDialogExec(); handleDialogExec();
else if (flags & QEventLoop::EventLoopExec) else if (flags & QEventLoop::ApplicationExec)
handleEventLoopExec(); handleApplicationExec();
} }
if (!(flags & QEventLoop::ExcludeUserInputEvents)) if (!(flags & QEventLoop::ExcludeUserInputEvents))
@ -368,7 +354,7 @@ void QEventDispatcherWasm::wakeUp()
emscripten_async_call(&QEventDispatcherWasm::callProcessEvents, this, 0); emscripten_async_call(&QEventDispatcherWasm::callProcessEvents, this, 0);
} }
void QEventDispatcherWasm::handleEventLoopExec() void QEventDispatcherWasm::handleApplicationExec()
{ {
// Start the main loop, and then stop it on the first callback. This // Start the main loop, and then stop it on the first callback. This
// is done for the "simulateInfiniteLoop" functionality where // is done for the "simulateInfiniteLoop" functionality where
@ -386,7 +372,7 @@ void QEventDispatcherWasm::handleEventLoopExec()
void QEventDispatcherWasm::handleDialogExec() void QEventDispatcherWasm::handleDialogExec()
{ {
#if !QT_HAVE_EMSCRIPTEN_ASYNCIFY #ifndef QT_HAVE_EMSCRIPTEN_ASYNCIFY
qWarning() << "Warning: dialog exec() is not supported on Qt for WebAssembly in this" qWarning() << "Warning: dialog exec() is not supported on Qt for WebAssembly in this"
<< "configuration. Please use show() instead, or enable experimental support" << "configuration. Please use show() instead, or enable experimental support"
<< "for asyncify.\n" << "for asyncify.\n"
@ -432,7 +418,7 @@ bool QEventDispatcherWasm::waitForForEvents()
Q_ASSERT(emscripten_is_main_runtime_thread()); Q_ASSERT(emscripten_is_main_runtime_thread());
#if QT_HAVE_EMSCRIPTEN_ASYNCIFY #ifdef QT_HAVE_EMSCRIPTEN_ASYNCIFY
// We can block on the main thread using asyncify: // We can block on the main thread using asyncify:
bool didSuspend = qt_asyncify_suspend(); bool didSuspend = qt_asyncify_suspend();
if (!didSuspend) if (!didSuspend)

View File

@ -90,7 +90,7 @@ private:
bool isMainThreadEventDispatcher(); bool isMainThreadEventDispatcher();
bool isSecondaryThreadEventDispatcher(); bool isSecondaryThreadEventDispatcher();
void handleEventLoopExec(); void handleApplicationExec();
void handleDialogExec(); void handleDialogExec();
void pollForNativeEvents(); void pollForNativeEvents();
bool waitForForEvents(); bool waitForForEvents();