From 48746937a7320c193fca9c850f4efb14bcbbeddc Mon Sep 17 00:00:00 2001 From: Ryan Malloy Date: Sat, 14 Feb 2026 09:41:47 -0700 Subject: [PATCH] Fix executor shutdown warning on TUI exit Poll threads (position at 2 Hz, signal when monitoring) run while-loops with time.sleep() that never exit on app quit. Add on_unmount() to PositionScreen, SignalScreen, and BirdcageApp to clear polling flags and disconnect the device, so worker threads exit within the sleep interval instead of hitting the 300s timeout. --- tui/src/birdcage_tui/app.py | 11 +++++++++++ tui/src/birdcage_tui/screens/position.py | 4 ++++ tui/src/birdcage_tui/screens/signal.py | 4 ++++ 3 files changed, 19 insertions(+) diff --git a/tui/src/birdcage_tui/app.py b/tui/src/birdcage_tui/app.py index 1349e63..c7990d1 100644 --- a/tui/src/birdcage_tui/app.py +++ b/tui/src/birdcage_tui/app.py @@ -129,6 +129,17 @@ class BirdcageApp(App): if hasattr(screen, "on_show"): screen.on_show() + def on_unmount(self) -> None: + """Stop all polling threads and disconnect the device on shutdown.""" + for mode_key in MODES: + screen = self.query_one(f"#{mode_key}") + if hasattr(screen, "_polling"): + screen._polling = False + if hasattr(screen, "_monitoring"): + screen._monitoring = False + if self.device and hasattr(self.device, "disconnect"): + self.device.disconnect() + def action_toggle_dark(self) -> None: self.dark = not self.dark diff --git a/tui/src/birdcage_tui/screens/position.py b/tui/src/birdcage_tui/screens/position.py index da348c2..388b3c6 100644 --- a/tui/src/birdcage_tui/screens/position.py +++ b/tui/src/birdcage_tui/screens/position.py @@ -87,6 +87,10 @@ class PositionScreen(Container): self._polling = True self._poll_worker = self._do_position_poll() + def on_unmount(self) -> None: + """Stop polling thread on teardown.""" + self._polling = False + # ------------------------------------------------------------------ # Position poll worker # ------------------------------------------------------------------ diff --git a/tui/src/birdcage_tui/screens/signal.py b/tui/src/birdcage_tui/screens/signal.py index 0903c0a..9b7c7e9 100644 --- a/tui/src/birdcage_tui/screens/signal.py +++ b/tui/src/birdcage_tui/screens/signal.py @@ -71,6 +71,10 @@ class SignalScreen(Container): """Called when this screen becomes visible.""" pass # Monitoring is explicit via Start/Stop buttons. + def on_unmount(self) -> None: + """Stop monitoring thread on teardown.""" + self._monitoring = False + # ------------------------------------------------------------------ # Signal poll worker # ------------------------------------------------------------------