fix(dashboard): race PTY reader against WS receive to prevent FD leak (#54028) - #54047
fix(dashboard): race PTY reader against WS receive to prevent FD leak (#54028)#54047liuhao1024 wants to merge 4 commits into
Conversation
…NousResearch#54028) When the PTY reader task exits (PTY EOF or WS send failure), the writer loop previously blocked forever on ws.receive(). The finally block (which calls bridge.close()) never ran, leaking PTY file descriptors. Race ws.receive() against reader_task via asyncio.wait(FIRST_COMPLETED) so the loop exits promptly when the reader dies, allowing cleanup. 22 lines changed in web_server.py + regression test.
tonydwb
left a comment
There was a problem hiding this comment.
Code Review Summary
Verdict: Approved
Fixes a PTY bridge FD leak when the reader task exits. When pump_pty_to_ws exits (PTY EOF or WS send failure), the writer loop blocked forever on ws.receive() because nothing unblocked it. The finally block (which calls bridge.close()) never ran. The fix races ws.receive() against reader_task via asyncio.wait(FIRST_COMPLETED) so the loop exits when the reader dies. Test verifies bridge.close() is called even when ws.receive() would block forever.
Reviewed by Hermes Agent
|
Upstream commit The upstream fix is more comprehensive (42-line diff in Superseded by: |
|
Thanks for the focused fix here. This has been implemented on current Evidence from this automated hermes-sweeper review:
Closing as implemented on main. |
What does this PR do?
Fixes a PTY file descriptor leak in the dashboard
/api/ptyWebSocket handler. When the PTY reader task exits (PTY EOF or WebSocket send failure), the writer loop previously blocked forever onws.receive(), preventing thefinallyblock from running and leaking the bridge's file descriptors. This caused the gateway to hitOSError: [Errno 24] Too many open filesafter 8-9 hours of uptime.The fix races
ws.receive()againstreader_taskusingasyncio.wait(FIRST_COMPLETED)so the writer loop exits promptly when the reader dies, allowingbridge.close()cleanup to proceed.Related Issue
Fixes #54028
Type of Change
Changes Made
hermes_cli/web_server.py: Racews.receive()againstreader_taskin the PTY writer loop. When the reader exits (PTY EOF or WS send error), cancel the pending receive future and break the loop so thefinallyblock can callbridge.close(). (+22/-1 lines)tests/hermes_cli/test_pty_ws_bridge_close_on_reader_exit.py: Regression test verifyingbridge.close()is called when the PTY reader exits with EOF, even whenws.receive()would block indefinitely.How to Test
python -m pytest tests/hermes_cli/test_pty_ws_bridge_close_on_reader_exit.py -xvs— should passpython -m pytest tests/hermes_cli/test_web_server_pty_import.py -xvs— should passpython -m pytest tests/hermes_cli/test_web_server_skills_profiles.py tests/hermes_cli/test_web_server_fs.py tests/hermes_cli/test_dashboard_register.py -x— all should pass/chattab, connect, then disconnect — the bridge FD should be released promptly. Observed result:lsof -p PID | grep ptyshows the FD count stays stable over time instead of growing monotonically.Checklist
Code
fix(scope):,feat(scope):, etc.)pytest tests/ -qand all tests passDocumentation & Housekeeping
docs/, docstrings) — or N/Acli-config.yaml.exampleif I added/changed config keys — or N/ACONTRIBUTING.mdorAGENTS.mdif I changed architecture or workflows — or N/A