diff options
| author | historia <historiavg@proton.me> | 2026-08-25 20:10:40 -0400 |
|---|---|---|
| committer | historia <historiavg@proton.me> | 2026-08-25 20:10:40 -0400 |
| commit | ca3c78ef577f5d8435dd10db0296c3e2fd8e69e2 (patch) | |
| tree | cff4b84c14d0ac88bc53f2aef63115352d57de90 /app/backends | |
| parent | 757588321d4c27889be6e8e3c12b75873ad1218d (diff) | |
| download | tts-audiobook-generator-ca3c78ef577f5d8435dd10db0296c3e2fd8e69e2.tar.gz | |
feat: stop server and exit after generating
Diffstat (limited to 'app/backends')
| -rw-r--r-- | app/backends/servers.py | 26 |
1 files changed, 26 insertions, 0 deletions
diff --git a/app/backends/servers.py b/app/backends/servers.py index 62eadb5..965d7df 100644 --- a/app/backends/servers.py +++ b/app/backends/servers.py @@ -135,6 +135,28 @@ def _pid_alive(pid: int) -> bool: return True +def _reap_exited(pid: int) -> bool: + """Reap PID when it is our exited child; True when confirmed dead. + + An exited child stays visible to signal-0 probes until its parent waits + for it, and the hub never reaps between ``start`` and ``stop`` — so a + server that honored SIGTERM would still count as alive and every stop + would burn the whole grace period before escalating to SIGKILL. Returns + False when the process may still be running or was not our child (the + caller then falls back to its own liveness probes). + """ + if not hasattr(os, "waitpid") or not hasattr(os, "WNOHANG"): + return False + try: + waited, _status = os.waitpid(pid, os.WNOHANG) + except ChildProcessError: + # Not our child (or someone reaped it already): not ours to judge. + return False + except OSError: + return False + return waited == pid + + def _kill_pid(pid: int) -> bool: """Terminate PID (and its process group on POSIX). Returns True when dead.""" if sys.platform == "win32": @@ -163,6 +185,10 @@ def _kill_pid(pid: int) -> bool: except PermissionError: return False for _ in range(int(STOP_GRACE_SECONDS * 10)): + # Reap first so an exited (zombie) child ends the wait immediately + # instead of keeping the killpg(0) probe "alive" until SIGKILL. + if _reap_exited(pid): + return True try: os.killpg(pgid, 0) except ProcessLookupError: |
