GeezerVison is showing github.com — Deadlock when calling PyGILState_Ensure() from a fresh C thread · Issue #96071 · python/cpython
Deadlock when calling PyGILState_Ensure() from a fresh C thread #96071Labels 3.11only security fixesonly security fixes3.12only security fixesonly security fixesinterpreter-core(Objects, Python, Grammar, and Parser dirs)(Objects, Python, Grammar, and Parser dirs)type-bugAn unexpected behavior, bug, or errorAn unexpected behavior, bug, or error DescriptionBug report
This used to work with py 3.8, 3.9 and 3.10 but now fails. The failure is due to a deadlock condition due to the move of an alloc from before a HEAD_LOCK() to after causing a recursive call to PyGILState_Ensure() which eventually deadlocks on the HEAD_LOCK().
The following is a trace of calls from the initial call to PyGILState_Ensure() to the eventual deadlock:
pystate:PyGILState_Ensure()
pystate:PyThreadState_New()
pystate:new_threadstate()
HEAD_LOCK() <-- Initial head locked, the alloc_threadstate() call below used to happen before this in previous versions of Python.
pystate:alloc_threadstate()
obmalloc:PyMem_RawCalloc()
_tracemalloc:_PyMem_Raw.calloc()
_tracemalloc:tracemalloc_raw_calloc()
get_reentrant() = 0
set_reenterant(1)
pystate:PyGILState_Ensure()
pystate:PyThreadState_New()
pystate:new_threadstate()
HEAD_LOCK() <-- DEADLOCK HERE!
Steps to reproduce: In a Python C extension module, create a C thread then try to call PyGILState_Ensure() in that thread with no previous existing Python threadstate.
Your environment
Python 3.11.0rc1
Linux tom-VirtualBox 5.15.0-46-generic #49~20.04.1-Ubuntu SMP Thu Aug 4 19:15:44 UTC 2022 x86_64 x86_64 x86_64 GNU/Linux Activityronaldoussoren commented on Aug 18, 2022 Do you have a self-contained example that demonstrates the problem? I quickly checked using PyObjC on macOS (which basically does this when starting threads using the Cocoa interface), and that code did not deadlock. tom-pytel commented on Aug 18, 2022 ContributorAuthor More actions
Do you have a self-contained example that demonstrates the problem? I quickly checked using PyObjC on macOS (which basically does this when starting threads using the Cocoa interface), and that code did not deadlock.
Ok, I investigated a little deeper and admittedly I have a bit more going on in the environment where I found this. Tried duplicating until I found the difference: tracemalloc. When tracemalloc is imported and started the function flow is as shown in the header of this issue. When it is not, then after obmalloc:PyMem_RawCalloc() it goes to obmalloc:_PyMem_DebugRawCalloc() instead of _tracemalloc:_PyMem_Raw.calloc() which avoids the recursive call to PyGILState_Ensure() and works fine. I will see about getting some poc module code here tomorrow or after. ronaldoussoren commented on Aug 18, 2022 I can reproduce using PyObjC by using python3.11 -X tracemalloc script.py to start the PyObjC using script I mentioned earlier.
@pablogsal is this a release blocker? pablogsal commented on Aug 18, 2022
@pablogsal is this a release blocker?
Indeed it is. Thanks for pinging me. pablogsal commented on Aug 18, 2022 pablogsal commented on Aug 18, 2022 Or alternatively, could you bisect the issue? tom-pytel commented on Aug 18, 2022 ContributorAuthor More actions
Or alternatively, could you bisect the issue?
In previous versions the memory for the new PyThreadState was allocated before the HEAD_LOCK() in pystate:new_threadstate(), if you revert to that behavior the bug is fixed. ronaldoussoren commented on Aug 18, 2022 The reproducer needs PyObjC (from v8.5-branch in https://github.com/ronaldoussoren/pyobjc because the latest release does not work with Python 3.11 yet).
The reproducer script:
from Cocoa import NSObject, NSThread
import time
class Runner(NSObject):
def doit_(self, arg):
print("hello world")
print(NSThread.currentThread().isMainThread())
runner = Runner.alloc().init()
thread = NSThread.alloc().initWithTarget_selector_object_(runner, b"doit:", None)
thread.start()
print(thread)
time.sleep(1)
while thread.isExecuting():
time.sleep(1)
This script hangs when using "python3.11 -X tracemalloc script.pyand works fine when using an older version of Python or leaving out-X tracemalloc``.
It is probably easier to reproduce this using a small C extension that uses pthread_create to launch a thread that call PyGILState_Ensure.
I can look into this during the weekend at the earliest, but also need to get a release of PyObjC out to fix Python 3.11 support (which is needed due to documented API changes, nothing to worry about there). pablogsal commented on Aug 18, 2022 I will prepare a patch tomorrow. I have enough information to produce a fix. ericsnowcurrently commented on Aug 22, 2022 kumaraditya303 commented on Aug 24, 2022 Closing as I have fixed both the issue and added tests to ensure no further regression. Thanks! ericsnowcurrently commented on Aug 24, 2022 added 2 commits that reference this issue on Nov 19, 2024gh-126914: Store the Preallocated Thread State's Pointer in a PyInter…
MetadataMetadata3.11only security fixesonly security fixes3.12only security fixesonly security fixesinterpreter-core(Objects, Python, Grammar, and Parser dirs)(Objects, Python, Grammar, and Parser dirs)type-bugAn unexpected behavior, bug, or errorAn unexpected behavior, bug, or error StatusShow more project fields
DevelopmentNo branches or pull requestsIssue actions
|