Fix Python runtime shutdown GIL state leak - Bug 9708 - #9786
Closed
Farrell-A wants to merge 3 commits into
Closed
Conversation
Member
|
Closing for now, replied in associated issue #9708 (comment) |
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.
Description
Py.GIL()acquisition fromPythonInitializer.Shutdown().Isolator, verifies Python is shut down, forces pending finalizers, and requires both a successful child exit and an explicit completion marker.Related Issue
Fixes #9708
Motivation and Context
PythonEngine.Shutdown()manages its own GIL state. LEAN's additional GIL handle is not disposed before the Python runtime shuts down. When that handle is finalized,Py.GILState.Finalize()throws on the CLR finalizer thread and aborts an otherwise successful algorithm or test process.Removing the redundant acquisition leaves Python.NET's internally paired shutdown behavior intact and prevents the leaked handle from reaching its throwing finalizer. The regression test uses a child process because the pre-fix exception terminates the test host and cannot be caught by the parent test. The completion marker also proves that the child executed the entire shutdown/finalizer path, preventing a zero-test or premature-exit false positive.
Requires Documentation Change
No. This corrects an internal shutdown lifecycle defect without changing user-facing behavior or public APIs.
How Has This Been Tested?
Validated after merging QuantConnect
masterat9bf283685, which uses QuantConnect.pythonnet 2.0.66. Test environment: Ubuntu 26.04 x86-64, .NET SDK 10.0.112, and CPython 3.14.4 (PYTHONHOME=/usr,PYTHONNET_PYDLL=/usr/lib/x86_64-linux-gnu/libpython3.14.so.1.0).ShutdownCompletesWithoutLeakingGil: 1/1 passed with the fix. The child completed shutdown, forced pending finalizers, emitted the required completion marker, and exited normally.var pyLock = Py.GIL();made the same test fail 1/1. The child test host aborted inPython.Runtime.Py.GILState.Finalize()withGIL must always be released, and it must be released from the same thread that acquired it.and exit code 1.PythonInitializerTestsplusIsolatorTests: 8/8 passed.dotnet build QuantConnect.Lean.sln -c Release --no-restore --maxcpucount:1: succeeded with 0 errors (existing repository warnings remain).The complete repository test suite was not run locally.
Types of changes
Checklist:
bug-<issue#>-<description>orfeature-<issue#>-<description>