Adapt WASM event dispatcher to use JSPI, if available

Detect if JSPI is available and suspend the execution of the program
if so, instead of using 'bare' asyncify.

For now:
1) This works only with emscripten 3.1.36 with mboc-qt patches from
emscripten repo
2) Apps have to specify the following linker options:
-sDYNCALLS=1
-sASYNCIFY=2
-sASYNCIFY_EXPORTS=dynCall_*

Fixes: QTBUG-113570
Change-Id: Ide7c51e36990df7e20c6c9b5a218366cb0db100e
Reviewed-by: Morten Johan Sørvig <morten.sorvig@qt.io>
This commit is contained in:
Mikolaj Boc 2023-05-12 15:16:42 +02:00
parent 97f68cd306
commit 05c3342b43
3 changed files with 69 additions and 8 deletions

View File

@ -42,6 +42,47 @@ static bool useAsyncify()
return qstdweb::haveAsyncify();
}
static bool useJspi()
{
return qstdweb::haveJspi();
}
EM_ASYNC_JS(void, qt_jspi_suspend_js, (), {
++Module.qtJspiSuspensionCounter;
await new Promise(resolve => {
Module.qtAsyncifyWakeUp.push(resolve);
});
});
EM_JS(bool, qt_jspi_resume_js, (), {
if (!Module.qtJspiSuspensionCounter)
return false;
--Module.qtJspiSuspensionCounter;
setTimeout(() => {
const wakeUp = (Module.qtAsyncifyWakeUp ?? []).pop();
if (wakeUp) wakeUp();
});
return true;
});
EM_JS(bool, qt_jspi_can_resume_js, (), {
return Module.qtJspiSuspensionCounter > 0;
});
EM_JS(void, init_jspi_support_js, (), {
Module.qtAsyncifyWakeUp = [];
Module.qtJspiSuspensionCounter = 0;
});
void initJspiSupport() {
init_jspi_support_js();
}
Q_CONSTRUCTOR_FUNCTION(initJspiSupport);
EM_JS(void, qt_asyncify_suspend_js, (), {
if (Module.qtSuspendId === undefined)
Module.qtSuspendId = 0;
@ -105,15 +146,15 @@ bool qt_asyncify_suspend()
// Wakes any currently suspended main thread. Returns true if the main
// thread was suspended, in which case it will now be asynchronously woken.
bool qt_asyncify_resume()
void qt_asyncify_resume()
{
if (!g_is_asyncify_suspended)
return false;
return;
g_is_asyncify_suspended = false;
qt_asyncify_resume_js();
return true;
}
Q_CONSTINIT QEventDispatcherWasm *QEventDispatcherWasm::g_mainThreadEventDispatcher = nullptr;
#if QT_CONFIG(thread)
Q_CONSTINIT QVector<QEventDispatcherWasm *> QEventDispatcherWasm::g_secondaryThreadEventDispatchers;
@ -431,10 +472,14 @@ bool QEventDispatcherWasm::wait(int timeout)
if (timeout > 0)
qWarning() << "QEventDispatcherWasm asyncify wait with timeout is not supported; timeout will be ignored"; // FIXME
bool didSuspend = qt_asyncify_suspend();
if (!didSuspend) {
qWarning("QEventDispatcherWasm: current thread is already suspended; could not asyncify wait for events");
return false;
if (useJspi()) {
qt_jspi_suspend_js();
} else {
bool didSuspend = qt_asyncify_suspend();
if (!didSuspend) {
qWarning("QEventDispatcherWasm: current thread is already suspended; could not asyncify wait for events");
return false;
}
}
return true;
} else {
@ -458,6 +503,12 @@ bool QEventDispatcherWasm::wakeEventDispatcherThread()
}
#endif
Q_ASSERT(isMainThreadEventDispatcher());
if (useJspi()) {
if (!qt_jspi_can_resume_js())
return false;
runOnMainThread([]{ qt_jspi_resume_js(); });
return true;
}
if (g_is_asyncify_suspended) {
runOnMainThread([]{ qt_asyncify_resume(); });
return true;

View File

@ -30,7 +30,7 @@ static void usePotentialyUnusedSymbols()
// called at runtime.
volatile bool doIt = false;
if (doIt)
emscripten_set_wheel_callback(NULL, 0, 0, NULL);
emscripten_set_wheel_callback("", 0, 0, NULL);
}
Q_CONSTRUCTOR_FUNCTION(usePotentialyUnusedSymbols)
@ -363,10 +363,13 @@ void WebPromiseManager::adoptPromise(emscripten::val target, PromiseCallbacks ca
#if defined(QT_STATIC)
EM_JS(bool, jsHaveAsyncify, (), { return typeof Asyncify !== "undefined"; });
EM_JS(bool, jsHaveJspi, (),
{ return !!Asyncify && !!Asyncify.makeAsyncFunction && !!WebAssembly.Function; });
#else
bool jsHaveAsyncify() { return false; }
bool jsHaveJspi() { return false; }
#endif
@ -855,6 +858,12 @@ bool haveAsyncify()
return HaveAsyncify;
}
bool haveJspi()
{
static bool HaveJspi = jsHaveJspi();
return HaveJspi;
}
std::shared_ptr<CancellationFlag>
readDataTransfer(emscripten::val webDataTransfer, std::function<QVariant(QByteArray)> imageReader,
std::function<void(std::unique_ptr<QMimeData>)> onDone)

View File

@ -203,6 +203,7 @@ namespace qstdweb {
}
bool haveAsyncify();
bool haveJspi();
struct CancellationFlag
{