@@ -159,7 +159,8 @@ def close(self) -> None:
159159 """Terminate the tmux control mode process and clean up threads.
160160
161161 Terminates the subprocess and waits for reader/stderr threads to
162- finish. Non-daemon threads ensure clean shutdown without races.
162+ finish. Threads are daemonized to avoid hanging interpreter shutdown
163+ if cleanup is interrupted, but close() still joins for a clean exit.
163164 """
164165 proc = self .process
165166 if proc is None :
@@ -178,7 +179,13 @@ def close(self) -> None:
178179 self ._shutdown_timeout ,
179180 )
180181 finally :
181- # Join threads to ensure clean shutdown (non-daemon threads).
182+ # Close pipes first to unblock reader/stderr threads.
183+ for stream in (proc .stdin , proc .stdout , proc .stderr ):
184+ if isinstance (stream , io .IOBase ):
185+ with contextlib .suppress (Exception ):
186+ stream .close ()
187+
188+ # Join threads to ensure clean shutdown.
182189 # Skip join if called from within the thread itself (e.g., during GC).
183190 current = threading .current_thread ()
184191 if (
@@ -204,12 +211,6 @@ def close(self) -> None:
204211 self ._shutdown_timeout ,
205212 )
206213
207- # Close pipes to avoid unraisable BrokenPipe errors on GC.
208- for stream in (proc .stdin , proc .stdout , proc .stderr ):
209- if isinstance (stream , io .IOBase ):
210- with contextlib .suppress (Exception ):
211- stream .close ()
212-
213214 self .process = None
214215 self ._server_args = None
215216 self ._protocol .mark_dead ("engine closed" )
@@ -686,19 +687,19 @@ def _start_process(self, server_args: tuple[str | int, ...]) -> None:
686687 self ._protocol .register_command (bootstrap_ctx )
687688
688689 # Start IO threads after registration to avoid early protocol errors.
689- # Non-daemon threads ensure clean shutdown via join() in close() .
690+ # Daemon threads prevent interpreter hang if cleanup is interrupted .
690691 if self ._start_threads :
691692 self ._reader_thread = threading .Thread (
692693 target = self ._reader ,
693694 args = (self .process ,),
694- daemon = False ,
695+ daemon = True ,
695696 )
696697 self ._reader_thread .start ()
697698
698699 self ._stderr_thread = threading .Thread (
699700 target = self ._drain_stderr ,
700701 args = (self .process ,),
701- daemon = False ,
702+ daemon = True ,
702703 )
703704 self ._stderr_thread .start ()
704705
0 commit comments