Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 791da1c

Browse files
committed
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.
1 parent 34be807 commit 791da1c

1 file changed

Lines changed: 50 additions & 34 deletions

File tree

Python/pylifecycle.c

Lines changed: 50 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -1267,31 +1267,62 @@ initstdio(void)
12671267
}
12681268

12691269

1270+
static void
1271+
_Py_FatalError_DumpTracebacks(int fd)
1272+
{
1273+
PyThreadState *tstate;
1274+
1275+
#ifdef WITH_THREAD
1276+
/* PyGILState_GetThisThreadState() works even if the GIL was released */
1277+
tstate = PyGILState_GetThisThreadState();
1278+
#else
1279+
tstate = PyThreadState_GET();
1280+
#endif
1281+
if (tstate == NULL) {
1282+
/* _Py_DumpTracebackThreads() requires the thread state to display
1283+
* frames */
1284+
return;
1285+
}
1286+
1287+
fputc('\n', stderr);
1288+
fflush(stderr);
1289+
1290+
/* display the current Python stack */
1291+
_Py_DumpTracebackThreads(fd, tstate->interp, tstate);
1292+
}
1293+
12701294
/* Print the current exception (if an exception is set) with its traceback,
1271-
* or display the current Python stack.
1272-
*
1273-
* Don't call PyErr_PrintEx() and the except hook, because Py_FatalError() is
1274-
* called on catastrophic cases. */
1295+
or display the current Python stack.
12751296
1276-
static void
1277-
_Py_PrintFatalError(int fd)
1297+
Don't call PyErr_PrintEx() and the except hook, because Py_FatalError() is
1298+
called on catastrophic cases.
1299+
1300+
Return 1 if the traceback was displayed, 0 otherwise. */
1301+
1302+
static int
1303+
_Py_FatalError_PrintExc(int fd)
12781304
{
12791305
PyObject *ferr, *res;
12801306
PyObject *exception, *v, *tb;
12811307
int has_tb;
1282-
PyThreadState *tstate;
1308+
1309+
if (PyThreadState_GET() == NULL) {
1310+
/* The GIL is released: trying to acquire it is likely to deadlock,
1311+
just give up. */
1312+
return 0;
1313+
}
12831314

12841315
PyErr_Fetch(&exception, &v, &tb);
12851316
if (exception == NULL) {
12861317
/* No current exception */
1287-
goto display_stack;
1318+
return 0;
12881319
}
12891320

12901321
ferr = _PySys_GetObjectId(&PyId_stderr);
12911322
if (ferr == NULL || ferr == Py_None) {
12921323
/* sys.stderr is not set yet or set to None,
12931324
no need to try to display the exception */
1294-
goto display_stack;
1325+
return 0;
12951326
}
12961327

12971328
PyErr_NormalizeException(&exception, &v, &tb);
@@ -1302,7 +1333,7 @@ _Py_PrintFatalError(int fd)
13021333
PyException_SetTraceback(v, tb);
13031334
if (exception == NULL) {
13041335
/* PyErr_NormalizeException() failed */
1305-
goto display_stack;
1336+
return 0;
13061337
}
13071338

13081339
has_tb = (tb != Py_None);
@@ -1318,28 +1349,9 @@ _Py_PrintFatalError(int fd)
13181349
else
13191350
Py_DECREF(res);
13201351

1321-
if (has_tb)
1322-
return;
1323-
1324-
display_stack:
1325-
#ifdef WITH_THREAD
1326-
/* PyGILState_GetThisThreadState() works even if the GIL was released */
1327-
tstate = PyGILState_GetThisThreadState();
1328-
#else
1329-
tstate = PyThreadState_GET();
1330-
#endif
1331-
if (tstate == NULL) {
1332-
/* _Py_DumpTracebackThreads() requires the thread state to display
1333-
* frames */
1334-
return;
1335-
}
1336-
1337-
fputc('\n', stderr);
1338-
fflush(stderr);
1339-
1340-
/* display the current Python stack */
1341-
_Py_DumpTracebackThreads(fd, tstate->interp, tstate);
1352+
return has_tb;
13421353
}
1354+
13431355
/* Print fatal error message and abort */
13441356

13451357
void
@@ -1365,10 +1377,14 @@ Py_FatalError(const char *msg)
13651377

13661378
/* Print the exception (if an exception is set) with its traceback,
13671379
* or display the current Python stack. */
1368-
_Py_PrintFatalError(fd);
1380+
if (!_Py_FatalError_PrintExc(fd))
1381+
_Py_FatalError_DumpTracebacks(fd);
13691382

1370-
/* Flush sys.stdout and sys.stderr */
1371-
flush_std_files();
1383+
/* Check if the current Python thread hold the GIL */
1384+
if (PyThreadState_GET() != NULL) {
1385+
/* Flush sys.stdout and sys.stderr */
1386+
flush_std_files();
1387+
}
13721388

13731389
/* The main purpose of faulthandler is to display the traceback. We already
13741390
* did our best to display it. So faulthandler can now be disabled.

0 commit comments

Comments
 (0)