Uh oh!
There was an error while loading. Please reload this page.
- Notifications
You must be signed in to change notification settings - Fork 33.9k
Description
The following simple balanced calls to Py_Initialize() / Py_FinalizeEx() trigger an error when running under Microsoft's Application Verifier on Windows:
Py_Initialize(); Py_FinalizeEx(); Py_Initialize(); Py_FinalizeEx();The above sequence causes there to be two calls to create_gil() for the main GIL, without an intervening call to destroy_gil(): the mismatched use of PyMUTEX_INIT()/PyMUTEX_FINI() (create_gil() calls PyMUTEX_INIT() and destroy_gil() calls PyMUTEX_FINI()) translates into mismatched calls to Win32 APIs InitializeCriticalSection() and DeleteCriticalSection(), which are then detected by Application Verifier.
[Note, however, that this error is not specific to Windows and the GIL is being incorrectly re-initialised without a call to PyMUTEX_FINI() on all platforms.]
The first Py_Initialize() causes a call to create_gil() on interp->ceval.gil for the main interpreter (_PyRuntime._main_interpreter), but as a result of bpo-9901, finalize_interp_delete() (as called from Py_FinalizeEx()) currently defers calling _PyEval_FiniGIL() (and thereby destroy_gil()) until the next Py_Initialize():
staticvoidfinalize_interp_delete(PyInterpreterState*interp){/* Cleanup auto-thread-state */_PyGILState_Fini(interp); /* We can't call _PyEval_FiniGIL() here because destroying the GIL lock can fail when it is being awaited by another running daemon thread (see bpo-9901). Instead pycore_create_interpreter() destroys the previously created GIL, which ensures that Py_Initialize / Py_FinalizeEx can be called multiple times. */Unfortunately, the second call Py_Initialize() doesn't call destroy_gil() before calling create_gil() a second time on the main interpreter's GIL, causing this error.
@ericsnowcurrently's comment on init_interp_create_gil() ('XXX This is broken with a per-interpreter GIL'):
staticPyStatusinit_interp_create_gil(PyThreadState*tstate, intown_gil){PyStatusstatus; /* finalize_interp_delete() comment explains why _PyEval_FiniGIL() is only called here. */// XXX This is broken with a per-interpreter GIL._PyEval_FiniGIL(tstate->interp);notes that the call to _PyEval_FiniGIL() in init_interp_create_gil() doesn't work with per-interpreter GIL, but it's worse than this as it's broken with just re-creating the main interpreter!
The reason for all this appears to be that the GIL is re-initialised by _PyRuntime_Initialize(), before _PyEval_FiniGIL() has a chance to get its hands on it. Prior to GH-104210 from @ericsnowcurrently landing yesterday (and on previous Python versions), _PyEval_FiniGIL() would detect that the GIL is reinitialised by a prior call to _gil_initialize() within _PyRuntime_Initialize(). However, now the main interpreter (_PyRuntime._main_interpreter) has a GIL pointer (interp->ceval.gil) which is cleared when _PyRuntimeState_Init() resets the runtime to _PyRuntimeState_INIT:
if (runtime->_initialized){// Py_Initialize() must be running again.// Reset to _PyRuntimeState_INIT.memcpy(runtime, &initial, sizeof(*runtime))}I have reproduced this with recent cpython commit bf89d4283a28dd00836f2c312a9255f543f93fc7 and have attached a log of the callstacks (avrf-gil.txt).
Linked PRs
Metadata
Metadata
Assignees
Labels
Projects
Status