Skip to content

Feature: Graceful shutdown#3

Merged
SeanathanVT merged 6 commits into
mainfrom
feature/graceful-shutdown
May 2, 2026
Merged

Feature: Graceful shutdown#3
SeanathanVT merged 6 commits into
mainfrom
feature/graceful-shutdown

Conversation

@SeanathanVT
Copy link
Copy Markdown
Owner

@SeanathanVT SeanathanVT commented May 1, 2026

Summary

Comprehensive rewrite of the shutdown path to ensure the belt always stops and BLE disconnects cleanly, plus a web UI notification so users see "Server is shutting down" instead of their browser silently going dead.

Problem

  • Ctrl+C on run.py sent SIGINT to both the wrapper and Waitress subprocess (same process group), causing race conditions: connection reset errors, belt potentially left running, no UI notification
  • Clicking Close while BLE scanning was still in progress crashed with RuntimeError: Event loop stopped before Future completed
  • _deferred_exit only waited 1s, not enough time for the browser's /stats poll to arrive and receive the shutdown signal

Changes

app.py

  • Added _server_stopping flag exposed via /stats endpoint so all session templates can detect shutdown
  • Increased deferred exit delay from 1s to 5s, giving browser time (worst case 3s poll interval) to receive /stats with stopping: true
  • Wrapped loop.run_until_complete(_connect_to_pad()) in try/except for RuntimeError to catch the race when shutdown interrupts BLE scanning

run.py

  • Added preexec_fn=os.setsid() to subprocess.Popen() so Waitress runs in its own process group. Ctrl+C only hits run.py
  • POST /shutdown before terminating, with 15s timeout (BLE cleanup can take a few seconds)
  • Added 6s wait after shutdown call so server exits cleanly on its own without forced terminate

Templates

active_session.html, paused_session.html, start_session.html all check data.stopping === true in their /stats poll callback and display "Server is shutting down. You may close this window."

Documentation

  • CHANGELOG.md: added shutdown notification and BLE scanning race condition fixes
  • ROADMAP.md: updated section 1.2 with accurate implementation details (os._exit, UI notification, process isolation)
  • README.md: updated graceful shutdown description

Shutdown Flow (Ctrl+C)

  1. run.py catches KeyboardInterrupt
  2. POSTs to http://127.0.0.1:5001/shutdown on still-alive Waitress server
  3. Waitress handles it, sets _server_stopping = True, does BLE cleanup, returns response
  4. Browser's next /stats poll (within ~1.5s) receives stopping: true and displays shutdown message
  5. After 5s, Waitress exits via os._exit(0)
  6. run.py waits 6s then calls terminate() as safety net

@SeanathanVT SeanathanVT merged commit 99d44ca into main May 2, 2026
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.

1 participant