summaryrefslogtreecommitdiffstats
path: root/Python/pylifecycle.c
diff options
context:
space:
mode:
authorVictor Stinner <victor.stinner@gmail.com>2016-03-14 15:53:12 (GMT)
committerVictor Stinner <victor.stinner@gmail.com>2016-03-14 15:53:12 (GMT)
commit791da1cc264574f8f3e44570d0fce293f755fdf3 (patch)
tree6a9cc7d7e834039e74571cd77b33a4e30cfc784e /Python/pylifecycle.c
parent34be807ca4dfecc5b0a9e577a48535e738dce32b (diff)
downloadcpython-791da1cc264574f8f3e44570d0fce293f755fdf3.zip
cpython-791da1cc264574f8f3e44570d0fce293f755fdf3.tar.gz
cpython-791da1cc264574f8f3e44570d0fce293f755fdf3.tar.bz2
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.
Diffstat (limited to 'Python/pylifecycle.c')
-rw-r--r--Python/pylifecycle.c84
1 files changed, 50 insertions, 34 deletions
diff --git a/Python/pylifecycle.c b/Python/pylifecycle.c
index 715a547..aaf5811 100644
--- a/Python/pylifecycle.c
+++ b/Python/pylifecycle.c
@@ -1267,31 +1267,62 @@ initstdio(void)
}
+static void
+_Py_FatalError_DumpTracebacks(int fd)
+{
+ PyThreadState *tstate;
+
+#ifdef WITH_THREAD
+ /* PyGILState_GetThisThreadState() works even if the GIL was released */
+ tstate = PyGILState_GetThisThreadState();
+#else
+ tstate = PyThreadState_GET();
+#endif
+ if (tstate == NULL) {
+ /* _Py_DumpTracebackThreads() requires the thread state to display
+ * frames */
+ return;
+ }
+
+ fputc('\n', stderr);
+ fflush(stderr);
+
+ /* 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. */
+ or display the current Python stack.
-static void
-_Py_PrintFatalError(int fd)
+ 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;
- PyThreadState *tstate;
+
+ 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 */
- goto display_stack;
+ 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 */
- goto display_stack;
+ return 0;
}
PyErr_NormalizeException(&exception, &v, &tb);
@@ -1302,7 +1333,7 @@ _Py_PrintFatalError(int fd)
PyException_SetTraceback(v, tb);
if (exception == NULL) {
/* PyErr_NormalizeException() failed */
- goto display_stack;
+ return 0;
}
has_tb = (tb != Py_None);
@@ -1318,28 +1349,9 @@ _Py_PrintFatalError(int fd)
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();
-#else
- tstate = PyThreadState_GET();
-#endif
- if (tstate == NULL) {
- /* _Py_DumpTracebackThreads() requires the thread state to display
- * frames */
- return;
- }
-
- fputc('\n', stderr);
- fflush(stderr);
-
- /* display the current Python stack */
- _Py_DumpTracebackThreads(fd, tstate->interp, tstate);
+ return has_tb;
}
+
/* Print fatal error message and abort */
void
@@ -1365,10 +1377,14 @@ Py_FatalError(const char *msg)
/* 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.