bpo-43406: Fix possible race condition where ``PyErr_CheckSignals`` tries to execute a non-Python signal handler (GH-24756)

We can receive signals (at the C level, in `trip_signal()` in signalmodule.c) while `signal.signal` is being called to modify the corresponding handler.  Later when `PyErr_CheckSignals()` is called to handle the given signal, the handler may be a non-callable object and would raise a cryptic asynchronous exception.
diff --git a/Modules/signalmodule.c b/Modules/signalmodule.c
index 7ac797a..c6564be 100644
--- a/Modules/signalmodule.c
+++ b/Modules/signalmodule.c
@@ -1706,10 +1706,34 @@ _PyErr_CheckSignalsTstate(PyThreadState *tstate)
         }
         _Py_atomic_store_relaxed(&Handlers[i].tripped, 0);
 
+        /* Signal handlers can be modified while a signal is received,
+         * and therefore the fact that trip_signal() or PyErr_SetInterrupt()
+         * was called doesn't guarantee that there is still a Python
+         * signal handler for it by the time PyErr_CheckSignals() is called
+         * (see bpo-43406).
+         */
+        PyObject *func = Handlers[i].func;
+        if (func == NULL || func == Py_None || func == IgnoreHandler ||
+            func == DefaultHandler) {
+            /* No Python signal handler due to aforementioned race condition.
+             * We can't call raise() as it would break the assumption
+             * that PyErr_SetInterrupt() only *simulates* an incoming
+             * signal (i.e. it will never kill the process).
+             * We also don't want to interrupt user code with a cryptic
+             * asynchronous exception, so instead just write out an
+             * unraisable error.
+             */
+            PyErr_Format(PyExc_OSError,
+                         "Signal %i ignored due to race condition",
+                         i);
+            PyErr_WriteUnraisable(Py_None);
+            continue;
+        }
+
         PyObject *arglist = Py_BuildValue("(iO)", i, frame);
         PyObject *result;
         if (arglist) {
-            result = _PyObject_Call(tstate, Handlers[i].func, arglist, NULL);
+            result = _PyObject_Call(tstate, func, arglist, NULL);
             Py_DECREF(arglist);
         }
         else {