summaryrefslogtreecommitdiff
path: root/Modules/signalmodule.c
diff options
context:
space:
mode:
authorCharles-François Natali <neologix@free.fr>2012-02-02 20:32:48 +0100
committerCharles-François Natali <neologix@free.fr>2012-02-02 20:32:48 +0100
commit136fcbb7b70ed7fbc70ec2c5ed3e87b2ab72b2ea (patch)
tree650cf12d6d8cab0a96e585e645e89c7c32f28fb8 /Modules/signalmodule.c
parent0656cf5f51ab1e842b55a7b41589309a9c7718cb (diff)
parent8332655c05dd8472037fb5f8ecdbff10088771f4 (diff)
downloadcpython-136fcbb7b70ed7fbc70ec2c5ed3e87b2ab72b2ea.tar.gz
Issue #13817: After fork(), reinit the ad-hoc TLS implementation earlier to fix
a random deadlock when fork() is called in a multithreaded process in debug mode, and make PyOS_AfterFork() more robust.
Diffstat (limited to 'Modules/signalmodule.c')
-rw-r--r--Modules/signalmodule.c444
1 files changed, 439 insertions, 5 deletions
diff --git a/Modules/signalmodule.c b/Modules/signalmodule.c
index 32cd8bbece..e46f8cf1dc 100644
--- a/Modules/signalmodule.c
+++ b/Modules/signalmodule.c
@@ -22,6 +22,14 @@
#include <sys/time.h>
#endif
+#if defined(HAVE_PTHREAD_SIGMASK) && !defined(HAVE_BROKEN_PTHREAD_SIGMASK)
+# define PYPTHREAD_SIGMASK
+#endif
+
+#if defined(PYPTHREAD_SIGMASK) && defined(HAVE_PTHREAD_H)
+# include <pthread.h>
+#endif
+
#ifndef SIG_ERR
#define SIG_ERR ((PyOS_sighandler_t)(-1))
#endif
@@ -101,6 +109,10 @@ static PyObject *IntHandler;
static PyOS_sighandler_t old_siginthandler = SIG_DFL;
+#ifdef MS_WINDOWS
+static HANDLE sigint_event = NULL;
+#endif
+
#ifdef HAVE_GETITIMER
static PyObject *ItimerError;
@@ -168,15 +180,19 @@ checksignals_witharg(void * unused)
static void
trip_signal(int sig_num)
{
+ unsigned char byte;
+
Handlers[sig_num].tripped = 1;
+ if (wakeup_fd != -1) {
+ byte = (unsigned char)sig_num;
+ write(wakeup_fd, &byte, 1);
+ }
if (is_tripped)
return;
/* Set is_tripped after setting .tripped, as it gets
cleared in PyErr_CheckSignals() before .tripped. */
is_tripped = 1;
Py_AddPendingCall(checksignals_witharg, NULL);
- if (wakeup_fd != -1)
- write(wakeup_fd, "\0", 1);
}
static void
@@ -217,6 +233,11 @@ signal_handler(int sig_num)
/* Issue #10311: asynchronously executing signal handlers should not
mutate errno under the feet of unsuspecting C code. */
errno = save_errno;
+
+#ifdef MS_WINDOWS
+ if (sig_num == SIGINT)
+ SetEvent(sigint_event);
+#endif
}
@@ -313,7 +334,7 @@ signal_signal(PyObject *self, PyObject *args)
else
func = signal_handler;
if (PyOS_setsig(sig_num, func) == SIG_ERR) {
- PyErr_SetFromErrno(PyExc_RuntimeError);
+ PyErr_SetFromErrno(PyExc_OSError);
return NULL;
}
old_handler = Handlers[sig_num].func;
@@ -382,7 +403,7 @@ signal_siginterrupt(PyObject *self, PyObject *args)
return NULL;
}
if (siginterrupt(sig_num, flag)<0) {
- PyErr_SetFromErrno(PyExc_RuntimeError);
+ PyErr_SetFromErrno(PyExc_OSError);
return NULL;
}
@@ -495,6 +516,346 @@ PyDoc_STRVAR(getitimer_doc,
Returns current value of given itimer.");
#endif
+#if defined(PYPTHREAD_SIGMASK) || defined(HAVE_SIGWAIT) || \
+ defined(HAVE_SIGWAITINFO) || defined(HAVE_SIGTIMEDWAIT)
+/* Convert an iterable to a sigset.
+ Return 0 on success, return -1 and raise an exception on error. */
+
+static int
+iterable_to_sigset(PyObject *iterable, sigset_t *mask)
+{
+ int result = -1;
+ PyObject *iterator, *item;
+ long signum;
+ int err;
+
+ sigemptyset(mask);
+
+ iterator = PyObject_GetIter(iterable);
+ if (iterator == NULL)
+ goto error;
+
+ while (1)
+ {
+ item = PyIter_Next(iterator);
+ if (item == NULL) {
+ if (PyErr_Occurred())
+ goto error;
+ else
+ break;
+ }
+
+ signum = PyLong_AsLong(item);
+ Py_DECREF(item);
+ if (signum == -1 && PyErr_Occurred())
+ goto error;
+ if (0 < signum && signum < NSIG)
+ err = sigaddset(mask, (int)signum);
+ else
+ err = 1;
+ if (err) {
+ PyErr_Format(PyExc_ValueError,
+ "signal number %ld out of range", signum);
+ goto error;
+ }
+ }
+ result = 0;
+
+error:
+ Py_XDECREF(iterator);
+ return result;
+}
+#endif
+
+#if defined(PYPTHREAD_SIGMASK) || defined(HAVE_SIGPENDING)
+static PyObject*
+sigset_to_set(sigset_t mask)
+{
+ PyObject *signum, *result;
+ int sig;
+
+ result = PySet_New(0);
+ if (result == NULL)
+ return NULL;
+
+ for (sig = 1; sig < NSIG; sig++) {
+ if (sigismember(&mask, sig) != 1)
+ continue;
+
+ /* Handle the case where it is a member by adding the signal to
+ the result list. Ignore the other cases because they mean the
+ signal isn't a member of the mask or the signal was invalid,
+ and an invalid signal must have been our fault in constructing
+ the loop boundaries. */
+ signum = PyLong_FromLong(sig);
+ if (signum == NULL) {
+ Py_DECREF(result);
+ return NULL;
+ }
+ if (PySet_Add(result, signum) == -1) {
+ Py_DECREF(signum);
+ Py_DECREF(result);
+ return NULL;
+ }
+ Py_DECREF(signum);
+ }
+ return result;
+}
+#endif
+
+#ifdef PYPTHREAD_SIGMASK
+static PyObject *
+signal_pthread_sigmask(PyObject *self, PyObject *args)
+{
+ int how;
+ PyObject *signals;
+ sigset_t mask, previous;
+ int err;
+
+ if (!PyArg_ParseTuple(args, "iO:pthread_sigmask", &how, &signals))
+ return NULL;
+
+ if (iterable_to_sigset(signals, &mask))
+ return NULL;
+
+ err = pthread_sigmask(how, &mask, &previous);
+ if (err != 0) {
+ errno = err;
+ PyErr_SetFromErrno(PyExc_OSError);
+ return NULL;
+ }
+
+ /* if signals was unblocked, signal handlers have been called */
+ if (PyErr_CheckSignals())
+ return NULL;
+
+ return sigset_to_set(previous);
+}
+
+PyDoc_STRVAR(signal_pthread_sigmask_doc,
+"pthread_sigmask(how, mask) -> old mask\n\
+\n\
+Fetch and/or change the signal mask of the calling thread.");
+#endif /* #ifdef PYPTHREAD_SIGMASK */
+
+
+#ifdef HAVE_SIGPENDING
+static PyObject *
+signal_sigpending(PyObject *self)
+{
+ int err;
+ sigset_t mask;
+ err = sigpending(&mask);
+ if (err)
+ return PyErr_SetFromErrno(PyExc_OSError);
+ return sigset_to_set(mask);
+}
+
+PyDoc_STRVAR(signal_sigpending_doc,
+"sigpending() -> list\n\
+\n\
+Examine pending signals.");
+#endif /* #ifdef HAVE_SIGPENDING */
+
+
+#ifdef HAVE_SIGWAIT
+static PyObject *
+signal_sigwait(PyObject *self, PyObject *args)
+{
+ PyObject *signals;
+ sigset_t set;
+ int err, signum;
+
+ if (!PyArg_ParseTuple(args, "O:sigwait", &signals))
+ return NULL;
+
+ if (iterable_to_sigset(signals, &set))
+ return NULL;
+
+ Py_BEGIN_ALLOW_THREADS
+ err = sigwait(&set, &signum);
+ Py_END_ALLOW_THREADS
+ if (err) {
+ errno = err;
+ return PyErr_SetFromErrno(PyExc_OSError);
+ }
+
+ return PyLong_FromLong(signum);
+}
+
+PyDoc_STRVAR(signal_sigwait_doc,
+"sigwait(sigset) -> signum\n\
+\n\
+Wait a signal.");
+#endif /* #ifdef HAVE_SIGPENDING */
+
+#if defined(HAVE_SIGWAITINFO) || defined(HAVE_SIGTIMEDWAIT)
+static int initialized;
+static PyStructSequence_Field struct_siginfo_fields[] = {
+ {"si_signo", "signal number"},
+ {"si_code", "signal code"},
+ {"si_errno", "errno associated with this signal"},
+ {"si_pid", "sending process ID"},
+ {"si_uid", "real user ID of sending process"},
+ {"si_status", "exit value or signal"},
+ {"si_band", "band event for SIGPOLL"},
+ {0}
+};
+
+PyDoc_STRVAR(struct_siginfo__doc__,
+"struct_siginfo: Result from sigwaitinfo or sigtimedwait.\n\n\
+This object may be accessed either as a tuple of\n\
+(si_signo, si_code, si_errno, si_pid, si_uid, si_status, si_band),\n\
+or via the attributes si_signo, si_code, and so on.");
+
+static PyStructSequence_Desc struct_siginfo_desc = {
+ "signal.struct_siginfo", /* name */
+ struct_siginfo__doc__, /* doc */
+ struct_siginfo_fields, /* fields */
+ 7 /* n_in_sequence */
+};
+
+static PyTypeObject SiginfoType;
+
+static PyObject *
+fill_siginfo(siginfo_t *si)
+{
+ PyObject *result = PyStructSequence_New(&SiginfoType);
+ if (!result)
+ return NULL;
+
+ PyStructSequence_SET_ITEM(result, 0, PyLong_FromLong((long)(si->si_signo)));
+ PyStructSequence_SET_ITEM(result, 1, PyLong_FromLong((long)(si->si_code)));
+ PyStructSequence_SET_ITEM(result, 2, PyLong_FromLong((long)(si->si_errno)));
+ PyStructSequence_SET_ITEM(result, 3, PyLong_FromPid(si->si_pid));
+ PyStructSequence_SET_ITEM(result, 4, PyLong_FromLong((long)(si->si_uid)));
+ PyStructSequence_SET_ITEM(result, 5,
+ PyLong_FromLong((long)(si->si_status)));
+ PyStructSequence_SET_ITEM(result, 6, PyLong_FromLong(si->si_band));
+ if (PyErr_Occurred()) {
+ Py_DECREF(result);
+ return NULL;
+ }
+
+ return result;
+}
+#endif
+
+#ifdef HAVE_SIGWAITINFO
+static PyObject *
+signal_sigwaitinfo(PyObject *self, PyObject *args)
+{
+ PyObject *signals;
+ sigset_t set;
+ siginfo_t si;
+ int err;
+
+ if (!PyArg_ParseTuple(args, "O:sigwaitinfo", &signals))
+ return NULL;
+
+ if (iterable_to_sigset(signals, &set))
+ return NULL;
+
+ Py_BEGIN_ALLOW_THREADS
+ err = sigwaitinfo(&set, &si);
+ Py_END_ALLOW_THREADS
+ if (err == -1)
+ return PyErr_SetFromErrno(PyExc_OSError);
+
+ return fill_siginfo(&si);
+}
+
+PyDoc_STRVAR(signal_sigwaitinfo_doc,
+"sigwaitinfo(sigset) -> struct_siginfo\n\
+\n\
+Wait synchronously for a signal until one of the signals in *sigset* is\n\
+delivered.\n\
+Returns a struct_siginfo containing information about the signal.");
+#endif /* #ifdef HAVE_SIGWAITINFO */
+
+#ifdef HAVE_SIGTIMEDWAIT
+static PyObject *
+signal_sigtimedwait(PyObject *self, PyObject *args)
+{
+ PyObject *signals, *timeout;
+ struct timespec buf;
+ sigset_t set;
+ siginfo_t si;
+ int err;
+
+ if (!PyArg_ParseTuple(args, "OO:sigtimedwait", &signals, &timeout))
+ return NULL;
+
+ if (!PyTuple_Check(timeout) || PyTuple_Size(timeout) != 2) {
+ PyErr_SetString(PyExc_TypeError,
+ "sigtimedwait() arg 2 must be a tuple "
+ "(timeout_sec, timeout_nsec)");
+ return NULL;
+ } else if (!PyArg_ParseTuple(timeout, "ll:sigtimedwait",
+ &(buf.tv_sec), &(buf.tv_nsec)))
+ return NULL;
+
+ if (buf.tv_sec < 0 || buf.tv_nsec < 0) {
+ PyErr_SetString(PyExc_ValueError, "timeout must be non-negative");
+ return NULL;
+ }
+
+ if (iterable_to_sigset(signals, &set))
+ return NULL;
+
+ Py_BEGIN_ALLOW_THREADS
+ err = sigtimedwait(&set, &si, &buf);
+ Py_END_ALLOW_THREADS
+ if (err == -1) {
+ if (errno == EAGAIN)
+ Py_RETURN_NONE;
+ else
+ return PyErr_SetFromErrno(PyExc_OSError);
+ }
+
+ return fill_siginfo(&si);
+}
+
+PyDoc_STRVAR(signal_sigtimedwait_doc,
+"sigtimedwait(sigset, (timeout_sec, timeout_nsec)) -> struct_siginfo\n\
+\n\
+Like sigwaitinfo(), but with a timeout specified as a tuple of (seconds,\n\
+nanoseconds).");
+#endif /* #ifdef HAVE_SIGTIMEDWAIT */
+
+
+#if defined(HAVE_PTHREAD_KILL) && defined(WITH_THREAD)
+static PyObject *
+signal_pthread_kill(PyObject *self, PyObject *args)
+{
+ long tid;
+ int signum;
+ int err;
+
+ if (!PyArg_ParseTuple(args, "li:pthread_kill", &tid, &signum))
+ return NULL;
+
+ err = pthread_kill((pthread_t)tid, signum);
+ if (err != 0) {
+ errno = err;
+ PyErr_SetFromErrno(PyExc_OSError);
+ return NULL;
+ }
+
+ /* the signal may have been send to the current thread */
+ if (PyErr_CheckSignals())
+ return NULL;
+
+ Py_RETURN_NONE;
+}
+
+PyDoc_STRVAR(signal_pthread_kill_doc,
+"pthread_kill(thread_id, signum)\n\
+\n\
+Send a signal to a thread.");
+#endif /* #if defined(HAVE_PTHREAD_KILL) && defined(WITH_THREAD) */
+
+
/* List of functions defined in the module */
static PyMethodDef signal_methods[] = {
@@ -515,10 +876,34 @@ static PyMethodDef signal_methods[] = {
#endif
#ifdef HAVE_PAUSE
{"pause", (PyCFunction)signal_pause,
- METH_NOARGS,pause_doc},
+ METH_NOARGS, pause_doc},
#endif
{"default_int_handler", signal_default_int_handler,
METH_VARARGS, default_int_handler_doc},
+#if defined(HAVE_PTHREAD_KILL) && defined(WITH_THREAD)
+ {"pthread_kill", (PyCFunction)signal_pthread_kill,
+ METH_VARARGS, signal_pthread_kill_doc},
+#endif
+#ifdef PYPTHREAD_SIGMASK
+ {"pthread_sigmask", (PyCFunction)signal_pthread_sigmask,
+ METH_VARARGS, signal_pthread_sigmask_doc},
+#endif
+#ifdef HAVE_SIGPENDING
+ {"sigpending", (PyCFunction)signal_sigpending,
+ METH_NOARGS, signal_sigpending_doc},
+#endif
+#ifdef HAVE_SIGWAIT
+ {"sigwait", (PyCFunction)signal_sigwait,
+ METH_VARARGS, signal_sigwait_doc},
+#endif
+#ifdef HAVE_SIGWAITINFO
+ {"sigwaitinfo", (PyCFunction)signal_sigwaitinfo,
+ METH_VARARGS, signal_sigwaitinfo_doc},
+#endif
+#ifdef HAVE_SIGTIMEDWAIT
+ {"sigtimedwait", (PyCFunction)signal_sigtimedwait,
+ METH_VARARGS, signal_sigtimedwait_doc},
+#endif
{NULL, NULL} /* sentinel */
};
@@ -587,6 +972,15 @@ PyInit_signal(void)
if (m == NULL)
return NULL;
+#if defined(HAVE_SIGWAITINFO) || defined(HAVE_SIGTIMEDWAIT)
+ if (!initialized)
+ PyStructSequence_InitType(&SiginfoType, &struct_siginfo_desc);
+
+ Py_INCREF((PyObject*) &SiginfoType);
+ PyModule_AddObject(m, "struct_siginfo", (PyObject*) &SiginfoType);
+ initialized = 1;
+#endif
+
/* Add some symbolic constants to the module */
d = PyModule_GetDict(m);
@@ -603,6 +997,19 @@ PyInit_signal(void)
goto finally;
Py_DECREF(x);
+#ifdef SIG_BLOCK
+ if (PyModule_AddIntMacro(m, SIG_BLOCK))
+ goto finally;
+#endif
+#ifdef SIG_UNBLOCK
+ if (PyModule_AddIntMacro(m, SIG_UNBLOCK))
+ goto finally;
+#endif
+#ifdef SIG_SETMASK
+ if (PyModule_AddIntMacro(m, SIG_SETMASK))
+ goto finally;
+#endif
+
x = IntHandler = PyDict_GetItemString(d, "default_int_handler");
if (!x)
goto finally;
@@ -855,6 +1262,11 @@ PyInit_signal(void)
Py_DECREF(x);
#endif
+#ifdef MS_WINDOWS
+ /* Create manual-reset event, initially unset */
+ sigint_event = CreateEvent(NULL, TRUE, FALSE, FALSE);
+#endif
+
if (PyErr_Occurred()) {
Py_DECREF(m);
m = NULL;
@@ -1001,3 +1413,25 @@ PyOS_AfterFork(void)
_PyImport_ReInitLock();
#endif
}
+
+int
+_PyOS_IsMainThread(void)
+{
+#ifdef WITH_THREAD
+ return PyThread_get_thread_ident() == main_thread;
+#else
+ return 1;
+#endif
+}
+
+#ifdef MS_WINDOWS
+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;
+}
+#endif