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()
:
static void
finalize_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
'):
static PyStatus
init_interp_create_gil(PyThreadState *tstate, int own_gil)
{
PyStatus status;
/* 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
Projects
Status