Skip to content

Commit ebadfd1

Browse files
[3.13] gh-128400: Stop-the-world when manually calling faulthandler (GH-128422) (GH-128423)
(cherry picked from commit c9356fe) Co-authored-by: Peter Bierma <[email protected]>
1 parent fa6c48e commit ebadfd1

File tree

3 files changed

+36
-1
lines changed

3 files changed

+36
-1
lines changed

Lib/test/test_faulthandler.py

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
import subprocess
88
import sys
99
from test import support
10-
from test.support import os_helper, script_helper, is_android, MS_WINDOWS
10+
from test.support import os_helper, script_helper, is_android, MS_WINDOWS, threading_helper
1111
import tempfile
1212
import unittest
1313
from textwrap import dedent
@@ -896,6 +896,34 @@ def test_cancel_later_without_dump_traceback_later(self):
896896
self.assertEqual(output, [])
897897
self.assertEqual(exitcode, 0)
898898

899+
@threading_helper.requires_working_threading()
900+
@unittest.skipUnless(support.Py_GIL_DISABLED, "only meaningful if the GIL is disabled")
901+
def test_free_threaded_dump_traceback(self):
902+
# gh-128400: Other threads need to be paused to invoke faulthandler
903+
code = dedent("""
904+
import faulthandler
905+
from threading import Thread, Event
906+
907+
class Waiter(Thread):
908+
def __init__(self):
909+
Thread.__init__(self)
910+
self.running = Event()
911+
self.stop = Event()
912+
913+
def run(self):
914+
self.running.set()
915+
self.stop.wait()
916+
917+
for _ in range(100):
918+
waiter = Waiter()
919+
waiter.start()
920+
waiter.running.wait()
921+
faulthandler.dump_traceback(all_threads=True)
922+
waiter.stop.set()
923+
waiter.join()
924+
""")
925+
_, exitcode = self.get_output(code)
926+
self.assertEqual(exitcode, 0)
899927

900928
if __name__ == "__main__":
901929
unittest.main()
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
Fix crash when using :func:`faulthandler.dump_traceback` while other threads
2+
are active on the :term:`free threaded <free threading>` build.

Modules/faulthandler.c

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -237,7 +237,12 @@ faulthandler_dump_traceback_py(PyObject *self,
237237
return NULL;
238238

239239
if (all_threads) {
240+
PyInterpreterState *interp = _PyInterpreterState_GET();
241+
/* gh-128400: Accessing other thread states while they're running
242+
* isn't safe if those threads are running. */
243+
_PyEval_StopTheWorld(interp);
240244
errmsg = _Py_DumpTracebackThreads(fd, NULL, tstate);
245+
_PyEval_StartTheWorld(interp);
241246
if (errmsg != NULL) {
242247
PyErr_SetString(PyExc_RuntimeError, errmsg);
243248
return NULL;

0 commit comments

Comments
 (0)