fix(defer): propagate CancelledError into background event loop#1688
Open
no0ktheali3n wants to merge 1 commit into
Open
fix(defer): propagate CancelledError into background event loop#1688no0ktheali3n wants to merge 1 commit into
no0ktheali3n wants to merge 1 commit into
Conversation
DeferredTask.result() awaits on loop.run_in_executor(None, _get_result), where _get_result is a blocking call to self._future.result(timeout). When the outer asyncio task is cancelled (for example a wall-clock timeout in the caller fires), only the outer task receives the CancelledError. The underlying concurrent.futures.Future continues running on the singleton background event loop, and the thread-pool worker stays parked in _get_result waiting for it. Every cancelled result() leaks one worker thread until the default ThreadPoolExecutor saturates and subsequent result() calls block indefinitely. Fix: catch asyncio.CancelledError in result(), call self.kill() to cancel the underlying future and drain the singleton background event loop, then re-raise. The worker thread is released as the future resolves to CancelledError; any in-flight coroutine tasks on the background loop are cancelled too. Adds tests/test_defer_cancellation.py asserting that after a cancelled result(), the underlying future reaches done() within 500ms - a thread-leak regression guard. Reproduces in upstream main and earlier releases - same code shape.
This file contains hidden or 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
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.
DeferredTask.result() awaits on loop.run_in_executor(None, _get_result), where _get_result is a blocking call to self._future.result(timeout). When the outer asyncio task is cancelled (for example a wall-clock timeout in the caller fires), only the outer task receives the CancelledError. The underlying concurrent.futures.Future continues running on the singleton background event loop, and the thread-pool worker stays parked in _get_result waiting for it. Every cancelled result() leaks one worker thread until the default ThreadPoolExecutor saturates and subsequent result() calls block indefinitely.
Fix: catch asyncio.CancelledError in result(), call self.kill() to cancel the underlying future and drain the singleton background event loop, then re-raise. The worker thread is released as the future resolves to CancelledError; any in-flight coroutine tasks on the background loop are cancelled too.
Adds tests/test_defer_cancellation.py asserting that after a cancelled result(), the underlying future reaches done() within 500ms
Reproduces in upstream main and earlier releases - same code shape.