Устаревание PyEval_ReleaseLock
в Python привело к проблеме в нашей кодовой базе: мы хотим завершить интерпретатор Python из функции обратного вызова C, используя Py_EndInterpreter
Поэтому для этого в Python Docs сказано, что вы должны удерживайте GIL при вызове этой функции:
void Py_EndInterpreter(PyThreadState *tstate)
Уничтожить (суб-)интерпретатор, представленный данным состоянием потока. Заданное состояние потока должно быть текущим состоянием потока. См. обсуждение состояний потока ниже. Когда вызов возвращается, текущее состояние потока равно NULL. Все состояния потока, связанные с этим интерпретатором, уничтожаются. (Глобальная блокировка интерпретатора должна быть удержана перед вызовом этой функции и все еще удерживается, когда она возвращается.) Py_FinalizeEx() уничтожит все подинтерпретаторы, которые не были явно заблокированы. уничтожено в тот момент.
Здорово! Итак, мы вызываем PyEval_RestoreThread
, чтобы восстановить состояние нашего потока до потока, который мы собираемся завершить, а затем вызываем Py_EndInterpreter
.
// Acquire the GIL
PyEval_RestoreThread(thread);
// Tear down the interpreter.
Py_EndInterpreter(thread);
// Now what? We still hold the GIL and we no longer have a valid thread state.
// Previously we did PyEval_ReleaseLock here, but that is now deprecated.
В документации для PyEval_ReleaseLock
сказано, что мы должны использовать либо PyEval_SaveThread
, либо PyEval_ReleaseThread
. В документации PyEval_ReleaseThread
говорится, что состояние входного потока не должно быть NULL. Хорошо, но мы не можем передать состояние недавно удаленного потока.
PyEval_SaveThread
столкнется с утверждением отладки, если вы попытаетесь вызвать его после вызова Py_EndInterpreter
, так что это тоже не вариант.
Итак, мы в настоящее время реализовали хак, чтобы обойти эту проблему — мы сохраняем состояние потока, вызывающего Py_InitializeEx
, в глобальной переменной и переключаемся на него после вызова Py_EndInterpreter
.
// Acquire the GIL
PyEval_RestoreThread(thread);
// Tear down the interpreter.
Py_EndInterpreter(thread);
// Swap to the main thread state.
PyThreadState_Swap(g_init.thread_state_);
PyEval_SaveThread(); // Release the GIL. Probably.
Какое правильное решение здесь? Кажется, что встроенный Python — это запоздалая мысль для этого API.
Аналогичный вопрос: PyEval_InitThreads в Python 3: как/когда его вызывать? (сага продолжается до отвращения)