fix(tui): close slash_worker inside _finalize_session (defense-in-depth, #38095) (#42149)

Fold the slash-worker subprocess close into _finalize_session itself —
the single _finalized-guarded session-end chokepoint — instead of
relying on each caller (_teardown_session, _shutdown_sessions) to close
it separately. A future code path that finalizes a session directly can
no longer reintroduce the #38095 worker leak.

Idempotent: _SlashWorker.close() is poll()-guarded and _finalize_session
short-circuits on _finalized, so the existing teardown paths are
unaffected. Drops the now-redundant separate close() in
_shutdown_sessions.

Note: the active leak this issue reported was already fixed on main
(WS-orphan reaper #38591, _restart_slash_worker close, atexit shutdown).
This addresses the residual defense-in-depth gap the reporter correctly
identified in their follow-up comment.
This commit is contained in:
Teknium 2026-06-08 07:26:05 -07:00 committed by GitHub
parent 5e06c9ffef
commit a3fca26c56
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 53 additions and 16 deletions

View file

@ -949,6 +949,37 @@ def test_ws_orphan_reap_closes_worker_when_session_stays_detached(monkeypatch):
server._sessions.pop("orphan-sid", None)
def test_finalize_session_closes_slash_worker(monkeypatch):
"""_finalize_session closes the slash_worker subprocess itself.
Regression for #38095: the worker cleanup used to live only in the
callers (_teardown_session / _shutdown_sessions), so any code path that
finalized a session without going through them leaked the worker. Folding
close() into the single _finalized-guarded chokepoint makes the cleanup
defense-in-depth and idempotent.
"""
closed = {"count": 0}
class _FakeWorker:
def close(self):
closed["count"] += 1
monkeypatch.setattr(server, "_notify_session_boundary", lambda *a, **k: None)
monkeypatch.setattr(server, "_get_db", lambda: None)
session = _session(slash_worker=_FakeWorker())
server._finalize_session(session)
assert closed["count"] == 1
assert session.get("_finalized") is True
# Idempotent: a second finalize (or a follow-up teardown) must not
# re-close the worker — the _finalized guard short-circuits.
server._finalize_session(session)
server._teardown_session(session)
assert closed["count"] == 1
def test_ws_orphan_reap_spares_reattached_session(monkeypatch):
"""A session that rebinds a live transport is NOT considered orphaned."""