Skip to content

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

Merged
pfeerick merged 1 commit into
EdgeTX:mainfrom
pfeerick:pfeerick/fix-wasm-sim-stop-deadlock
Nov 5, 2026
Merged

pfeerick merged 1 commit into
EdgeTX:mainfrom
pfeerick:pfeerick/fix-wasm-sim-stop-deadlock

Conversation

@pfeerick

@pfeerick pfeerick commented Nov 4, 2026

Copy link
Copy Markdown
Member

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

  • Tested the CI build on macOS: closing the simulator while the splash screen is still showing no longer hangs Companion.
  • Not tested on Linux or Windows.
  • To reproduce: launch the simulator for a colour radio (e.g. TX16S) from Companion and close it while the splash screen is still showing.

🤖 Generated with Claude Code

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>
@pfeerick pfeerick added this to the 3.0 milestone Nov 4, 2026
@pfeerick pfeerick added companion (cpn) Related to the companion software simulator (sim) bug/regression ↩️ A new version of EdgeTX broke something labels Nov 4, 2026
@pfeerick
pfeerick merged commit e99601c into EdgeTX:main Nov 5, 2026
16 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug/regression ↩️ A new version of EdgeTX broke something companion (cpn) Related to the companion software simulator (sim)

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant