Curiously this doesn't seem to affect Python 3.6.8 (running on 3.6 requires a change to the stop_asyncio_loop function, switching to asyncio.Task.all_tasks instead of asyncio.all_tasks).
Instead of an indefinite hang, a message is printed akin to Unknown child process pid 37535, will report returncode 255
That looks like a nasty problem. Do you want to help by checking the logic of asyncio signals and subprocess creation? There might even be a simple fix.
I don't currently have the bandwidth personally or professionally at the moment unfortunately. :(
I took a quick look through the create_subprocess_exec logic before reporting the issue, and didn't see anything that jumped out to me, but that doesn't mean there's nothing there.
I think the same although I don't think it's that simple, we possibly need to rework and audit whole cancelation of subprocess, I am pretty sure it is broken at more places than this.
I think the fix is to use:
self._waiter = waiter
self._task = task = self._loop.create_task(self._connect_pipes())
task.add_done_callback(self._wake_waiter_and_call_pending_calls_or_close)
I'm not so sure. This might fix the problem now, but IMO lead to code that's hard to maintain. If, say, connect_pipes task in time gets some other code that mustn't be cancelled and instead should be put into the callback.
In general, I think that separating logic between async/await code and done callbacks is a big antipattern.
Instead I'd think about shielding the connect_pipes/init code from cancellation, or handling it explicitly.
#140805 introduced a race condition; test_cancel_post_init captured the race condition in MagicStack/uvloop#709, but not in cpython because the unhandled exception in event loop is not failing the test_subprocess tests. Here's a patch to reveal this race condition:
This patch is not a PR because it will make the test suite flaky. I shall loop back for a proper fix of the race condition if this wasn't fixed by then.
This patch is not a PR because it will make the test suite flaky. I shall loop back for a proper fix of the race condition if this wasn't fixed by then.
Activity
DarkArc commented on Apr 25, 2023
Curiously this doesn't seem to affect Python 3.6.8 (running on 3.6 requires a change to the stop_asyncio_loop function, switching to asyncio.Task.all_tasks instead of asyncio.all_tasks).
Instead of an indefinite hang, a message is printed akin to Unknown child process pid 37535, will report returncode 255
gvanrossum commented on Apr 26, 2023
That looks like a nasty problem. Do you want to help by checking the logic of asyncio signals and subprocess creation? There might even be a simple fix.
DarkArc commented on May 1, 2023
I don't currently have the bandwidth personally or professionally at the moment unfortunately. :(
I took a quick look through the create_subprocess_exec logic before reporting the issue, and didn't see anything that jumped out to me, but that doesn't mean there's nothing there.
gvanrossum commented on May 2, 2023
I’m in a similar situation, so we‘ll have to leave this open for a while.
graingert commented on Oct 19, 2024
Copying my analysis from: #125502 (comment)
the problem occurs when asyncio.runners._cancel_all_tasks is run at an inopportune instant when connecting pipes:
This task gets cancelled:
cpython/Lib/asyncio/base_subprocess.py
Line 56 in 92af191
which means self._pending_calls is never run:
cpython/Lib/asyncio/base_subprocess.py
Lines 199 to 202 in 92af191
so when _try_finish appends self._call_connection_lost to self._pending_calls:
cpython/Lib/asyncio/base_subprocess.py
Line 257 in 92af191
call_connection_lost is never called, which means self._exit_waiters are never woken:
cpython/Lib/asyncio/base_subprocess.py
Lines 259 to 270 in 92af191
Here's a demo that hangs every time for me:
graingert commented on Oct 19, 2024
@kumaraditya303 I think the fix is to use:
Then in def _wake_waiter_and_call_pending_calls_or_close(self, task): ... check the exception/done/cancelled state of task and do the right thing
I've spent a little time fiddling around but can't get the tests to pass and not spray a bunch of errors!
graingert commented on Oct 21, 2024
Are these the same issue?
#115787
#105288
savannahostrowski commented on Oct 21, 2024
@graingert Happy to take a look at this if no one else has started!
kumaraditya303 commented on Oct 24, 2024
I think the same although I don't think it's that simple, we possibly need to rework and audit whole cancelation of subprocess, I am pretty sure it is broken at more places than this.
1st1 commented on Oct 29, 2024
@graingert @kumaraditya303
I'm not so sure. This might fix the problem now, but IMO lead to code that's hard to maintain. If, say, connect_pipes task in time gets some other code that mustn't be cancelled and instead should be put into the callback.
In general, I think that separating logic between async/await code and done callbacks is a big antipattern.
Instead I'd think about shielding the connect_pipes/init code from cancellation, or handling it explicitly.
12 remaining items
gh-103847: fix cancellation safety of `asyncio.create_subprocess_exec` (
pythongh-103847: fix cancellation safety of `asyncio.create_subproces…
pythongh-103847: fix cancellation safety of `asyncio.create_subproces…
[3.14] gh-103847: fix cancellation safety of `asyncio.create_subproce…
[3.13] gh-103847: fix cancellation safety of `asyncio.create_subproce…
pythongh-103847: fix cancellation safety of `asyncio.create_subproces…
fantix commented on Jan 12, 2026
#140805 introduced a race condition; test_cancel_post_init captured the race condition in MagicStack/uvloop#709, but not in cpython because the unhandled exception in event loop is not failing the test_subprocess tests. Here's a patch to reveal this race condition:
0001-gh-103847-catch-unhandled-exc-for-test_subprocess.patch
This patch is not a PR because it will make the test suite flaky. I shall loop back for a proper fix of the race condition if this wasn't fixed by then.
test: skip test_shell_orphan_reaped on Python 3.12 due to asyncio bug
kumaraditya303 commented on Mar 28, 2026
@fantix I think it is fixed now with #145541.
gh-103847: fix some asyncio subprocess cancellation bugs (#146571)
[3.14] gh-103847: fix some asyncio subprocess cancellation bugs (GH-1…
[3.15] gh-103847: fix some asyncio subprocess cancellation bugs (GH-1…