Skip to content

fix(cpn): avoid deadlock when closing the simulator during boot - #39

Open
pfeerick wants to merge 10 commits into
mainfrom
pfeerick/fix-wasm-sim-stop-deadlock
Open

pfeerick wants to merge 10 commits into
mainfrom
pfeerick/fix-wasm-sim-stop-deadlock

Conversation

@pfeerick

@pfeerick pfeerick commented Nov 3, 2026

Copy link
Copy Markdown
Owner

Summary

Closing the simulator while the firmware is still booting can hang Companion. This fixes a deadlock in WasmSimulatorInterface between stop() and the firmware threads.

Cause

  • stop() holds m_mutex across simuStop(), which joins the firmware threads.
  • Firmware threads read analog inputs through host_simuGetAnalog → getAnalogValue(), which also locked m_mutex. A thread that reads an analog after stop() takes the lock blocks forever, so the join never returns.
  • The GUI thread polls simulator->isRunning() in SimulatorWidget::stop(), which takes the same mutex. It blocks there, so the 2s stop timeout never fires and Companion hangs.

It only happens when closing early because the menus task has to finish edgeTxInit() before it can exit, and that reads analogs (throttle/switch checks, splash inactivity check). In steady state the threads exit without another analog read.

Changes

  • Store m_analogValues as std::atomic<int16_t>, so getAnalogValue() and setAnalogValue() no longer take m_mutex.
  • Close the SDL audio device after simuStop() rather than before, so the firmware audio task cannot be inside SDL_QueueAudio() while the device is closed.

Testing

  • Not yet built or run: WAMR could not be fetched in the dev environment. Checked separately that the std::atomic<int16_t>[] member initialiser compiles with -std=c++17.
  • To verify: launch the simulator for a colour radio (e.g. TX16S) from Companion and close it while the splash screen is still showing. Companion should no longer hang.

🤖 Generated with Claude Code

raphaelcoeffic and others added 10 commits November 1, 2026 10:25
Co-authored-by: raphaelcoeffic <1050031+raphaelcoeffic@users.noreply.github.com>
Co-authored-by: raphaelcoeffic <1050031+raphaelcoeffic@users.noreply.github.com>
…e bottom (EdgeTX#7967)

Co-authored-by: raphaelcoeffic <1050031+raphaelcoeffic@users.noreply.github.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
…sh (EdgeTX#7980)

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…eTX#7976)

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
WasmSimulatorInterface::stop() holds m_mutex across simuStop(), which
joins the firmware threads. Those threads read analog inputs through
host_simuGetAnalog -> getAnalogValue(), which also took m_mutex. If a
firmware thread read an analog after stop() took the lock, the join
never returned. The GUI thread then blocked on the same mutex in
isRunning(), so the 2s stop timeout never fired and Companion hung.

This shows up when the simulator is closed while the firmware is still
booting, because edgeTxInit() reads analogs (throttle/switch checks,
splash inactivity check) before the menus task can exit.

Store the analog values as atomics so getAnalogValue() and
setAnalogValue() no longer need m_mutex. Also close the SDL audio
device after simuStop() rather than before, so the firmware audio task
cannot be inside SDL_QueueAudio() while the device is closed.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants