8000 bpo-40010: COMPUTE_EVAL_BREAKER() checks for subinterpreter by vstinner · Pull Request #19087 · python/cpython · GitHub
[go: up one dir, main page]

Skip to content

bpo-40010: COMPUTE_EVAL_BREAKER() checks for subinterpreter #19087

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Mar 20, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 7 additions & 5 deletions Doc/library/signal.rst
Original file line number Diff line number Diff line change
Expand Up @@ -53,12 +53,12 @@ This has consequences:
Signals and threads
^^^^^^^^^^^^^^^^^^^

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

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


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

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

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

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

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

Expand Down
28 changes: 27 additions & 1 deletion Include/internal/pycore_pystate.h
Original file line number Diff line number Diff line change
Expand Up @@ -294,7 +294,33 @@ _PyRuntimeState_SetFinalizing(_PyRuntimeState *runtime, PyThreadState *tstate) {
_Py_atomic_store_relaxed(&runtime->_finalizing, (uintptr_t)tstate);
}

PyAPI_FUNC(int) _Py_IsMainInterpreter(PyThreadState* tstate);
/* Check if the current thread is the main thread.
Use _Py_IsMainInterpreter() to check if it's the main interpreter. */
static inline int
_Py_IsMainThread(void)
{
unsigned long thread = PyThread_get_thread_ident();
return (thread == _PyRuntime.main_thread);
}


static inline int
_Py_IsMainInterpreter(PyThreadState* tstate)
{
/* Use directly _PyRuntime rather than tstate->interp->runtime, since
this function is used in performance critical code path (ceval) */
return (tstate->interp == _PyRuntime.interpreters.main);
}


/* Only handle signals on the main thread of the main interpreter. */
static inline int
_Py_ThreadCanHandleSignals(PyThreadState *tstate)
{
/* Use directly _PyRuntime rather than tstate->interp->runtime, since
this function is used in performance critical code path (ceval) */
return (_Py_IsMainThread() && _Py_IsMainInterpreter(tstate));
}


/* Variable and macro for in-line access to current thread
Expand Down
29 changes: 12 additions & 17 deletions Modules/signalmodule.c
Original file line number Diff line number Diff line change
Expand Up @@ -190,12 +190,10 @@ itimer_retval(struct itimerval *iv)
#endif

static int
is_main(_PyRuntimeState *runtime)
thread_can_handle_signals(void)
{
unsigned long thread = PyThread_get_thread_ident();
PyInterpreterState *interp = _PyRuntimeState_GetThreadState(runtime)->interp;
return (thread == runtime->main_thread
&& interp == runtime->interpreters.main);
PyThreadState *tstate = _PyThreadState_GET();
return _Py_ThreadCanHandleSignals(tstate);
}

static PyObject *
Expand Down Expand Up @@ -482,10 +480,10 @@ signal_signal_impl(PyObject *module, int signalnum, PyObject *handler)
}
#endif

_PyRuntimeState *runtime = &_PyRuntime;
if (!is_main(runtime)) {
if (!thread_can_handle_signals()) {
PyErr_SetString(PyExc_ValueError,
"signal only works in main thread");
"signal only works in main thread "
"of the main interpreter");
return NULL;
}
if (signalnum < 1 || signalnum >= NSIG) {
Expand Down Expand Up @@ -700,10 +698,10 @@ signal_set_wakeup_fd(PyObject *self, PyObject *args, PyObject *kwds)
return NULL;
#endif

_PyRuntimeState *runtime = &_PyRuntime;
if (!is_main(runtime)) {
if (!thread_can_handle_signals()) {
PyErr_SetString(PyExc_ValueError,
"set_wakeup_fd only works in main thread");
"set_wakeup_fd only works in main thread "
"of the main interpreter");
return NULL;
}

Expand Down Expand Up @@ -1675,8 +1673,7 @@ finisignal(void)
int
PyErr_CheckSignals(void)
{
_PyRuntimeState *runtime = &_PyRuntime;
if (!is_main(runtime)) {
if (!thread_can_handle_signals()) {
return 0;
}

Expand Down Expand Up @@ -1769,8 +1766,7 @@ int
PyOS_InterruptOccurred(void)
{
if (_Py_atomic_load_relaxed(&Handlers[SIGINT].tripped)) {
_PyRuntimeState *runtime = &_PyRuntime;
if (!is_main(runtime)) {
if (!thread_can_handle_signals()) {
return 0;
}
_Py_atomic_store_relaxed(&Handlers[SIGINT].tripped, 0);
Expand Down Expand Up @@ -1803,8 +1799,7 @@ _PySignal_AfterFork(void)
int
_PyOS_IsMainThread(void)
{
_PyRuntimeState *runtime = &_PyRuntime;
return is_main(runtime);
return thread_can_handle_signals();
}

#ifdef MS_WINDOWS
Expand Down
27 changes: 3 additions & 24 deletions Python/ceval.c
Original file line number Diff line number Diff line change
Expand Up @@ -136,14 +136,6 @@ is_tstate_valid(PyThreadState *tstate)
#endif


/* Only handle signals on the main thread of the main interpreter. */
static int
thread_can_handle_signals(void)
{
return (PyThread_get_thread_ident() == _PyRuntime.main_thread);
}


/* This can set eval_breaker to 0 even though gil_drop_request became
1. We believe this is all right because the eval loop will release
the GIL eventually anyway. */
Expand All @@ -156,7 +148,7 @@ COMPUTE_EVAL_BREAKER(PyThreadState *tstate,
_Py_atomic_store_relaxed(&ceval2->eval_breaker,
_Py_atomic_load_relaxed(&ceval->gil_drop_request)
| (_Py_atomic_load_relaxed(&ceval->signals_pending)
&& thread_can_handle_signals())
&& _Py_ThreadCanHandleSignals(tstate))
| _Py_atomic_load_relaxed(&ceval2->pending.calls_to_do)
| ceval2->pending.async_exc);
}
Expand Down Expand Up @@ -598,17 +590,7 @@ Py_AddPendingCall(int (*func)(void *), void *arg)
static int
handle_signals(PyThreadState *tstate)
{
_PyRuntimeState *runtime = tstate->interp->runtime;

if (!thread_can_handle_signals()) {
return 0;
}
/*
* Ensure that the thread isn't currently running some other
* interpreter.
*/
PyInterpreterState *interp = tstate->interp;
if (interp != runtime->interpreters.main) {
if (!_Py_ThreadCanHandleSignals(tstate)) {
return 0;
}

Expand All @@ -624,11 +606,8 @@ handle_signals(PyThreadState *tstate)
static int
make_pending_calls(PyThreadState *tstate)
{

_PyRuntimeState *runtime = tstate->interp->runtime;

/* only service pending calls on main thread */
if (PyThread_get_thread_ident() != runtime->main_thread) {
if (!_Py_IsMainThread()) {
return 0;
}

Expand Down
6 changes: 0 additions & 6 deletions Python/pystate.c
Original file line number Diff line number Diff line change
Expand Up @@ -169,12 +169,6 @@ _PyRuntimeState_ReInitThreads(_PyRuntimeState *runtime)
#define HEAD_UNLOCK(runtime) \
PyThread_release_lock((runtime)->interpreters.mutex)

int
_Py_IsMainInterpreter(PyThreadState* tstate)
{
return (tstate->interp == tstate->interp->runtime->interpreters.main);
}

/* Forward declaration */
static void _PyGILState_NoteThreadState(
struct _gilstate_runtime_state *gilstate, PyThreadState* tstate);
Expand Down
0