99#include "pycore_emscripten_signal.h" // _Py_CHECK_EMSCRIPTEN_SIGNALS
1010#include "pycore_fileutils.h" // _Py_BEGIN_SUPPRESS_IPH
1111#include "pycore_interpframe.h" // _PyThreadState_GetFrame()
12+ #include "pycore_lock.h" // _PyMutex_at_fork_reinit()
1213#include "pycore_moduleobject.h" // _PyModule_GetState()
1314#include "pycore_pyerrors.h" // _PyErr_SetString()
1415#include "pycore_pystate.h" // _PyThreadState_GET()
@@ -128,7 +129,6 @@ typedef struct {
128129 PyTypeObject * siginfo_type ;
129130} _signal_module_state ;
130131
131-
132132Py_LOCAL_INLINE (PyObject * )
133133get_handler (int i )
134134{
@@ -143,6 +143,34 @@ set_handler(int i, PyObject* func)
143143 _Py_atomic_store_ptr (& Handlers [i ].func , func );
144144}
145145
146+ /* Serialize readers of Handlers[] with signal.signal() replacing a handler:
147+ the slot's reference is handed to the caller of signal.signal(), who may
148+ release it while another thread has loaded the pointer but not yet
149+ incref'ed it. Never take this mutex from a signal handler.
150+
151+ The GIL already makes "load the handler and incref it" atomic with respect
152+ to signal.signal(), so the lock is only needed in the free-threaded build;
153+ the helpers do nothing in the default build. */
154+ #ifdef Py_GIL_DISABLED
155+ static PyMutex handlers_mutex ;
156+ #endif
157+
158+ Py_LOCAL_INLINE (void )
159+ claim_signalnum (void )
160+ {
161+ #ifdef Py_GIL_DISABLED
162+ PyMutex_Lock (& handlers_mutex );
163+ #endif
164+ }
165+
166+ Py_LOCAL_INLINE (void )
167+ release_signalnum (void )
168+ {
169+ #ifdef Py_GIL_DISABLED
170+ PyMutex_Unlock (& handlers_mutex );
171+ #endif
172+ }
173+
146174
147175static inline _signal_module_state *
148176get_signal_state (PyObject * module )
@@ -532,8 +560,10 @@ signal_signal_impl(PyObject *module, int signalnum, PyObject *handler)
532560 return NULL ;
533561 }
534562
563+ claim_signalnum ();
535564 old_handler = get_handler (signalnum );
536565 set_handler (signalnum , Py_NewRef (handler ));
566+ release_signalnum ();
537567
538568 if (old_handler != NULL ) {
539569 return old_handler ;
@@ -569,9 +599,12 @@ signal_getsignal_impl(PyObject *module, int signalnum)
569599 "signal number out of range" );
570600 return NULL ;
571601 }
572- old_handler = get_handler (signalnum );
602+ claim_signalnum ();
603+ old_handler = Py_XNewRef (get_handler (signalnum ));
604+ release_signalnum ();
605+
573606 if (old_handler != NULL ) {
574- return Py_NewRef ( old_handler ) ;
607+ return old_handler ;
575608 }
576609 else {
577610 Py_RETURN_NONE ;
@@ -1595,8 +1628,10 @@ signal_get_set_handlers(signal_state_t *state, PyObject *mod_dict)
15951628 }
15961629 // If signal_module_exec() is called more than one, we must
15971630 // clear the strong reference to the previous function.
1631+ claim_signalnum ();
15981632 PyObject * old_func = get_handler (signum );
15991633 set_handler (signum , Py_NewRef (func ));
1634+ release_signalnum ();
16001635 Py_XDECREF (old_func );
16011636 }
16021637
@@ -1609,7 +1644,9 @@ signal_get_set_handlers(signal_state_t *state, PyObject *mod_dict)
16091644 return -1 ;
16101645 }
16111646
1647+ claim_signalnum ();
16121648 set_handler (SIGINT , int_handler );
1649+ release_signalnum ();
16131650 Py_DECREF (sigint_func );
16141651 PyOS_setsig (SIGINT , signal_handler );
16151652 }
@@ -1744,9 +1781,11 @@ _PySignal_Fini(void)
17441781
17451782 // Restore default signals and clear handlers
17461783 for (int signum = 1 ; signum < Py_NSIG ; signum ++ ) {
1747- PyObject * func = get_handler (signum );
17481784 _Py_atomic_store_int_relaxed (& Handlers [signum ].tripped , 0 );
1785+ claim_signalnum ();
1786+ PyObject * func = get_handler (signum );
17491787 set_handler (signum , NULL );
1788+ release_signalnum ();
17501789 if (func != NULL
17511790 && func != Py_None
17521791 && !compare_handler (func , state -> default_handler )
@@ -2056,13 +2095,24 @@ _clear_pending_signals(void)
20562095 }
20572096}
20582097
2098+ static void
2099+ reinit_handlers_mutex (void )
2100+ {
2101+ /* Another thread may have held the mutex when fork() was called; it does
2102+ not exist in the child, so nobody would ever release it. */
2103+ #ifdef Py_GIL_DISABLED
2104+ _PyMutex_at_fork_reinit (& handlers_mutex );
2105+ #endif
2106+ }
2107+
20592108void
20602109_PySignal_AfterFork (void )
20612110{
20622111 /* Clear the signal flags after forking so that they aren't handled
20632112 * in both processes if they came in just before the fork() but before
20642113 * the interpreter had an opportunity to call the handlers. issue9535. */
20652114 _clear_pending_signals ();
2115+ reinit_handlers_mutex ();
20662116}
20672117#endif /* HAVE_FORK */
20682118
0 commit comments