mirror of
https://github.com/Niko1221/Strata.git
synced 2026-11-07 06:15:58 -10:00
lifeline.popen ties every engine, image encoder, MCP server and sd-cli to the server that started them (kill-on-close job on Windows, PR_SET_PDEATHSIG from one anchor thread on Linux, a pipe watcher elsewhere). Engines and encoders get their own process group so Ctrl+C, a closing terminal and a console Break reach the server only, which stops the engine the clean way (STOP, QUIT, wait 20 s, terminate 5 s, kill). SIGHUP, SIGBREAK and the Windows console close/logoff/shutdown events take Ctrl+C's path; the handlers are installed before the model loads and the browser launch no longer holds the main thread (#1872). The owner guard escalates SIGTERM to SIGKILL after a grace period. strata_control/procs.py records every launch and finds leftovers; the next engine start, runtime start, runtime stop and the new runtime cleanup end them after re-checking identity. runtime start --die-with-pid for app-owned instances. tests/terminal/lifecycle_matrix.py is the close-path matrix.
53 lines
2.2 KiB
Python
53 lines
2.2 KiB
Python
"""#1445: a vision encoder that cannot start because the GPU is busy/unavailable (compute mode Exclusive_Process) says
|
|
so, with the nvidia-smi check, instead of only the first line of the encoder's output.
|
|
|
|
python -m serve.test_vision_busy
|
|
"""
|
|
import io
|
|
import tempfile
|
|
import unittest
|
|
from pathlib import Path
|
|
from types import SimpleNamespace
|
|
from unittest import mock
|
|
|
|
import serve.server as server
|
|
|
|
|
|
class Busy(unittest.TestCase):
|
|
def test_message_names_the_cause_from_the_line(self):
|
|
m = server.vision_start_error("ggml_backend_cuda_device_get_memory: cudaMemGetInfo failed "
|
|
"(CUDA-capable device(s) is/are busy or unavailable), returning 0/0")
|
|
self.assertIn("Exclusive_Process", m)
|
|
self.assertIn("nvidia-smi", m)
|
|
self.assertIn("did not start", m)
|
|
|
|
def test_message_names_the_cause_from_the_log_tail(self):
|
|
with tempfile.NamedTemporaryFile("w+", suffix=".log", delete=False) as f:
|
|
f.write("x" * 40000 + "\ncudaMalloc failed: CUDA-capable device(s) is/are busy or unavailable\n")
|
|
name = f.name
|
|
try:
|
|
with open(name, "a") as log:
|
|
m = server.vision_start_error("load_hparams: something", log)
|
|
self.assertIn("Exclusive_Process", m)
|
|
finally:
|
|
Path(name).unlink()
|
|
|
|
def test_other_failures_are_unchanged(self):
|
|
self.assertEqual(server.vision_start_error("ERR no mmproj\n"), "the vision encoder did not start: ERR no mmproj")
|
|
|
|
def test_start_raises_it(self):
|
|
line = "cudaMalloc failed: CUDA-capable device(s) is/are busy or unavailable\n"
|
|
proc = SimpleNamespace(stdin=io.StringIO(), stdout=io.StringIO(line), kill=lambda: None, wait=lambda timeout=None: 0,
|
|
poll=lambda: None)
|
|
v = server.Vision.__new__(server.Vision)
|
|
v.spawn = (["strata-vision"], None, None)
|
|
v.dir = Path(tempfile.gettempdir())
|
|
with mock.patch.object(server, "popen", lambda *a, **k: proc):
|
|
with self.assertRaises(RuntimeError) as cm:
|
|
v._start()
|
|
self.assertIn("Exclusive_Process", str(cm.exception))
|
|
|
|
|
|
if __name__ == "__main__":
|
|
unittest.main()
|