fix(execute_code): parallel cells no longer orphan kernel processes; LSP stops treating package dirs as project roots
Session kernels: a kernel mid-spawn (proc=None) read as dead, so every concurrent cell for one owner replaced the registry entry and the winner's process leaked outside the registry (110 live kernels, 1.1 GB, 330 threads under one 4-capped process). Reap/evict also tore down kernels with cells attached, rmtree-ing the staging dir under the spawner. Kernels now track attached cells: only settled kernels are reaped/evicted, a kernel dropped while busy is torn down by its last cell, and in-cell registry pops never remove a replacement. LSP: a directory holding __init__.py is a package, not a project root. hermes_cli/setup.py matched the python marker list and gave every worktree a second pyright rooted at hermes_cli/ (70 of 105 reaped clients in one session, ~40 servers / 8.7 GB live).
This commit is contained in:
@@ -307,6 +307,31 @@ class TestKernelOwnershipAndLifecycle(unittest.TestCase):
|
||||
stale.proc.wait(timeout=10)
|
||||
self.assertFalse(stale.alive())
|
||||
|
||||
def test_parallel_cells_share_one_kernel_process(self):
|
||||
"""Parallel cells for one owner race the first spawn. Each racer
|
||||
used to see proc=None as 'dead', replace the registry entry, and
|
||||
orphan the winner's process — 110 live kernels under a 4-capped
|
||||
process (Sep 2026). Every kernel process must stay registry-owned."""
|
||||
import subprocess
|
||||
import threading
|
||||
|
||||
results = []
|
||||
with _kernel_config():
|
||||
def _cell():
|
||||
results.append(self._run_as("conv-a", "import time; time.sleep(0.3)", task_id="t"))
|
||||
threads = [threading.Thread(target=_cell) for _ in range(6)]
|
||||
for t in threads:
|
||||
t.start()
|
||||
for t in threads:
|
||||
t.join()
|
||||
self.assertEqual([r["status"] for r in results], ["success"] * 6)
|
||||
self.assertEqual(len(_KERNELS), 1)
|
||||
live = subprocess.run(
|
||||
["pgrep", "-fc", "-P", str(os.getpid()), "hermes_kernel_runner"],
|
||||
capture_output=True, text=True,
|
||||
).stdout.strip()
|
||||
self.assertEqual(live, "1")
|
||||
|
||||
|
||||
class TestPerCellRpcAuthority(unittest.TestCase):
|
||||
"""Interpreter state persists across cells; RPC authority must not."""
|
||||
|
||||
Reference in New Issue
Block a user