mirror of
https://github.com/python/cpython.git
synced 2026-06-07 02:11:41 +00:00
gh-149388: Make asyncio PipeHandle.close idempotent (#149518)
This commit is contained in:
parent
9587726a3e
commit
7241f2739c
3 changed files with 27 additions and 1 deletions
|
|
@ -111,8 +111,9 @@ def fileno(self):
|
|||
|
||||
def close(self, *, CloseHandle=_winapi.CloseHandle):
|
||||
if self._handle is not None:
|
||||
CloseHandle(self._handle)
|
||||
handle = self._handle
|
||||
self._handle = None
|
||||
CloseHandle(handle)
|
||||
|
||||
def __del__(self, _warn=warnings.warn):
|
||||
if self._handle is not None:
|
||||
|
|
|
|||
|
|
@ -77,6 +77,30 @@ def test_pipe_handle(self):
|
|||
else:
|
||||
raise RuntimeError('expected ERROR_INVALID_HANDLE')
|
||||
|
||||
def test_pipe_handle_close_after_external_close(self):
|
||||
# gh-149388: PipeHandle.close() must clear ``_handle`` before calling
|
||||
# CloseHandle so that if CloseHandle raises on a stale handle the
|
||||
# PipeHandle is still marked closed and __del__ / subsequent close()
|
||||
# calls are silent no-ops.
|
||||
h1, h2 = windows_utils.pipe(overlapped=(False, False))
|
||||
try:
|
||||
p = windows_utils.PipeHandle(h1)
|
||||
# Simulate an external close of the underlying handle (e.g.
|
||||
# a finalizer race or a concurrent close on the same object).
|
||||
_winapi.CloseHandle(p.handle)
|
||||
# First close() still propagates the OSError from CloseHandle,
|
||||
# but must clear ``_handle`` first.
|
||||
with self.assertRaises(OSError):
|
||||
p.close()
|
||||
self.assertIsNone(p.handle)
|
||||
# Second close() is a no-op.
|
||||
p.close()
|
||||
# __del__ through GC is also a silent no-op — no unraisable.
|
||||
del p
|
||||
support.gc_collect()
|
||||
finally:
|
||||
_winapi.CloseHandle(h2)
|
||||
|
||||
|
||||
class PopenTests(unittest.TestCase):
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1 @@
|
|||
Make :class:`!asyncio.windows_utils.PipeHandle` closing idempotent.
|
||||
Loading…
Add table
Add a link
Reference in a new issue