Repository navigation
adversarial: keep executor close bounded when a child holds stdout - #657
Merged
Pedro Henrique Penna (ppenna) merged 3 commits intoOct 20, 2026
Merged
Pedro Henrique Penna (ppenna) merged 3 commits into
Pedro Henrique Penna (ppenna) merged 3 commits into
Conversation
ExecutorClient.close() closed the buffered stdout stream after the wrapper had exited. If a child process it started still held stdout, the protocol reader was blocked in readline and holds the buffered stream's lock, so close waited until that child exited. The reader join with its timeout ran only after the close. Join the reader first. If it is still blocked, do not close stdout; report that a surviving process holds the executor output. The reader is a daemon thread and ends when that process exits. Adds a real-process regression test in which the wrapper exits and leaves a child holding stdout. Before the change, close blocked for the child's full lifetime and returned without error. Fixes #503 (executor close blocks on inherited stdout) Co-Authored-By: Claude Haiku 5.5 <noreply@anthropic.com> Signed-off-by: jay23606 <4602056+jay23606@users.noreply.github.com>
When close gives up on a surviving process, the reader kept queueing its output for a client that no longer reads, and stdout was closed only on the synchronous path, so a deferred close never released the pipe. Once close abandons the reader, the reader drains and discards what the surviving process writes, and the reader closes stdout when its output ends. Co-Authored-By: Claude Haiku 5.5 <noreply@anthropic.com> Signed-off-by: jay23606 <4602056+jay23606@users.noreply.github.com>
…ed request Co-Authored-By: Claude Haiku 5.5 <noreply@anthropic.com> Signed-off-by: jay23606 <4602056+jay23606@users.noreply.github.com>
Contributor
There was a problem hiding this comment.
🟢 Approval recommended
The focused changes address the blocking cleanup path with regression coverage and no identified blocking issues.
No open issues 🎉
What changed in this PR
Fixes #503 by keeping executor cleanup bounded when a surviving child holds stdout open.
Changes:
- Moves stdout closure to the reader thread and reports when bounded cleanup leaves it running.
- Discards output received after the reader is abandoned.
- Adds regression coverage for bounded close, stream closure, and discarded output.
| File | Description |
|---|---|
| scripts/test_adversarial.py | Adds executor cleanup and reader regression tests. |
| scripts/nvx_tools/adversarial.py | Implements reader-owned stdout closure and bounded cleanup. |
Review effort: Balanced
Pedro Henrique Penna (ppenna)
deleted the
ppenna/executor-close-surviving-stdout-20261020
branch
October 20, 2026 22:10
This was referenced Oct 20, 2026
This was referenced Oct 21, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Replaces #586 by Jay (@jay23606), whose commits are carried here unchanged (head
756f215b2). It is opened from amicrosoft/nvxbranch so that CI runs on a stable head: automated rebases of the original kept superseding its runs.Fixes #503 (executor close blocks on inherited stdout).
This replaces #532, which was closed. This branch is a single commit on current
dev, with no merge commits.Summary
ExecutorClient.close()closed the buffered stdout stream after the wrapper exited. If a child it started still held stdout, the protocol reader was blocked inreadlineand held that stream's lock, soclose()waited until the child exited (about 20 seconds in the reproduction).close()now joins the reader with its existing timeout first. If the reader is still blocked, stdout is not closed, andclose()raisesexecutor output is still held open by a surviving process. Otherwise stdout closes as before. The reader is a daemon thread and ends when the surviving process exits.Test
ExecutorCloseTests.test_close_is_bounded_when_a_surviving_child_holds_executor_stdoutruns a real wrapper that exits after starting a child that inherits stdout. Before the change,close()blocked about 20 seconds and returned without error. After the change it raises within the bound. The test kills the surviving child in afinallyblock.python -B -m unittest test_adversarial: 71 tests, OK (6 skipped, as before).ruff checkandruff format --checkare clean for both files.nvx.py verify, which needs the OpenVMM submodule, and hardware-backed campaigns.🤖 Generated with Claude Code