forked from OSchip/llvm-project
Fix Python initialization for Python 3.
Python 3 reverses the order in which you must call Py_InitializeEx and PyEval_InitThreads. Since that log is in itself already a little nuanced, it is refactored into a function so that the reversal is more clear. At the same time, there's a lot of logic during Python initialization to save off a bunch of state and then restore it after initialization is complete. To express this more cleanly, it is refactored to an RAII-style pattern where state is saved off on acquisition and restored on release. llvm-svn: 250306
This commit is contained in:
parent
4bca0bb010
commit
079fe48a1d
|
@ -80,27 +80,101 @@ static ScriptInterpreterPython::SWIGPythonCallThreadPlan g_swig_call_thread_plan
|
||||||
|
|
||||||
static bool g_initialized = false;
|
static bool g_initialized = false;
|
||||||
|
|
||||||
#if PY_MAJOR_VERSION >= 3 && defined(LLDB_PYTHON_HOME)
|
namespace
|
||||||
typedef wchar_t PythonHomeChar;
|
|
||||||
#else
|
|
||||||
typedef char PythonHomeChar;
|
|
||||||
#endif
|
|
||||||
|
|
||||||
PythonHomeChar *
|
|
||||||
GetDesiredPythonHome()
|
|
||||||
{
|
{
|
||||||
|
|
||||||
|
// Initializing Python is not a straightforward process. We cannot control what
|
||||||
|
// external code may have done before getting to this point in LLDB, including
|
||||||
|
// potentially having already initialized Python, so we need to do a lot of work
|
||||||
|
// to ensure that the existing state of the system is maintained across our
|
||||||
|
// initialization. We do this by using an RAII pattern where we save off initial
|
||||||
|
// state at the beginning, and restore it at the end
|
||||||
|
struct InitializePythonRAII
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
InitializePythonRAII() :
|
||||||
|
m_was_already_initialized(false),
|
||||||
|
m_gil_state(PyGILState_UNLOCKED)
|
||||||
|
{
|
||||||
|
// Python will muck with STDIN terminal state, so save off any current TTY
|
||||||
|
// settings so we can restore them.
|
||||||
|
m_stdin_tty_state.Save(STDIN_FILENO, false);
|
||||||
|
|
||||||
|
InitializePythonHome();
|
||||||
|
|
||||||
|
// Python < 3.2 and Python >= 3.2 reversed the ordering requirements for
|
||||||
|
// calling `Py_Initialize` and `PyEval_InitThreads`. < 3.2 requires that you
|
||||||
|
// call `PyEval_InitThreads` first, and >= 3.2 requires that you call it last.
|
||||||
|
#if (PY_MAJOR_VERSION == 3 && PY_MINOR_VERSION >= 2) || (PY_MAJOR_VERSION > 3)
|
||||||
|
Py_InitializeEx(0);
|
||||||
|
InitializeThreadsPrivate();
|
||||||
|
#else
|
||||||
|
InitializeThreadsPrivate();
|
||||||
|
Py_InitializeEx(0);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
~InitializePythonRAII()
|
||||||
|
{
|
||||||
|
if (m_was_already_initialized)
|
||||||
|
{
|
||||||
|
Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_SCRIPT | LIBLLDB_LOG_VERBOSE));
|
||||||
|
|
||||||
|
if (log)
|
||||||
|
{
|
||||||
|
log->Printf("Releasing PyGILState. Returning to state = %slocked\n",
|
||||||
|
m_was_already_initialized == PyGILState_UNLOCKED ? "un" : "");
|
||||||
|
}
|
||||||
|
PyGILState_Release(m_gil_state);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// We initialized the threads in this function, just unlock the GIL.
|
||||||
|
PyEval_SaveThread();
|
||||||
|
}
|
||||||
|
|
||||||
|
m_stdin_tty_state.Restore();
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
void InitializePythonHome()
|
||||||
|
{
|
||||||
#if defined(LLDB_PYTHON_HOME)
|
#if defined(LLDB_PYTHON_HOME)
|
||||||
#if PY_MAJOR_VERSION >= 3
|
#if PY_MAJOR_VERSION >= 3
|
||||||
size_t size = 0;
|
size_t size = 0;
|
||||||
static PythonHomeChar *g_python_home = Py_DecodeLocale(LLDB_PYTHON_HOME, &size);
|
static wchar_t *g_python_home = Py_DecodeLocale(LLDB_PYTHON_HOME, &size);
|
||||||
return g_python_home;
|
|
||||||
#else
|
#else
|
||||||
static PythonHomeChar *g_python_home = LLDB_PYTHON_HOME;
|
static char *g_python_home = LLDB_PYTHON_HOME;
|
||||||
return g_python_home;
|
|
||||||
#endif
|
#endif
|
||||||
#else
|
Py_SetPythonHome(g_python_home);
|
||||||
return nullptr;
|
|
||||||
#endif
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
void InitializeThreadsPrivate()
|
||||||
|
{
|
||||||
|
if (PyEval_ThreadsInitialized())
|
||||||
|
{
|
||||||
|
Log *log(lldb_private::GetLogIfAllCategoriesSet(LIBLLDB_LOG_SCRIPT | LIBLLDB_LOG_VERBOSE));
|
||||||
|
|
||||||
|
m_was_already_initialized = true;
|
||||||
|
m_gil_state = PyGILState_Ensure();
|
||||||
|
if (log)
|
||||||
|
{
|
||||||
|
log->Printf("Ensured PyGILState. Previous state = %slocked\n",
|
||||||
|
m_gil_state == PyGILState_UNLOCKED ? "un" : "");
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// InitThreads acquires the GIL if it hasn't been called before.
|
||||||
|
PyEval_InitThreads();
|
||||||
|
}
|
||||||
|
|
||||||
|
TerminalState m_stdin_tty_state;
|
||||||
|
PyGILState_STATE m_gil_state;
|
||||||
|
bool m_was_already_initialized;
|
||||||
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static std::string ReadPythonBacktrace(PythonObject py_backtrace);
|
static std::string ReadPythonBacktrace(PythonObject py_backtrace);
|
||||||
|
@ -3104,28 +3178,10 @@ ScriptInterpreterPython::InitializePrivate ()
|
||||||
|
|
||||||
Timer scoped_timer (__PRETTY_FUNCTION__, __PRETTY_FUNCTION__);
|
Timer scoped_timer (__PRETTY_FUNCTION__, __PRETTY_FUNCTION__);
|
||||||
|
|
||||||
// Python will muck with STDIN terminal state, so save off any current TTY
|
// RAII-based initialization which correctly handles multiple-initialization, version-
|
||||||
// settings so we can restore them.
|
// specific differences among Python 2 and Python 3, and saving and restoring various
|
||||||
TerminalState stdin_tty_state;
|
// other pieces of state that can get mucked with during initialization.
|
||||||
stdin_tty_state.Save(STDIN_FILENO, false);
|
InitializePythonRAII initialize_guard;
|
||||||
|
|
||||||
#if defined(LLDB_PYTHON_HOME)
|
|
||||||
Py_SetPythonHome(GetDesiredPythonHome());
|
|
||||||
#endif
|
|
||||||
|
|
||||||
PyGILState_STATE gstate;
|
|
||||||
Log *log (lldb_private::GetLogIfAllCategoriesSet (LIBLLDB_LOG_SCRIPT | LIBLLDB_LOG_VERBOSE));
|
|
||||||
bool threads_already_initialized = false;
|
|
||||||
if (PyEval_ThreadsInitialized ()) {
|
|
||||||
gstate = PyGILState_Ensure ();
|
|
||||||
if (log)
|
|
||||||
log->Printf("Ensured PyGILState. Previous state = %slocked\n", gstate == PyGILState_UNLOCKED ? "un" : "");
|
|
||||||
threads_already_initialized = true;
|
|
||||||
} else {
|
|
||||||
// InitThreads acquires the GIL if it hasn't been called before.
|
|
||||||
PyEval_InitThreads ();
|
|
||||||
}
|
|
||||||
Py_InitializeEx (0);
|
|
||||||
|
|
||||||
if (g_swig_init_callback)
|
if (g_swig_init_callback)
|
||||||
g_swig_init_callback ();
|
g_swig_init_callback ();
|
||||||
|
@ -3146,17 +3202,6 @@ ScriptInterpreterPython::InitializePrivate ()
|
||||||
AddToSysPath(AddLocation::Beginning, file_spec.GetPath(false));
|
AddToSysPath(AddLocation::Beginning, file_spec.GetPath(false));
|
||||||
|
|
||||||
PyRun_SimpleString ("sys.dont_write_bytecode = 1; import lldb.embedded_interpreter; from lldb.embedded_interpreter import run_python_interpreter; from lldb.embedded_interpreter import run_one_line");
|
PyRun_SimpleString ("sys.dont_write_bytecode = 1; import lldb.embedded_interpreter; from lldb.embedded_interpreter import run_python_interpreter; from lldb.embedded_interpreter import run_one_line");
|
||||||
|
|
||||||
if (threads_already_initialized) {
|
|
||||||
if (log)
|
|
||||||
log->Printf("Releasing PyGILState. Returning to state = %slocked\n", gstate == PyGILState_UNLOCKED ? "un" : "");
|
|
||||||
PyGILState_Release (gstate);
|
|
||||||
} else {
|
|
||||||
// We initialized the threads in this function, just unlock the GIL.
|
|
||||||
PyEval_SaveThread();
|
|
||||||
}
|
|
||||||
|
|
||||||
stdin_tty_state.Restore();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
|
|
Loading…
Reference in New Issue