Skip to content

gh-149388: Make PipeHandle.close idempotent#149518

Open
mxschmitt wants to merge 1 commit intopython:mainfrom
mxschmitt:gh-149388-pipehandle-close-idempotent
Open

gh-149388: Make PipeHandle.close idempotent#149518
mxschmitt wants to merge 1 commit intopython:mainfrom
mxschmitt:gh-149388-pipehandle-close-idempotent

Conversation

@mxschmitt
Copy link
Copy Markdown

@mxschmitt mxschmitt commented May 7, 2026

Fixes #149388. Applies @kumaraditya303's suggestion from the issue thread: clear _handle before calling CloseHandle, so that when CloseHandle raises on an already-closed handle, _handle is already None and neither a second close() nor __del__ re-raises.

The new regression test in test_windows_utils.PipeTests fails on main:

FAIL: test_pipe_handle_close_after_external_close
AssertionError: 792 is not None

and passes with the patch. Full python -m test test_asyncio is green on the patched build (2524 tests, 0 failures).

The end-to-end repro from the issue (repro_v2.py) goes from 6/20 → 0/60 trials firing WinError 6. Verified against a locally built CPython 3.16.0a0 (Windows x86_64) from this branch.

@python-cla-bot
Copy link
Copy Markdown

python-cla-bot Bot commented May 7, 2026

All commit authors signed the Contributor License Agreement.

CLA signed

@mxschmitt
Copy link
Copy Markdown
Author

Closing to validate with a local CPython build before re-opening.

@mxschmitt mxschmitt closed this May 7, 2026
@mxschmitt
Copy link
Copy Markdown
Author

Validated locally against a built CPython 3.16.0a0 from this branch: new regression test transitions FAIL -> PASS, full python -m test test_asyncio passes (2524 tests), and the end-to-end repro from the issue goes from 6/20 -> 0/60 trials. Updating PR body with the details.

@mxschmitt mxschmitt reopened this May 7, 2026
Clear _handle before calling CloseHandle so a stale handle (closed by
another code path) does not leak OSError into
_ProactorBasePipeTransport._call_connection_lost.
@mxschmitt mxschmitt force-pushed the gh-149388-pipehandle-close-idempotent branch from 798a844 to 37904e4 Compare May 7, 2026 22:17
@@ -0,0 +1,7 @@
Make :meth:`!asyncio.windows_utils.PipeHandle.close` idempotent by clearing
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

no need to explain the internals, it can be simple as "make asyncio windows pipe closing idempotent"

# subsequent close() are silent no-ops.
try:
p.close()
except OSError:
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

does this still raises?

if self._handle is not None:
CloseHandle(self._handle)
self._handle = None
handle, self._handle = self._handle, None
Copy link
Copy Markdown
Contributor

@kumaraditya303 kumaraditya303 May 8, 2026

Choose a reason for hiding this comment

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

I'd prefer to keep it on two lines, it makes reasoning about the order easier.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

asyncio: _ProactorBasePipeTransport._call_connection_lost leaks OSError [WinError 6] from PipeHandle.close() on Windows

2 participants