When multiple modal dialogs are triggered in quick succession, Asyncify operations can overlap causing crashes:
- "indirect call to null"
- "func is not a function"
- "index out of bounds"
Per Emscripten docs: "It is not safe to start an async operation while another is already running."
When the first modal completes:
- Asyncify begins rewinding the C++ stack
- C++ code triggers second modal before rewind completes
- Second modal's Asyncify operation conflicts with first modal's cleanup
- Asyncify state corruption occurs
You cannot use ANY Asyncify mechanism to wait - neither EM_ASYNC_JS await nor emscripten_sleep() - while another Asyncify operation is cleaning up. Both use Asyncify internally and cause the same conflict.
- Use a global lock to track when Asyncify is busy
- Check lock with synchronous JS (
EM_JS, notEM_ASYNC_JS) - this doesn't use Asyncify - If locked, return immediately instead of waiting
- Release lock via double setTimeout(0) to ensure Asyncify fully completes before allowing new operations
// Good: Synchronous check (no Asyncify)
EM_JS(int, isLocked, (), { return Module._locked ? 1 : 0; });
// Bad: This uses Asyncify and will cause conflicts
while (isLocked()) {
emscripten_sleep(10); // Uses Asyncify!
}wxwidgets/src/wasm/dialog.cpp - Modal implementation with lock mechanism