From c59f028150bcc76fd9147b9302fd5ad8a7fb3092 Mon Sep 17 00:00:00 2001 From: Daniel Han Date: Sun, 15 Mar 2026 06:54:40 +0000 Subject: [PATCH] studio: kill orphaned llama-server processes on startup When the studio process is killed (SIGTERM/SIGKILL), atexit handlers may not run in the subprocess orchestrator, leaving llama-server processes orphaned and holding GPU memory. This caused OOM errors when trying to load a new model after a studio restart. On init, LlamaCppBackend now runs pgrep to find and SIGKILL any stale llama-server processes before starting fresh. --- studio/backend/core/inference/llama_cpp.py | 28 ++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/studio/backend/core/inference/llama_cpp.py b/studio/backend/core/inference/llama_cpp.py index b853bcc5a6..0dde135fd4 100644 --- a/studio/backend/core/inference/llama_cpp.py +++ b/studio/backend/core/inference/llama_cpp.py @@ -49,6 +49,7 @@ class LlamaCppBackend: self._stdout_lines: list[str] = [] self._stdout_thread: Optional[threading.Thread] = None + self._kill_orphaned_servers() atexit.register(self._cleanup) # ── Properties ──────────────────────────────────────────────── @@ -706,6 +707,33 @@ class LlamaCppBackend: self._stdout_thread.join(timeout = 2) self._stdout_thread = None + @staticmethod + def _kill_orphaned_servers(): + """Kill any orphaned llama-server processes from previous studio runs.""" + import os + import signal + + try: + result = subprocess.run( + ["pgrep", "-f", "llama-server"], + capture_output = True, text = True, timeout = 5, + ) + if result.returncode != 0: + return + for line in result.stdout.strip().splitlines(): + pid = int(line.strip()) + if pid == os.getpid(): + continue + try: + os.kill(pid, signal.SIGKILL) + logger.info(f"Killed orphaned llama-server process (pid={pid})") + except ProcessLookupError: + pass + except PermissionError: + pass + except Exception: + pass + def _cleanup(self): """atexit handler to ensure llama-server is terminated.""" self._kill_process()