Skip to content

Call ClosePseudoConsole off the event-loop thread - #36

Open
tonyfettes wants to merge 2 commits into
mainfrom
fix/win32-close-pseudo-console-off-loop
Open

tonyfettes wants to merge 2 commits into
mainfrom
fix/win32-close-pseudo-console-off-loop

Conversation

@tonyfettes

@tonyfettes tonyfettes commented Oct 11, 2026 •

Copy link
Copy Markdown
Collaborator

Problem

Follow-up to #35, which fixed the cancellation path and left this one documented as open.

On the normal exit path the wait task calls ClosePseudoConsole as soon as the child exits, on the event-loop thread, with the output pipe still open — it has to stay open, because the caller may read up to EOF and EOF only arrives after ClosePseudoConsole.

Before Windows 11 24H2 (build 26100) that call waits for the pseudoconsole to exit, and Microsoft's documentation says not to make it on the thread that reads the output pipe. Here the event loop is that thread in effect: while it is blocked no reader task can issue another read, so a pseudoconsole that still holds more output than the pipe can take should never finish writing it.

Change

Two commits, pushed separately so each has its own CI run:

  1. Test only. The child types two 100x300 screens of text and exits at once, while the test reads to EOF and checks the last line and the exit code.
  2. The fix. ClosePseudoConsole runs on a short-lived helper thread, so the event loop keeps serving the reader. That gives every Windows version the "returns immediately" behavior 24H2 introduced.

Status: not reproduced — the problem is not demonstrated

Nothing win32 here can be run locally (macOS); CI is the only execution.

attempt test windows-2022 without the fix
1 (650e95b, replaced) slow reader, steady stream of lines passed in about a minute
2 (85598b1) type a screenful, exit at once, normal reader passed in about a minute

Both tests were built to leave the pseudoconsole with more unwritten output than the pipe holds at the moment the child exits, and neither hung on windows-2022 without the fix. With the fix, all four jobs pass in both rounds, so the helper thread breaks nothing CI covers — but there is no evidence it fixes anything.

Compare #35, where the same method did reproduce the cancellation-path hang (the unfixed run sat in Test until timeout-minutes).

So this PR rests on Microsoft's documentation alone. Recommendation: close it rather than add a thread for an undemonstrated problem. The second test is a reasonable regression test for "a screenful at exit reaches the reader in full" and could be kept on its own.

🤖 Generated with Claude Code

@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Oct 11, 2026 •

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review ✅ Completed 2026-10-11T08:00:29.356020Z bb3b781 New commits
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 650e95b6f3

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread pty_win32_test.mbt Outdated
// empties, and neither side moves again. As with the test below, that
// failure shows up as the CI job's `timeout-minutes`, not as an assertion.
#cfg(platform="windows")
async test "@pty.spawn lets a slow reader reach EOF after the child exits" {

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Add the off-loop close before enabling this test

On the windows-2022 CI runner, this test deliberately reaches the existing normal-exit path where pty_win32.mbt still calls ClosePseudoConsole on the event-loop thread. Once that call blocks, neither the slow reader nor with_timeout_opt can run, so the test hangs until .github/workflows/ci.yml kills the entire job after ten minutes. Because this commit contains only the reproducer and not the advertised helper-thread implementation, it leaves the Windows CI check unable to pass whenever the regression is successfully reproduced.

Useful? React with 👍 / 👎.

On the normal exit path the wait task calls ClosePseudoConsole on the
event-loop thread as soon as the child exits. Before Windows 11 24H2 that
call waits for the pseudoconsole to exit, and while it waits no reader
task can run, so a pseudoconsole that still has more output than the pipe
holds can never finish writing it.

The child `type`s two 100x300 screens of text and exits at once while the
test reads to EOF. No fix yet: if the analysis is right this commit runs
into timeout-minutes on windows-2022.

A first attempt with a slow reader and a steady stream of lines did not
reproduce it (the run passed on windows-2022 in about a minute); this one
replaces it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@tonyfettes
tonyfettes force-pushed the fix/win32-close-pseudo-console-off-loop branch from d64b095 to 85598b1 Compare October 11, 2026 07:57
The wait task closed the HPCON on the event-loop thread as soon as the
child exited. Before Windows 11 24H2 ClosePseudoConsole waits for the
pseudoconsole to exit, and the pseudoconsole cannot exit until its
remaining output has been read; with the event loop blocked no reader
task can run, so a pseudoconsole holding more output than the pipe can
take never finishes.

Make the call on a short-lived helper thread instead. The event loop keeps
serving the reader, and every Windows version gets the "returns
immediately" behavior that 24H2 introduced. If the thread cannot be
created the call is made in place, as before.

The cancellation path still closes the output pipe first: nobody reads it
there, so nothing else would unblock the pseudoconsole.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant