Skip to content

Commit 36d0262

Browse files
kumaraditya303miss-islington
authored andcommitted
gh-103847: fix some asyncio subprocess cancellation bugs (GH-146571)
(cherry picked from commit f429fb3) Co-authored-by: Kumar Aditya <kumaraditya@python.org>
1 parent b6f24be commit 36d0262

4 files changed

Lines changed: 18 additions & 9 deletions

File tree

Lib/asyncio/base_subprocess.py

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ def __init__(self, loop, protocol, args, shell,
2222
self._proc = None
2323
self._pid = None
2424
self._returncode = None
25-
self._exit_waiters = []
25+
self._exit_waiters = set()
2626
self._pending_calls = collections.deque()
2727
self._pipes = {}
2828
self._finished = False
@@ -211,6 +211,14 @@ async def _connect_pipes(self, waiter):
211211
except (SystemExit, KeyboardInterrupt):
212212
raise
213213
except BaseException as exc:
214+
# Close any pipes that were already connected before the
215+
# error/cancellation to avoid leaking file descriptors.
216+
for proto in self._pipes.values():
217+
if proto is not None:
218+
proto.pipe.close()
219+
for raw_pipe in (proc.stdin, proc.stdout, proc.stderr):
220+
if raw_pipe is not None:
221+
raw_pipe.close()
214222
if waiter is not None and not waiter.cancelled():
215223
waiter.set_exception(exc)
216224
else:
@@ -260,8 +268,12 @@ async def _wait(self):
260268
return self._returncode
261269

262270
waiter = self._loop.create_future()
263-
self._exit_waiters.append(waiter)
264-
return await waiter
271+
self._exit_waiters.add(waiter)
272+
try:
273+
return await waiter
274+
finally:
275+
if self._exit_waiters is not None:
276+
self._exit_waiters.discard(waiter)
265277

266278
def _try_finish(self):
267279
assert not self._finished

Lib/asyncio/unix_events.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -212,7 +212,7 @@ async def _make_subprocess_transport(self, protocol, args, shell,
212212
raise
213213
except BaseException:
214214
transp.close()
215-
await transp._wait()
215+
await tasks.shield(transp._wait())
216216
raise
217217

218218
return transp

Lib/asyncio/windows_events.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -408,7 +408,7 @@ async def _make_subprocess_transport(self, protocol, args, shell,
408408
raise
409409
except BaseException:
410410
transp.close()
411-
await transp._wait()
411+
await tasks.shield(transp._wait())
412412
raise
413413

414414
return transp

Lib/test/test_asyncio/test_subprocess.py

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
from asyncio import subprocess
1212
from test.test_asyncio import utils as test_utils
1313
from test import support
14-
from test.support import os_helper, warnings_helper, gc_collect
14+
from test.support import os_helper, gc_collect
1515

1616
if not support.has_subprocess_support:
1717
raise unittest.SkipTest("test module requires subprocess")
@@ -919,7 +919,6 @@ async def main():
919919

920920
self.loop.run_until_complete(main())
921921

922-
@warnings_helper.ignore_warnings(category=ResourceWarning)
923922
def test_subprocess_read_pipe_cancelled(self):
924923
async def main():
925924
loop = asyncio.get_running_loop()
@@ -930,7 +929,6 @@ async def main():
930929
asyncio.run(main())
931930
gc_collect()
932931

933-
@warnings_helper.ignore_warnings(category=ResourceWarning)
934932
def test_subprocess_write_pipe_cancelled(self):
935933
async def main():
936934
loop = asyncio.get_running_loop()
@@ -941,7 +939,6 @@ async def main():
941939
asyncio.run(main())
942940
gc_collect()
943941

944-
@warnings_helper.ignore_warnings(category=ResourceWarning)
945942
def test_subprocess_read_write_pipe_cancelled(self):
946943
async def main():
947944
loop = asyncio.get_running_loop()

0 commit comments

Comments
 (0)