Skip to content

Commit d2a8e5b

Browse files
authored
bpo-40010: COMPUTE_EVAL_BREAKER() checks for subinterpreter (GH-19087)
COMPUTE_EVAL_BREAKER() now also checks if the Python thread state belongs to the main interpreter. Don't break the evaluation loop if there are pending signals but the Python thread state it belongs to a subinterpeter. * Add _Py_IsMainThread() function. * Add _Py_ThreadCanHandleSignals() function.
1 parent da2914d commit d2a8e5b

File tree

5 files changed

+49
-53
lines changed

5 files changed

+49
-53
lines changed

Doc/library/signal.rst

+7-5
Original file line numberDiff line numberDiff line change
@@ -53,12 +53,12 @@ This has consequences:
5353
Signals and threads
5454
^^^^^^^^^^^^^^^^^^^
5555

56-
Python signal handlers are always executed in the main Python thread,
56+
Python signal handlers are always executed in the main Python thread of the main interpreter,
5757
even if the signal was received in another thread. This means that signals
5858
can't be used as a means of inter-thread communication. You can use
5959
the synchronization primitives from the :mod:`threading` module instead.
6060

61-
Besides, only the main thread is allowed to set a new signal handler.
61+
Besides, only the main thread of the main interpreter is allowed to set a new signal handler.
6262

6363

6464
Module contents
@@ -266,7 +266,7 @@ The :mod:`signal` module defines the following functions:
266266
same process as the caller. The target thread can be executing any code
267267
(Python or not). However, if the target thread is executing the Python
268268
interpreter, the Python signal handlers will be :ref:`executed by the main
269-
thread <signals-and-threads>`. Therefore, the only point of sending a
269+
thread of the main interpreter <signals-and-threads>`. Therefore, the only point of sending a
270270
signal to a particular Python thread would be to force a running system call
271271
to fail with :exc:`InterruptedError`.
272272

@@ -360,7 +360,8 @@ The :mod:`signal` module defines the following functions:
360360
If not -1, *fd* must be non-blocking. It is up to the library to remove
361361
any bytes from *fd* before calling poll or select again.
362362

363-
When threads are enabled, this function can only be called from the main thread;
363+
When threads are enabled, this function can only be called
364+
from :ref:`the main thread of the main interpreter <signals-and-threads>`;
364365
attempting to call it from other threads will cause a :exc:`ValueError`
365366
exception to be raised.
366367

@@ -413,7 +414,8 @@ The :mod:`signal` module defines the following functions:
413414
signal handler will be returned (see the description of :func:`getsignal`
414415
above). (See the Unix man page :manpage:`signal(2)` for further information.)
415416

416-
When threads are enabled, this function can only be called from the main thread;
417+
When threads are enabled, this function can only be called
418+
from :ref:`the main thread of the main interpreter <signals-and-threads>`;
417419
attempting to call it from other threads will cause a :exc:`ValueError`
418420
exception to be raised.
419421

Include/internal/pycore_pystate.h

+27-1
Original file line numberDiff line numberDiff line change
@@ -294,7 +294,33 @@ _PyRuntimeState_SetFinalizing(_PyRuntimeState *runtime, PyThreadState *tstate) {
294294
_Py_atomic_store_relaxed(&runtime->_finalizing, (uintptr_t)tstate);
295295
}
296296

297-
PyAPI_FUNC(int) _Py_IsMainInterpreter(PyThreadState* tstate);
297+
/* Check if the current thread is the main thread.
298+
Use _Py_IsMainInterpreter() to check if it's the main interpreter. */
299+
static inline int
300+
_Py_IsMainThread(void)
301+
{
302+
unsigned long thread = PyThread_get_thread_ident();
303+
return (thread == _PyRuntime.main_thread);
304+
}
305+
306+
307+
static inline int
308+
_Py_IsMainInterpreter(PyThreadState* tstate)
309+
{
310+
/* Use directly _PyRuntime rather than tstate->interp->runtime, since
311+
this function is used in performance critical code path (ceval) */
312+
return (tstate->interp == _PyRuntime.interpreters.main);
313+
}
314+
315+
316+
/* Only handle signals on the main thread of the main interpreter. */
317+
static inline int
318+
_Py_ThreadCanHandleSignals(PyThreadState *tstate)
319+
{
320+
/* Use directly _PyRuntime rather than tstate->interp->runtime, since
321+
this function is used in performance critical code path (ceval) */
322+
return (_Py_IsMainThread() && _Py_IsMainInterpreter(tstate));
323+
}
298324

299325

300326
/* Variable and macro for in-line access to current thread

Modules/signalmodule.c

+12-17
Original file line numberDiff line numberDiff line change
@@ -190,12 +190,10 @@ itimer_retval(struct itimerval *iv)
190190
#endif
191191

192192
static int
193-
is_main(_PyRuntimeState *runtime)
193+
thread_can_handle_signals(void)
194194
{
195-
unsigned long thread = PyThread_get_thread_ident();
196-
PyInterpreterState *interp = _PyRuntimeState_GetThreadState(runtime)->interp;
197-
return (thread == runtime->main_thread
198-
&& interp == runtime->interpreters.main);
195+
PyThreadState *tstate = _PyThreadState_GET();
196+
return _Py_ThreadCanHandleSignals(tstate);
199197
}
200198

201199
static PyObject *
@@ -482,10 +480,10 @@ signal_signal_impl(PyObject *module, int signalnum, PyObject *handler)
482480
}
483481
#endif
484482

485-
_PyRuntimeState *runtime = &_PyRuntime;
486-
if (!is_main(runtime)) {
483+
if (!thread_can_handle_signals()) {
487484
PyErr_SetString(PyExc_ValueError,
488-
"signal only works in main thread");
485+
"signal only works in main thread "
486+
"of the main interpreter");
489487
return NULL;
490488
}
491489
if (signalnum < 1 || signalnum >= NSIG) {
@@ -700,10 +698,10 @@ signal_set_wakeup_fd(PyObject *self, PyObject *args, PyObject *kwds)
700698
return NULL;
701699
#endif
702700

703-
_PyRuntimeState *runtime = &_PyRuntime;
704-
if (!is_main(runtime)) {
701+
if (!thread_can_handle_signals()) {
705702
PyErr_SetString(PyExc_ValueError,
706-
"set_wakeup_fd only works in main thread");
703+
"set_wakeup_fd only works in main thread "
704+
"of the main interpreter");
707705
return NULL;
708706
}
709707

@@ -1675,8 +1673,7 @@ finisignal(void)
16751673
int
16761674
PyErr_CheckSignals(void)
16771675
{
1678-
_PyRuntimeState *runtime = &_PyRuntime;
1679-
if (!is_main(runtime)) {
1676+
if (!thread_can_handle_signals()) {
16801677
return 0;
16811678
}
16821679

@@ -1769,8 +1766,7 @@ int
17691766
PyOS_InterruptOccurred(void)
17701767
{
17711768
if (_Py_atomic_load_relaxed(&Handlers[SIGINT].tripped)) {
1772-
_PyRuntimeState *runtime = &_PyRuntime;
1773-
if (!is_main(runtime)) {
1769+
if (!thread_can_handle_signals()) {
17741770
return 0;
17751771
}
17761772
_Py_atomic_store_relaxed(&Handlers[SIGINT].tripped, 0);
@@ -1803,8 +1799,7 @@ _PySignal_AfterFork(void)
18031799
int
18041800
_PyOS_IsMainThread(void)
18051801
{
1806-
_PyRuntimeState *runtime = &_PyRuntime;
1807-
return is_main(runtime);
1802+
return thread_can_handle_signals();
18081803
}
18091804

18101805
#ifdef MS_WINDOWS

Python/ceval.c

+3-24
Original file line numberDiff line numberDiff line change
@@ -136,14 +136,6 @@ is_tstate_valid(PyThreadState *tstate)
136136
#endif
137137

138138

139-
/* Only handle signals on the main thread of the main interpreter. */
140-
static int
141-
thread_can_handle_signals(void)
142-
{
143-
return (PyThread_get_thread_ident() == _PyRuntime.main_thread);
144-
}
145-
146-
147139
/* This can set eval_breaker to 0 even though gil_drop_request became
148140
1. We believe this is all right because the eval loop will release
149141
the GIL eventually anyway. */
@@ -156,7 +148,7 @@ COMPUTE_EVAL_BREAKER(PyThreadState *tstate,
156148
_Py_atomic_store_relaxed(&ceval2->eval_breaker,
157149
_Py_atomic_load_relaxed(&ceval->gil_drop_request)
158150
| (_Py_atomic_load_relaxed(&ceval->signals_pending)
159-
&& thread_can_handle_signals())
151+
&& _Py_ThreadCanHandleSignals(tstate))
160152
| _Py_atomic_load_relaxed(&ceval2->pending.calls_to_do)
161153
| ceval2->pending.async_exc);
162154
}
@@ -598,17 +590,7 @@ Py_AddPendingCall(int (*func)(void *), void *arg)
598590
static int
599591
handle_signals(PyThreadState *tstate)
600592
{
601-
_PyRuntimeState *runtime = tstate->interp->runtime;
602-
603-
if (!thread_can_handle_signals()) {
604-
return 0;
605-
}
606-
/*
607-
* Ensure that the thread isn't currently running some other
608-
* interpreter.
609-
*/
610-
PyInterpreterState *interp = tstate->interp;
611-
if (interp != runtime->interpreters.main) {
593+
if (!_Py_ThreadCanHandleSignals(tstate)) {
612594
return 0;
613595
}
614596

@@ -624,11 +606,8 @@ handle_signals(PyThreadState *tstate)
624606
static int
625607
make_pending_calls(PyThreadState *tstate)
626608
{
627-
628-
_PyRuntimeState *runtime = tstate->interp->runtime;
629-
630609
/* only service pending calls on main thread */
631-
if (PyThread_get_thread_ident() != runtime->main_thread) {
610+
if (!_Py_IsMainThread()) {
632611
return 0;
633612
}
634613

Python/pystate.c

-6
Original file line numberDiff line numberDiff line change
@@ -169,12 +169,6 @@ _PyRuntimeState_ReInitThreads(_PyRuntimeState *runtime)
169169
#define HEAD_UNLOCK(runtime) \
170170
PyThread_release_lock((runtime)->interpreters.mutex)
171171

172-
int
173-
_Py_IsMainInterpreter(PyThreadState* tstate)
174-
{
175-
return (tstate->interp == tstate->interp->runtime->interpreters.main);
176-
}
177-
178172
/* Forward declaration */
179173
static void _PyGILState_NoteThreadState(
180174
struct _gilstate_runtime_state *gilstate, PyThreadState* tstate);

0 commit comments

Comments
 (0)