fix: ensure all daw_engine exit on app close (abrupt kill + LAN client open)

- desktop_engine: watchdog parent check via WaitForSingleObject(SYNCHRONIZE)
  instead of OpenProcess — killed parents linger as zombie objects, OpenProcess
  succeeds forever and watchdog never fires; hard-exit after 5s if graceful
  shutdown hangs (open LAN connection)
- lib.rs: Destroyed handler sweeps any orphan daw_engine/daw_vst_bridge by
  image name before exit(0), covering manual-launch and stale engines
This commit is contained in:
2026-08-16 21:18:21 +07:00
parent cead8c8c71
commit 98574aa0ea
3 changed files with 35 additions and 3 deletions
+23 -3
View File
@@ -61,12 +61,21 @@ def _parent_alive(pid):
# pid khong con ton tai.
if os.name == "nt":
import ctypes
# OpenProcess chi bao NULL khi process object da bi reap (moi handle
# dong het). Process vua bi kill (taskkill/crash) con "zombie" mot
# thoi gian -> OpenProcess van thanh cong -> watchdog tuong cha con
# song -> engine khong bao gio tu thoat. WaitForSingleObject tren
# handle co PROCESS_SYNCHRONIZE phan biet dung: object da terminate
# thi SIGNALED ngay, du handle con ton tai.
PROCESS_QUERY_LIMITED_INFORMATION = 0x1000
h = ctypes.windll.kernel32.OpenProcess(PROCESS_QUERY_LIMITED_INFORMATION, False, pid)
PROCESS_SYNCHRONIZE = 0x00100000
h = ctypes.windll.kernel32.OpenProcess(
PROCESS_QUERY_LIMITED_INFORMATION | PROCESS_SYNCHRONIZE, False, pid)
if not h:
return False # tien trinh da chet
return False # process object da mat -> cha da chet
terminated = ctypes.windll.kernel32.WaitForSingleObject(h, 0) == 0
ctypes.windll.kernel32.CloseHandle(h)
return True
return not terminated
try:
os.kill(pid, 0)
return True
@@ -140,6 +149,17 @@ def main():
logging.getLogger("desktop_engine").info(
"Parent process exited — shutting down engine")
server.should_exit = True
# Graceful shutdown co the TREO vo han: uvicorn
# _wait_tasks_to_complete cho open connection (client LAN
# dang mo browser) va timeout_graceful_shutdown=None.
# => engine song dai khi app chet dot ngot (crash/Task
# Manager). Hard-exit fallback sau 5s dam bao thoat het.
def _force_exit():
time.sleep(5.0)
logging.getLogger("desktop_engine").info(
"Graceful shutdown hung — forcing exit")
os._exit(0)
threading.Thread(target=_force_exit, daemon=True).start()
return
threading.Thread(target=_watchdog, daemon=True).start()