Issue #8407, issue #11859: Add signal.pthread_sigmask() function to fetch

and/or change the signal mask of the calling thread.

Fix also tests of test_io using threads and an alarm: use pthread_sigmask() to
ensure that the SIGALRM signal is received by the main thread.

Original patch written by Jean-Paul Calderone.
This commit is contained in:
Victor Stinner 2011-04-30 15:21:58 +02:00
parent d5c355ccc7
commit a929335961
6 changed files with 262 additions and 6 deletions

View file

@ -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
@ -495,6 +503,110 @@ PyDoc_STRVAR(getitimer_doc,
Returns current value of given itimer.");
#endif
#ifdef PYPTHREAD_SIGMASK
/* 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;
}
static PyObject *
signal_pthread_sigmask(PyObject *self, PyObject *args)
{
int how, sig;
PyObject *signals, *result, *signum;
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_RuntimeError);
return NULL;
}
result = PyList_New(0);
if (result == NULL)
return NULL;
for (sig = 1; sig < NSIG; sig++) {
if (sigismember(&previous, 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 (PyList_Append(result, signum) == -1) {
Py_DECREF(signum);
Py_DECREF(result);
return NULL;
}
Py_DECREF(signum);
}
return result;
}
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 */
/* List of functions defined in the module */
static PyMethodDef signal_methods[] = {
@ -515,10 +627,14 @@ 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},
#ifdef PYPTHREAD_SIGMASK
{"pthread_sigmask", (PyCFunction)signal_pthread_sigmask,
METH_VARARGS, signal_pthread_sigmask_doc},
#endif
{NULL, NULL} /* sentinel */
};
@ -603,6 +719,27 @@ PyInit_signal(void)
goto finally;
Py_DECREF(x);
#ifdef SIG_BLOCK
x = PyLong_FromLong(SIG_BLOCK);
if (!x || PyDict_SetItemString(d, "SIG_BLOCK", x) < 0)
goto finally;
Py_DECREF(x);
#endif
#ifdef SIG_UNBLOCK
x = PyLong_FromLong(SIG_UNBLOCK);
if (!x || PyDict_SetItemString(d, "SIG_UNBLOCK", x) < 0)
goto finally;
Py_DECREF(x);
#endif
#ifdef SIG_SETMASK
x = PyLong_FromLong(SIG_SETMASK);
if (!x || PyDict_SetItemString(d, "SIG_SETMASK", x) < 0)
goto finally;
Py_DECREF(x);
#endif
x = IntHandler = PyDict_GetItemString(d, "default_int_handler");
if (!x)
goto finally;