faulthandler now works in non-Python threads

Issue #26563:

* Add _PyGILState_GetInterpreterStateUnsafe() function: the single
  PyInterpreterState used by this process' GILState implementation.
* Enhance _Py_DumpTracebackThreads() to retrieve the interpreter state from
  autoInterpreterState in last resort. The function now accepts NULL for interp
  and current_tstate parameters.
* test_faulthandler: fix a ResourceWarning when test is interrupted by CTRL+c
diff --git a/Python/pylifecycle.c b/Python/pylifecycle.c
index 4fc6a15..41528cd 100644
--- a/Python/pylifecycle.c
+++ b/Python/pylifecycle.c
@@ -1275,25 +1275,11 @@
 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);
+    _Py_DumpTracebackThreads(fd, NULL, NULL);
 }
 
 /* Print the current exception (if an exception is set) with its traceback,
diff --git a/Python/pystate.c b/Python/pystate.c
index e8026c5..0503f32 100644
--- a/Python/pystate.c
+++ b/Python/pystate.c
@@ -714,6 +714,12 @@
     _PyGILState_NoteThreadState(t);
 }
 
+PyInterpreterState *
+_PyGILState_GetInterpreterStateUnsafe(void)
+{
+    return autoInterpreterState;
+}
+
 void
 _PyGILState_Fini(void)
 {
diff --git a/Python/traceback.c b/Python/traceback.c
index 8383c16..403dba5 100644
--- a/Python/traceback.c
+++ b/Python/traceback.c
@@ -707,11 +707,56 @@
    handlers if signals were received. */
 const char*
 _Py_DumpTracebackThreads(int fd, PyInterpreterState *interp,
-                         PyThreadState *current_thread)
+                         PyThreadState *current_tstate)
 {
     PyThreadState *tstate;
     unsigned int nthreads;
 
+#ifdef WITH_THREAD
+    if (current_tstate == NULL) {
+        /* _Py_DumpTracebackThreads() is called from signal handlers by
+           faulthandler.
+
+           SIGSEGV, SIGFPE, SIGABRT, SIGBUS and SIGILL are synchronous signals
+           and are thus delivered to the thread that caused the fault. Get the
+           Python thread state of the current thread.
+
+           PyThreadState_Get() doesn't give the state of the thread that caused
+           the fault if the thread released the GIL, and so this function
+           cannot be used. Read the thread local storage (TLS) instead: call
+           PyGILState_GetThisThreadState(). */
+        current_tstate = PyGILState_GetThisThreadState();
+    }
+
+    if (interp == NULL) {
+        if (current_tstate == NULL) {
+            interp = _PyGILState_GetInterpreterStateUnsafe();
+            if (interp == NULL) {
+                /* We need the interpreter state to get Python threads */
+                return "unable to get the interpreter state";
+            }
+        }
+        else {
+            interp = current_tstate->interp;
+        }
+    }
+#else
+    if (current_tstate == NULL) {
+        /* Call _PyThreadState_UncheckedGet() instead of PyThreadState_Get()
+           to not fail with a fatal error if the thread state is NULL. */
+        current_thread = _PyThreadState_UncheckedGet();
+    }
+
+    if (interp == NULL) {
+        if (current_tstate == NULL) {
+            /* We need the interpreter state to get Python threads */
+            return "unable to get the interpreter state";
+        }
+        interp = current_tstate->interp;
+    }
+#endif
+    assert(interp != NULL);
+
     /* Get the current interpreter from the current thread */
     tstate = PyInterpreterState_ThreadHead(interp);
     if (tstate == NULL)
@@ -729,7 +774,7 @@
             PUTS(fd, "...\n");
             break;
         }
-        write_thread_id(fd, tstate, tstate == current_thread);
+        write_thread_id(fd, tstate, tstate == current_tstate);
         dump_traceback(fd, tstate, 0);
         tstate = PyThreadState_Next(tstate);
         nthreads++;