Skip to content

Fix MQTT disconnect crash: lifetime & race fixes in MQTT/MoonRaker/HttpServer - #870

Merged
zhangzhend0ng merged 4 commits into
build_platform_engineeringfrom
feature_sentry_crash_fix
Sep 17, 2026
Merged

zhangzhend0ng merged 4 commits into
build_platform_engineeringfrom
feature_sentry_crash_fix

Conversation

@SukiSunYuhang

@SukiSunYuhang SukiSunYuhang commented Sep 17, 2026

Copy link
Copy Markdown

Summary

Fixes the crash (0xC0000409 / std::bad_weak_ptr) triggered when a printer disconnects, by resolving lifetime races across the connection layer:

  • MQTT (Utils/MQTT.cpp/.hpp): safe callback dispatch and teardown around disconnect
  • MoonRaker (Utils/MoonRaker.cpp/.hpp): eliminate use-after-free on connection drop
  • HttpServer (GUI/HttpServer.cpp/.hpp): harden shutdown against in-flight handlers
  • SSWCP (GUI/SSWCP.cpp/.hpp): related lifetime guards

Commits (rebased linearly on the current build_platform_engineering tip):

  • e88ace5 Fix crash on MQTT disconnect (MQTT/MoonRaker/HttpServer)
  • a4c08cf Fix remaining MQTT lifetime hazards and harden HTTP server shutdown

Test plan

  • Repeated printer connect/disconnect (MQTT WAN mode) — no crash
  • LAN mode (MoonRaker) connect/disconnect — no crash
  • App shutdown with device page open and requests in flight — no crash
  • Verify slicing/print workflows unaffected

@SukiSunYuhang
SukiSunYuhang force-pushed the feature_sentry_crash_fix branch from f716d44 to 1d53ceb Compare September 17, 2026 03:43
@SukiSunYuhang
SukiSunYuhang force-pushed the feature_sentry_crash_fix branch from 1d53ceb to a4c08cf Compare September 17, 2026 04:12
SukiSunYuhang added a commit that referenced this pull request Sep 17, 2026
…de m_server_mtx

Address review findings on PR #870:

- start_http_server is written by the io thread's exception handler
  without holding m_server_mtx and read unlocked by is_started()/
  set_port() and the health-check loop, so it must be atomic.
- Split start() into start_locked() plus tail: restart() and start()
  now call start_health_check() only after m_server_mtx is released.
  A retired health-check thread can still be blocked in is_healthy()
  waiting for that lock; joining it via start_health_check() while
  holding the lock deadlocks (T1 holds m_server_mtx waiting on join,
  T2 waits on m_server_mtx to finish).
SukiSunYuhang added a commit that referenced this pull request Sep 17, 2026
…ure in failure callback

Address second-round review findings on PR #870:

- Moonraker: time_sync_manager_ is reset by disconnect() on the calling
  thread while Paho receive-thread callbacks and async_* APIs on
  arbitrary threads read it. All reads now go through a locked
  time_sync_manager_snapshot(), the disconnect reset happens under the
  same mutex, and SetMessageCallback(nullptr) is moved BEFORE the reset
  so no new dispatch can start once the manager is dropped.
- SSWCP: the connection failure callback captured the engine
  shared_ptr while being stored inside the engine itself, keeping its
  refcount >= 1 forever — ~MqttClient (the only place the callback is
  cleared) could never run. Capture a weak_ptr instead.
MQTT:
- Add MqttClient::create() factory that caches the client's own weak_ptr
  (self_) at construction; Paho callbacks use it instead of
  shared_from_this(), which threw bad_weak_ptr for raw new-ed clients and
  during destruction. A tearing_down_ flag guards late callbacks.
- SSWCP sw_get_pin_code: keep the one-shot pin-code client in a static
  shared_ptr released on the UI thread only (was raw new/delete racing
  the Paho receive thread); parse the response with allow_exceptions=
  false so a malformed payload cannot terminate the process inside the
  C callback.
- Moonraker_Mqtt: check callback-map emptiness under m_cbs_mtx in
  on_status_arrived/on_notification_arrived.

HttpServer:
- Create the IOServer on the calling thread; the io thread no longer
  writes server_, which raced the locked readers.
- Guard start_health_check against assigning to our own joinable thread
  handle (std::terminate) when stop() disabled the check while the
  health thread was inside restart() -> start().
- Hold m_server_mtx across restart() teardown and start() so
  is_healthy() cannot observe the torn-down state and trigger a second
  restart.
…de m_server_mtx

Address review findings on PR #870:

- start_http_server is written by the io thread's exception handler
  without holding m_server_mtx and read unlocked by is_started()/
  set_port() and the health-check loop, so it must be atomic.
- Split start() into start_locked() plus tail: restart() and start()
  now call start_health_check() only after m_server_mtx is released.
  A retired health-check thread can still be blocked in is_healthy()
  waiting for that lock; joining it via start_health_check() while
  holding the lock deadlocks (T1 holds m_server_mtx waiting on join,
  T2 waits on m_server_mtx to finish).
…ure in failure callback

Address second-round review findings on PR #870:

- Moonraker: time_sync_manager_ is reset by disconnect() on the calling
  thread while Paho receive-thread callbacks and async_* APIs on
  arbitrary threads read it. All reads now go through a locked
  time_sync_manager_snapshot(), the disconnect reset happens under the
  same mutex, and SetMessageCallback(nullptr) is moved BEFORE the reset
  so no new dispatch can start once the manager is dropped.
- SSWCP: the connection failure callback captured the engine
  shared_ptr while being stored inside the engine itself, keeping its
  refcount >= 1 forever — ~MqttClient (the only place the callback is
  cleared) could never run. Capture a weak_ptr instead.
@SukiSunYuhang
SukiSunYuhang force-pushed the feature_sentry_crash_fix branch from 89a123b to 6faa813 Compare September 17, 2026 10:01
@zhangzhend0ng
zhangzhend0ng merged commit e700c93 into build_platform_engineering Sep 17, 2026
@ZhongZhiPan
ZhongZhiPan deleted the feature_sentry_crash_fix branch September 18, 2026 06:33
@ZhongZhiPan
ZhongZhiPan restored the feature_sentry_crash_fix branch September 18, 2026 07:01
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.

2 participants