Details
-
Epic
-
Resolution: Unresolved
-
P2: Important
-
None
-
None
-
None
-
Qt for WebAssembly event dispatcher update
-
Description
Intro
1. The current Qt wasm event dispatcher implementation is not completely correct in how it handles asyncify suspend/resume. (It calls into wasm code without resuming when processing events)
2. Web browsers have now started to support for asyncify 2 / JSPI ("JavaScript Promise Integration") which is has less overhead than the current Emscripten asyncify 1 feature (which is based on rewriting all functions in the wasm code).
This leads two overall dev topics:
- Implement correct suspend/resume for the wasm event dispatcher, supporting both asyncify 1 and 2 from a single implementation.
- Implement support for building for JSPI. In theory this is just setting the -s JSPI linker flag, in practice other settings such as exception types have to be kept in sync as well.
Background
Web Platform Event Handling
Qt for WebAssembly applications are event handling machines:
Events are propagated to from the browser to the wasm instance by event handlers. The instance should process the event, and then return control to the browser's event loop.
Further:
- The instance has some amount of time to process each event, before the browser flags the tab as "unresponsive" and ask the user if they want to close it.
- There is no native API for waiting for new events [like POSIX select()]; the instance must return control to the browser after processing each event.
- Returning control means unwinding the stack, which makes it impossible to implement APIs like QEventLoop::exec()
- main() is not a special function, and must also return. Emscripten supports returning from main() without terminating the instance, see the EXIT_RUNTIME and NO_EXIT_RUNTIME flags.
- Qt has a hack in place for the top-level app.exec(), which leaks the contents of the stack at that point.
Lack of support for exec()-type API is a porting obstacle for some (but not all) Qt applications, typically for widgets-based applications.
Asyncify
Emscripten Asyncify supports returning control to the browser without unwinding the stack:
The instance can suspend and return from the JS/instance entry point while keeping stack state. Later, the instance can be resumed to continue from where it left off.
This makes it possible to implement API like QEventLoop:exec(), which can suspend while waiting for new events, and then resume later on.
There are two asyncify implementations:
- Asyncify 1: Implemented as a WebAssembly byte code transform by Emscripten, supported on all platforms
- Asyncify 2 (JSPI): Implemented natively by the WebAssembly host, currently supported on Chrome (behind a flag)
From Qt's point of view there is no difference between these on the event handling implementation level. There are some ripple effects, such as choosing a compatible C++ exception handling implementation (-fexceptions vs -fwasm-exceptions).
Tasks