Skip to content

fix(shutdown): wake and join websocket thread before widget teardown - #367

Merged
gmmcosta15 merged 5 commits into
devfrom
fix/shutdown-teardown-segv
Oct 23, 2026
Merged

gmmcosta15 merged 5 commits into
devfrom
fix/shutdown-teardown-segv

Conversation

@gmmcosta15

Copy link
Copy Markdown
Collaborator

Description

  • Feature
  • Bug Fix
  • Code Refactor
  • Documentation

Stopping the service sometimes ends in status=11/SEGV right after Final exit cleanup (2 of 7 stops on the .151 dev box, 2026-10-20). The cause is the websocket thread. ws.close() frees the socket while the websocket-client dispatcher is blocked in epoll with ping_timeout (10 s), so the thread wakes up to 10 s later. It then runs on_close, which emits Qt signals while sip is destroying the widget tree at interpreter exit. This PR wakes the dispatcher with shutdown(SHUT_RD), joins it before teardown, and silences the websocket callbacks once shutdown starts.

Changes

  • moonrakerComm: _closing flag set in wb_disconnect; on_open, on_message, on_close and reconnect return early once it is set
  • moonrakerComm: wb_disconnect stops the klippy status poll, sets keep_running=False and shuts the raw socket's read side so the dispatcher leaves epoll, then joins it (falls back to ws.close() on OSError)
  • updater_worker: shutdown() joins UpdaterAsyncLoop for up to 2 s
  • BlocksScreen: on_quit logs the threads still alive at final cleanup

Motivation

Repro script (websocket-client 1.9.0, close during an idle connection): 12 of 30 runs left the thread alive past the join, 0 of 90 with this change. The _closing guard follows KlipperScreen's KlippyWebsocket closing flag and the USBManager _closing fix from #359. Qt still delivers signals that were already queued, so blocking new callbacks is the only reliable guard. PyQt6 destroys wrapped widgets in sip's atexit hook, after Python's thread join, so a daemon thread that is still running at that point can emit into freed objects.

Tests

  • New: TestShutdown (dispatcher woken + poll stopped, close fallback, callbacks silent after disconnect, real-socket thread exits within the join over 8 runs) and test_shutdown_joins_loop_thread

Future work

  • One of the two 10-20 SEGVs is not yet tied to a thread; the new on_quit log names any thread still alive the next time it happens

@gmmcosta15
gmmcosta15 requested a review from a team October 23, 2026 08:35
@gmmcosta15 gmmcosta15 self-assigned this Oct 23, 2026
@gmmcosta15 gmmcosta15 added the bug Something is broken, or a fix for it label Oct 23, 2026
@gmmcosta15
gmmcosta15 merged commit 055197f into dev Oct 23, 2026
9 checks passed
@gmmcosta15
gmmcosta15 deleted the fix/shutdown-teardown-segv branch October 23, 2026 16:17
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something is broken, or a fix for it

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants