bpo-43963: Add _signal module state (GH-25676)

* Add signal_state_t structure and signal_global_state variable.
* Add a module state to the _signal module.
* Move and rename variables:

  * DefaultHandler becomes state->default_handler
  * IgnoreHandler becomes state->ignore_handler
  * sigint_event becomes state->sigint_event
  * ItimerError becomes modstate->itimer_error

* Rename SetHandler() to set_handler() to be consistent with
  get_handler().
This commit is contained in:
Victor Stinner 2021-04-28 03:02:55 +02:00 committed by GitHub
parent 5c84bb506a
commit a5e64444e6
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
1 changed files with 154 additions and 85 deletions

View File

@ -4,38 +4,39 @@
/* XXX Signals should be recorded per thread, now we have thread state. */
#include "Python.h"
#include "pycore_atomic.h"
#include "pycore_call.h"
#include "pycore_ceval.h"
#include "pycore_pyerrors.h"
#include "pycore_pylifecycle.h"
#include "pycore_pystate.h" // _PyThreadState_GET()
#include "pycore_atomic.h" // _Py_atomic_int
#include "pycore_call.h" // _PyObject_Call()
#include "pycore_ceval.h" // _PyEval_SignalReceived()
#include "pycore_moduleobject.h" // _PyModule_GetState()
#include "pycore_pyerrors.h" // _PyErr_SetString()
#include "pycore_pylifecycle.h" // NSIG
#include "pycore_pystate.h" // _PyThreadState_GET()
#ifndef MS_WINDOWS
#include "posixmodule.h"
# include "posixmodule.h"
#endif
#ifdef MS_WINDOWS
#include "socketmodule.h" /* needed for SOCKET_T */
# include "socketmodule.h" /* needed for SOCKET_T */
#endif
#ifdef MS_WINDOWS
#include <windows.h>
#ifdef HAVE_PROCESS_H
#include <process.h>
#endif
# include <windows.h>
# ifdef HAVE_PROCESS_H
# include <process.h>
# endif
#endif
#ifdef HAVE_SIGNAL_H
#include <signal.h>
# include <signal.h>
#endif
#ifdef HAVE_SYS_SYSCALL_H
#include <sys/syscall.h>
# include <sys/syscall.h>
#endif
#ifdef HAVE_SYS_STAT_H
#include <sys/stat.h>
# include <sys/stat.h>
#endif
#ifdef HAVE_SYS_TIME_H
#include <sys/time.h>
# include <sys/time.h>
#endif
#if defined(HAVE_PTHREAD_SIGMASK) && !defined(HAVE_BROKEN_PTHREAD_SIGMASK)
@ -47,7 +48,7 @@
#endif
#ifndef SIG_ERR
#define SIG_ERR ((PyOS_sighandler_t)(-1))
# define SIG_ERR ((PyOS_sighandler_t)(-1))
#endif
#include "clinic/signalmodule.c.h"
@ -124,27 +125,52 @@ static volatile struct {
/* Speed up sigcheck() when none tripped */
static _Py_atomic_int is_tripped;
static PyObject *DefaultHandler;
static PyObject *IgnoreHandler;
typedef struct {
PyObject *default_handler;
PyObject *ignore_handler;
#ifdef MS_WINDOWS
static HANDLE sigint_event = NULL;
HANDLE sigint_event;
#endif
} signal_state_t;
// State shared by all Python interpreters
static signal_state_t signal_global_state = {0};
#if defined(HAVE_GETITIMER) || defined(HAVE_SETITIMER)
static PyObject *ItimerError;
# define PYHAVE_ITIMER_ERROR
#endif
typedef struct {
PyObject *default_handler; // borrowed ref (signal_global_state)
PyObject *ignore_handler; // borrowed ref (signal_global_state)
#ifdef PYHAVE_ITIMER_ERROR
PyObject *itimer_error;
#endif
} _signal_module_state;
Py_LOCAL_INLINE(PyObject *)
get_handler(int i) {
get_handler(int i)
{
return (PyObject *)_Py_atomic_load(&Handlers[i].func);
}
Py_LOCAL_INLINE(void)
SetHandler(int i, PyObject* func) {
set_handler(int i, PyObject* func)
{
_Py_atomic_store(&Handlers[i].func, (uintptr_t)func);
}
static inline _signal_module_state*
get_signal_state(PyObject *module)
{
void *state = _PyModule_GetState(module);
assert(state != NULL);
return (_signal_module_state *)state;
}
#ifdef HAVE_GETITIMER
/* auxiliary functions for setitimer */
static int
@ -359,8 +385,10 @@ signal_handler(int sig_num)
errno = save_errno;
#ifdef MS_WINDOWS
if (sig_num == SIGINT)
SetEvent(sigint_event);
if (sig_num == SIGINT) {
signal_state_t *state = &signal_global_state;
SetEvent(state->sigint_event);
}
#endif
}
@ -459,6 +487,7 @@ static PyObject *
signal_signal_impl(PyObject *module, int signalnum, PyObject *handler)
/*[clinic end generated code: output=b44cfda43780f3a1 input=deee84af5fa0432c]*/
{
_signal_module_state *modstate = get_signal_state(module);
PyObject *old_handler;
void (*func)(int);
#ifdef MS_WINDOWS
@ -493,10 +522,10 @@ signal_signal_impl(PyObject *module, int signalnum, PyObject *handler)
"signal number out of range");
return NULL;
}
if (handler == IgnoreHandler) {
if (handler == modstate->ignore_handler) {
func = SIG_IGN;
}
else if (handler == DefaultHandler) {
else if (handler == modstate->default_handler) {
func = SIG_DFL;
}
else if (!PyCallable_Check(handler)) {
@ -519,7 +548,7 @@ signal_signal_impl(PyObject *module, int signalnum, PyObject *handler)
}
old_handler = get_handler(signalnum);
SetHandler(signalnum, Py_NewRef(handler));
set_handler(signalnum, Py_NewRef(handler));
if (old_handler != NULL) {
return old_handler;
@ -831,7 +860,6 @@ PySignal_SetWakeupFd(int fd)
#ifdef HAVE_SETITIMER
/*[clinic input]
signal.setitimer
@ -853,8 +881,9 @@ signal_setitimer_impl(PyObject *module, int which, PyObject *seconds,
PyObject *interval)
/*[clinic end generated code: output=65f9dcbddc35527b input=de43daf194e6f66f]*/
{
struct itimerval new;
_signal_module_state *modstate = get_signal_state(module);
struct itimerval new;
if (timeval_from_double(seconds, &new.it_value) < 0) {
return NULL;
}
@ -865,18 +894,16 @@ signal_setitimer_impl(PyObject *module, int which, PyObject *seconds,
/* Let OS check "which" value */
struct itimerval old;
if (setitimer(which, &new, &old) != 0) {
PyErr_SetFromErrno(ItimerError);
PyErr_SetFromErrno(modstate->itimer_error);
return NULL;
}
return itimer_retval(&old);
}
#endif
#endif // HAVE_SETITIMER
#ifdef HAVE_GETITIMER
/*[clinic input]
signal.getitimer
@ -890,17 +917,18 @@ static PyObject *
signal_getitimer_impl(PyObject *module, int which)
/*[clinic end generated code: output=9e053175d517db40 input=f7d21d38f3490627]*/
{
struct itimerval old;
_signal_module_state *modstate = get_signal_state(module);
struct itimerval old;
if (getitimer(which, &old) != 0) {
PyErr_SetFromErrno(ItimerError);
PyErr_SetFromErrno(modstate->itimer_error);
return NULL;
}
return itimer_retval(&old);
}
#endif // HAVE_GETITIMER
#endif
#if defined(PYPTHREAD_SIGMASK) || defined(HAVE_SIGPENDING)
static PyObject*
@ -1544,17 +1572,17 @@ signal_add_constants(PyObject *module)
static int
signal_get_set_handlers(PyObject *mod_dict)
signal_get_set_handlers(signal_state_t *state, PyObject *mod_dict)
{
// Get signal handlers
for (int signum = 1; signum < NSIG; signum++) {
void (*c_handler)(int) = PyOS_getsig(signum);
PyObject *func;
if (c_handler == SIG_DFL) {
func = DefaultHandler;
func = state->default_handler;
}
else if (c_handler == SIG_IGN) {
func = IgnoreHandler;
func = state->ignore_handler;
}
else {
func = Py_None; // None of our business
@ -1562,20 +1590,20 @@ signal_get_set_handlers(PyObject *mod_dict)
// If signal_module_exec() is called more than one, we must
// clear the strong reference to the previous function.
PyObject* old_func = get_handler(signum);
SetHandler(signum, Py_NewRef(func));
set_handler(signum, Py_NewRef(func));
Py_XDECREF(old_func);
}
// Instal Python SIGINT handler which raises KeyboardInterrupt
PyObject* sigint_func = get_handler(SIGINT);
if (sigint_func == DefaultHandler) {
if (sigint_func == state->default_handler) {
PyObject *int_handler = PyMapping_GetItemString(mod_dict,
"default_int_handler");
if (!int_handler) {
return -1;
}
SetHandler(SIGINT, int_handler);
set_handler(SIGINT, int_handler);
Py_DECREF(sigint_func);
PyOS_setsig(SIGINT, signal_handler);
}
@ -1588,20 +1616,34 @@ signal_module_exec(PyObject *m)
{
assert(!PyErr_Occurred());
signal_state_t *state = &signal_global_state;
_signal_module_state *modstate = get_signal_state(m);
modstate->default_handler = state->default_handler; // borrowed ref
modstate->ignore_handler = state->ignore_handler; // borrowed ref
#ifdef PYHAVE_ITIMER_ERROR
modstate->itimer_error = PyErr_NewException("signal.itimer_error",
PyExc_OSError, NULL);
if (modstate->itimer_error == NULL) {
return -1;
}
#endif
if (signal_add_constants(m) < 0) {
return -1;
}
/* Add some symbolic constants to the module */
PyObject *d = PyModule_GetDict(m);
if (PyDict_SetItemString(d, "SIG_DFL", DefaultHandler) < 0) {
if (PyDict_SetItemString(d, "SIG_DFL", state->default_handler) < 0) {
return -1;
}
if (PyDict_SetItemString(d, "SIG_IGN", IgnoreHandler) < 0) {
if (PyDict_SetItemString(d, "SIG_IGN", state->ignore_handler) < 0) {
return -1;
}
#if defined(HAVE_GETITIMER) || defined(HAVE_SETITIMER)
if (PyDict_SetItemString(d, "ItimerError", ItimerError) < 0) {
#ifdef PYHAVE_ITIMER_ERROR
if (PyDict_SetItemString(d, "ItimerError", modstate->itimer_error) < 0) {
return -1;
}
#endif
@ -1613,7 +1655,7 @@ signal_module_exec(PyObject *m)
PyThreadState *tstate = _PyThreadState_GET();
if (_Py_IsMainInterpreter(tstate->interp)) {
if (signal_get_set_handlers(d) < 0) {
if (signal_get_set_handlers(state, d) < 0) {
return -1;
}
}
@ -1623,6 +1665,31 @@ signal_module_exec(PyObject *m)
}
#ifdef PYHAVE_ITIMER_ERROR
static int
_signal_module_traverse(PyObject *module, visitproc visit, void *arg)
{
_signal_module_state *modstate = get_signal_state(module);
Py_VISIT(modstate->itimer_error);
return 0;
}
static int
_signal_module_clear(PyObject *module)
{
_signal_module_state *modstate = get_signal_state(module);
Py_CLEAR(modstate->itimer_error);
return 0;
}
static void
_signal_module_free(void *module)
{
_signal_module_clear((PyObject *)module);
}
#endif // PYHAVE_ITIMER_ERROR
static PyModuleDef_Slot signal_slots[] = {
{Py_mod_exec, signal_module_exec},
{0, NULL}
@ -1632,9 +1699,14 @@ static struct PyModuleDef signal_module = {
PyModuleDef_HEAD_INIT,
"_signal",
.m_doc = module_doc,
.m_size = 0,
.m_size = sizeof(_signal_module_state),
.m_methods = signal_methods,
.m_slots = signal_slots,
#ifdef PYHAVE_ITIMER_ERROR
.m_traverse = _signal_module_traverse,
.m_clear = _signal_module_clear,
.m_free = _signal_module_free,
#endif
};
@ -1648,15 +1720,17 @@ PyInit__signal(void)
void
_PySignal_Fini(void)
{
signal_state_t *state = &signal_global_state;
// Restore default signals and clear handlers
for (int signum = 1; signum < NSIG; signum++) {
PyObject *func = get_handler(signum);
_Py_atomic_store_relaxed(&Handlers[signum].tripped, 0);
SetHandler(signum, NULL);
set_handler(signum, NULL);
if (func != NULL
&& func != Py_None
&& func != DefaultHandler
&& func != IgnoreHandler)
&& func != state->default_handler
&& func != state->ignore_handler)
{
PyOS_setsig(signum, SIG_DFL);
}
@ -1664,17 +1738,14 @@ _PySignal_Fini(void)
}
#ifdef MS_WINDOWS
if (sigint_event != NULL) {
CloseHandle(sigint_event);
sigint_event = NULL;
if (state->sigint_event != NULL) {
CloseHandle(state->sigint_event);
state->sigint_event = NULL;
}
#endif
Py_CLEAR(DefaultHandler);
Py_CLEAR(IgnoreHandler);
#if defined(HAVE_GETITIMER) || defined(HAVE_SETITIMER)
Py_CLEAR(ItimerError);
#endif
Py_CLEAR(state->default_handler);
Py_CLEAR(state->ignore_handler);
}
@ -1720,6 +1791,7 @@ _PyErr_CheckSignalsTstate(PyThreadState *tstate)
frame = Py_None;
}
signal_state_t *state = &signal_global_state;
for (int i = 1; i < NSIG; i++) {
if (!_Py_atomic_load_relaxed(&Handlers[i].tripped)) {
continue;
@ -1733,8 +1805,8 @@ _PyErr_CheckSignalsTstate(PyThreadState *tstate)
* (see bpo-43406).
*/
PyObject *func = get_handler(i);
if (func == NULL || func == Py_None || func == IgnoreHandler ||
func == DefaultHandler) {
if (func == NULL || func == Py_None || func == state->ignore_handler ||
func == state->default_handler) {
/* 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
@ -1791,8 +1863,10 @@ PyErr_SetInterruptEx(int signum)
if (signum < 1 || signum >= NSIG) {
return -1;
}
PyObject* func = get_handler(signum);
if (func != IgnoreHandler && func != DefaultHandler) {
signal_state_t *state = &signal_global_state;
PyObject *func = get_handler(signum);
if (func != state->ignore_handler && func != state->default_handler) {
trip_signal(signum);
}
return 0;
@ -1854,28 +1928,22 @@ _Py_RestoreSignals(void)
int
_PySignal_Init(int install_signal_handlers)
{
DefaultHandler = PyLong_FromVoidPtr((void *)SIG_DFL);
if (!DefaultHandler) {
signal_state_t *state = &signal_global_state;
state->default_handler = PyLong_FromVoidPtr((void *)SIG_DFL);
if (state->default_handler == NULL) {
return -1;
}
IgnoreHandler = PyLong_FromVoidPtr((void *)SIG_IGN);
if (!IgnoreHandler) {
state->ignore_handler = PyLong_FromVoidPtr((void *)SIG_IGN);
if (state->ignore_handler == NULL) {
return -1;
}
#if defined(HAVE_GETITIMER) || defined(HAVE_SETITIMER)
ItimerError = PyErr_NewException("signal.ItimerError",
PyExc_OSError, NULL);
if (!ItimerError) {
return -1;
}
#endif
#ifdef MS_WINDOWS
/* Create manual-reset event, initially unset */
sigint_event = CreateEvent(NULL, TRUE, FALSE, FALSE);
if (sigint_event == NULL) {
state->sigint_event = CreateEvent(NULL, TRUE, FALSE, FALSE);
if (state->sigint_event == NULL) {
PyErr_SetFromWindowsErr(0);
return -1;
}
@ -1963,13 +2031,14 @@ _PyOS_IsMainThread(void)
}
#ifdef MS_WINDOWS
/* Returns a manual-reset event which gets tripped whenever
SIGINT is received.
Python.h does not include windows.h so we do cannot use HANDLE
as the return type of this function. We use void* instead. */
void *_PyOS_SigintEvent(void)
{
/* Returns a manual-reset event which gets tripped whenever
SIGINT is received.
Python.h does not include windows.h so we do cannot use HANDLE
as the return type of this function. We use void* instead. */
return sigint_event;
signal_state_t *state = &signal_global_state;
return state->sigint_event;
}
#endif