-
Notifications
You must be signed in to change notification settings - Fork 138
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Fix run_process()
and open_process().__aexit__
leaking an orphan process when cancelled
#672
Merged
Conversation
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
…process has not yet closed them
i should also note that this appears to also be how Trio implemented |
agronholm
requested changes
Jan 20, 2024
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just some formatting changes.
With pytest.mark.xfail, the test is still run and will emit xpass if/when it stops failing, i.e. it will detect when the mark.xfail should be removed. In contrast, pytest.xfail immediately force-skips the test, skipping the xpass check. The only remaining use of pytest.xfail (in TestBlockingPortal.test_from_async) needs to remain a pytest.xfail until someone gets around to agronholm#524 (comment) because it will deadlock otherwise.
agronholm
reviewed
Jan 23, 2024
How is it that the xfail test passes? Explain please? |
oops, i forgot to make the xfail conditional on the backend. fixed. |
agronholm
approved these changes
Jan 25, 2024
Thanks! |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
fixes #669.
underlying AnyIO bug
currently, cancellation of
async with await open_process()
(i.e.Process.__aexit__()
in a cancelled scope) can cause theAsyncResource
to not get fully closed, leaking an orphan process. this is because a cancelledProcess.__aexit__()
is not performing the minimum level of cleanup: Trio's cancellation model states thathow it caused #669
#669 is a special case of this bug: in #669, even though
run_process
does callprocess.kill
if cancelled, it still (very briefly) leaks an orphan process. this is because whenprocess.kill()
returns, the process is not necessarily dead yet:process.kill
(kill(2)
/TerminateProcess
) is likecall_soon
and only schedules the kill. so afterprocess.kill()
we need to wait briefly for the death to occur and for the event loop to learn about it.so the problem seen in #669 was: an orphan process was leaked briefly. this started a race between
note that this race is present on both backends. if the event loop closing happened first and won the race, then:
Popen.__del__
to emit aResourceWarning
BaseSubprocessTransport.__del__
to emit aResouceWarning
, but due to an asyncio bug (BaseSubprocessTransport.__del__
fails if the event loop is already closed, which can leak an orphan process python/cpython#114177) it caused an unraisable error instead of aResourceWarning
.the tests currently added in this PR should cover the underlying AnyIO bug that caused #669 while being deterministic (not flaky). however, if you also want a test that looks more like the original example in #669 (testing asyncio/Trio
close
&__del__
behavior), here's another test that could be added to this PR: