From c3ce3f160671de4b8172f765a7c42e40d1956af6 Mon Sep 17 00:00:00 2001 From: Jakub <106778416+MarekZegare4@users.noreply.github.com> Date: Thu, 3 Sep 2026 22:34:47 +0200 Subject: [PATCH] fix(sim): add sim_stop_main_loop() -- host pages must stop old instances A Reset control on a host page (meshcore-solo-site's RESET button, mesh.html's own Reset buttons) re-invokes the MODULARIZE factory for the same simInstanceTag to simulate a device restart, since board.reboot() just exit()s the wasm process (inert / freezes the tab under -sEXIT_RUNTIME=0). Nothing ever tore down the OLD instance's own emscripten_set_main_loop() callback, so it keeps ticking forever after being "replaced" -- and keeps drawing onto the same simInstanceTag-keyed the new instance draws onto too. Two visible symptoms this caused on meshcore-solo-site: flicker/reversion after resetting the same device more than once (multiple orphaned instances competing for one canvas), and -- together with the HomeScreen::poll() fix in 5fbfd7c5 -- would have still left a residual single-old-instance repaint race even after that fix alone. sim_stop_main_loop() wraps emscripten_cancel_main_loop() so a host page can explicitly stop the OLD Module reference right before discarding it. Co-Authored-By: Claude Sonnet 5 Claude-Session: https://claude.ai/code/session_018iubftDmKNWmkNnhJRz8UH --- examples/companion_radio/main.cpp | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/examples/companion_radio/main.cpp b/examples/companion_radio/main.cpp index 2e9d9d37..b12f931f 100644 --- a/examples/companion_radio/main.cpp +++ b/examples/companion_radio/main.cpp @@ -359,6 +359,27 @@ extern "C" EMSCRIPTEN_KEEPALIVE int sim_is_ready() { return g_sim_ready ? 1 : 0; } +// A host page's "Reset" control (meshcore-solo-site's RESET button, +// mesh.html's own Reset buttons) re-invokes the MODULARIZE factory +// function for the same simInstanceTag to simulate a real device restart +// -- board.reboot() is inert here (exit()s the whole wasm process, which +// under -sEXIT_RUNTIME=0 just freezes the tab). That leaves the OLD +// Module instance's own emscripten_set_main_loop() callback (see +// sim_main.cpp's sim_idbfs_ready()) still registered and still ticking +// forever afterwards -- nothing ever tore it down. Two real, user-visible +// consequences: it keeps re-drawing onto the same simInstanceTag-keyed +// element the NEW instance is also drawing onto (visible as +// flicker/reversion once more than one reset has piled up orphaned +// instances), and if the old instance had been sitting on the Shutdown +// screen, HomeScreen::poll() keeps re-firing shutdown() -> turnOff() on +// every tick, permanently blacking that canvas out from under the new +// instance. A host page should call this on the OLD Module reference +// right before discarding it (i.e. right before re-invoking the +// MODULARIZE factory for that same tag) to actually stop it. +extern "C" EMSCRIPTEN_KEEPALIVE void sim_stop_main_loop() { + emscripten_cancel_main_loop(); +} + extern "C" EMSCRIPTEN_KEEPALIVE int sim_test_advert_flood() { if (!g_sim_ready) return 0; return the_mesh.advertFlood() ? 1 : 0;