changeset: 100545:b394fc71f92a branch: 3.5 parent: 100541:4cabf12b7fc6 user: Victor Stinner date: Mon Mar 14 16:53:12 2016 +0100 files: Python/pylifecycle.c description: 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. diff -r 4cabf12b7fc6 -r b394fc71f92a Python/pylifecycle.c --- a/Python/pylifecycle.c Tue Mar 15 10:48:28 2016 +0100 +++ b/Python/pylifecycle.c Mon Mar 14 16:53:12 2016 +0100 @@ -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.