[Python-checkins] cpython (3.5): Fix Py_FatalError() if called without the GIL

victor.stinner python-checkins at python.org
Tue Mar 15 12:26:08 EDT 2016


https://hg.python.org/cpython/rev/b394fc71f92a
changeset:   100545:b394fc71f92a
branch:      3.5
parent:      100541:4cabf12b7fc6
user:        Victor Stinner <victor.stinner at gmail.com>
date:        Mon Mar 14 16:53:12 2016 +0100
summary:
  Fix Py_FatalError() if called without the GIL

Issue #26558: If Py_FatalError() is called without the GIL, don't try to print
the current exception, nor try to flush stdout and stderr: only dump the
traceback of Python threads.

files:
  Python/pylifecycle.c |  124 +++++++++++++++++-------------
  1 files changed, 70 insertions(+), 54 deletions(-)


diff --git a/Python/pylifecycle.c b/Python/pylifecycle.c
--- a/Python/pylifecycle.c
+++ b/Python/pylifecycle.c
@@ -1241,61 +1241,11 @@
 }
 
 
-/* Print the current exception (if an exception is set) with its traceback,
- * or display the current Python stack.
- *
- * Don't call PyErr_PrintEx() and the except hook, because Py_FatalError() is
- * called on catastrophic cases. */
-
 static void
-_Py_PrintFatalError(int fd)
+_Py_FatalError_DumpTracebacks(int fd)
 {
-    PyObject *ferr, *res;
-    PyObject *exception, *v, *tb;
-    int has_tb;
     PyThreadState *tstate;
 
-    PyErr_Fetch(&exception, &v, &tb);
-    if (exception == NULL) {
-        /* No current exception */
-        goto display_stack;
-    }
-
-    ferr = _PySys_GetObjectId(&PyId_stderr);
-    if (ferr == NULL || ferr == Py_None) {
-        /* sys.stderr is not set yet or set to None,
-           no need to try to display the exception */
-        goto display_stack;
-    }
-
-    PyErr_NormalizeException(&exception, &v, &tb);
-    if (tb == NULL) {
-        tb = Py_None;
-        Py_INCREF(tb);
-    }
-    PyException_SetTraceback(v, tb);
-    if (exception == NULL) {
-        /* PyErr_NormalizeException() failed */
-        goto display_stack;
-    }
-
-    has_tb = (tb != Py_None);
-    PyErr_Display(exception, v, tb);
-    Py_XDECREF(exception);
-    Py_XDECREF(v);
-    Py_XDECREF(tb);
-
-    /* sys.stderr may be buffered: call sys.stderr.flush() */
-    res = _PyObject_CallMethodId(ferr, &PyId_flush, "");
-    if (res == NULL)
-        PyErr_Clear();
-    else
-        Py_DECREF(res);
-
-    if (has_tb)
-        return;
-
-display_stack:
 #ifdef WITH_THREAD
     /* PyGILState_GetThisThreadState() works even if the GIL was released */
     tstate = PyGILState_GetThisThreadState();
@@ -1314,6 +1264,68 @@
     /* display the current Python stack */
     _Py_DumpTracebackThreads(fd, tstate->interp, tstate);
 }
+
+/* Print the current exception (if an exception is set) with its traceback,
+   or display the current Python stack.
+
+   Don't call PyErr_PrintEx() and the except hook, because Py_FatalError() is
+   called on catastrophic cases.
+
+   Return 1 if the traceback was displayed, 0 otherwise. */
+
+static int
+_Py_FatalError_PrintExc(int fd)
+{
+    PyObject *ferr, *res;
+    PyObject *exception, *v, *tb;
+    int has_tb;
+
+    if (PyThreadState_GET() == NULL) {
+        /* The GIL is released: trying to acquire it is likely to deadlock,
+           just give up. */
+        return 0;
+    }
+
+    PyErr_Fetch(&exception, &v, &tb);
+    if (exception == NULL) {
+        /* No current exception */
+        return 0;
+    }
+
+    ferr = _PySys_GetObjectId(&PyId_stderr);
+    if (ferr == NULL || ferr == Py_None) {
+        /* sys.stderr is not set yet or set to None,
+           no need to try to display the exception */
+        return 0;
+    }
+
+    PyErr_NormalizeException(&exception, &v, &tb);
+    if (tb == NULL) {
+        tb = Py_None;
+        Py_INCREF(tb);
+    }
+    PyException_SetTraceback(v, tb);
+    if (exception == NULL) {
+        /* PyErr_NormalizeException() failed */
+        return 0;
+    }
+
+    has_tb = (tb != Py_None);
+    PyErr_Display(exception, v, tb);
+    Py_XDECREF(exception);
+    Py_XDECREF(v);
+    Py_XDECREF(tb);
+
+    /* sys.stderr may be buffered: call sys.stderr.flush() */
+    res = _PyObject_CallMethodId(ferr, &PyId_flush, "");
+    if (res == NULL)
+        PyErr_Clear();
+    else
+        Py_DECREF(res);
+
+    return has_tb;
+}
+
 /* Print fatal error message and abort */
 
 void
@@ -1339,10 +1351,14 @@
 
     /* Print the exception (if an exception is set) with its traceback,
      * or display the current Python stack. */
-    _Py_PrintFatalError(fd);
+    if (!_Py_FatalError_PrintExc(fd))
+        _Py_FatalError_DumpTracebacks(fd);
 
-    /* Flush sys.stdout and sys.stderr */
-    flush_std_files();
+    /* Check if the current Python thread hold the GIL */
+    if (PyThreadState_GET() != NULL) {
+        /* Flush sys.stdout and sys.stderr */
+        flush_std_files();
+    }
 
     /* The main purpose of faulthandler is to display the traceback. We already
      * did our best to display it. So faulthandler can now be disabled.

-- 
Repository URL: https://hg.python.org/cpython


More information about the Python-checkins mailing list