[Lldb-commits] [lldb] r250306 - Fix Python initialization for Python 3.

Zachary Turner via lldb-commits lldb-commits at lists.llvm.org
Wed Oct 14 10:51:29 PDT 2015


Author: zturner
Date: Wed Oct 14 12:51:29 2015
New Revision: 250306

URL: http://llvm.org/viewvc/llvm-project?rev=250306&view=rev
Log:
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.

Modified:
    lldb/trunk/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPython.cpp

Modified: lldb/trunk/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPython.cpp
URL: http://llvm.org/viewvc/llvm-project/lldb/trunk/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPython.cpp?rev=250306&r1=250305&r2=250306&view=diff
==============================================================================
--- lldb/trunk/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPython.cpp (original)
+++ lldb/trunk/source/Plugins/ScriptInterpreter/Python/ScriptInterpreterPython.cpp Wed Oct 14 12:51:29 2015
@@ -80,27 +80,101 @@ static ScriptInterpreterPython::SWIGPyth
 
 static bool g_initialized = false;
 
-#if PY_MAJOR_VERSION >= 3 && defined(LLDB_PYTHON_HOME)
-typedef wchar_t PythonHomeChar;
+namespace
+{
+
+// 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
-typedef char PythonHomeChar;
+        InitializeThreadsPrivate();
+        Py_InitializeEx(0);
 #endif
+    }
 
-PythonHomeChar *
-GetDesiredPythonHome()
-{
+    ~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 PY_MAJOR_VERSION >= 3
-    size_t size = 0;
-    static PythonHomeChar *g_python_home = Py_DecodeLocale(LLDB_PYTHON_HOME, &size);
-    return g_python_home;
+        size_t size = 0;
+        static wchar_t *g_python_home = Py_DecodeLocale(LLDB_PYTHON_HOME, &size);
 #else
-    static PythonHomeChar *g_python_home = LLDB_PYTHON_HOME;
-    return g_python_home;
+        static char *g_python_home = LLDB_PYTHON_HOME;
 #endif
-#else
-    return nullptr;
+        Py_SetPythonHome(g_python_home);
 #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);
@@ -3104,28 +3178,10 @@ ScriptInterpreterPython::InitializePriva
 
     Timer scoped_timer (__PRETTY_FUNCTION__, __PRETTY_FUNCTION__);
 
-    // Python will muck with STDIN terminal state, so save off any current TTY
-    // settings so we can restore them.
-    TerminalState stdin_tty_state;
-    stdin_tty_state.Save(STDIN_FILENO, false);
-
-#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);
+    // RAII-based initialization which correctly handles multiple-initialization, version-
+    // specific differences among Python 2 and Python 3, and saving and restoring various
+    // other pieces of state that can get mucked with during initialization.
+    InitializePythonRAII initialize_guard;
 
     if (g_swig_init_callback)
         g_swig_init_callback ();
@@ -3146,17 +3202,6 @@ ScriptInterpreterPython::InitializePriva
         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");
-
-    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




More information about the lldb-commits mailing list