cpython: 1d8a57deddc4 (original) (raw)
Mercurial > cpython
changeset 69934:1d8a57deddc4
Issue #8407: Add pthread_kill(), sigpending() and sigwait() functions to the signal module. [#8407]
Victor Stinner victor.stinner@haypocalc.com | |
---|---|
date | Sun, 08 May 2011 01:46:11 +0200 |
parents | 6aea90327020 |
children | f8c49a930015 |
files | Doc/library/os.rst Doc/library/signal.rst Doc/whatsnew/3.3.rst Lib/test/test_signal.py Misc/NEWS Modules/signalmodule.c configure configure.in pyconfig.h.in |
diffstat | 9 files changed, 287 insertions(+), 42 deletions(-)[+] [-] Doc/library/os.rst 2 Doc/library/signal.rst 53 Doc/whatsnew/3.3.rst 9 Lib/test/test_signal.py 141 Misc/NEWS 3 Modules/signalmodule.c 102 configure 5 configure.in 5 pyconfig.h.in 9 |
line wrap: on
line diff
--- a/Doc/library/os.rst
+++ b/Doc/library/os.rst
@@ -2284,6 +2284,8 @@ written in Python, such as a mail server
will be set to sig. The Windows version of :func:kill
additionally takes
process handles to be killed.
--- a/Doc/library/signal.rst
+++ b/Doc/library/signal.rst
@@ -179,6 +179,29 @@ The :mod:signal
module defines the fol
will then be called. Returns nothing. Not on Windows. (See the Unix man page
:manpage:signal(2)
.)
+ +.. function:: pthread_kill(thread_id, signum) +
- Send the signal signum to the thread thread_id, another thread in the same
- process as the caller. The signal is asynchronously directed to thread. +
- thread_id can be read from the :attr:
~threading.Thread.ident
attribute - of :attr:
threading.Thread
. For example, threading.current_thread().ident
gives the identifier of the current- thread. +
- If signum is 0, then no signal is sent, but error checking is still
- performed; this can be used to check if a thread is still running. +
- Availability: Unix (see the man page :manpage:
pthread_kill(3)
for further - information). +
- See also :func:
os.kill
. + - .. versionadded:: 3.3 +
.. function:: pthread_sigmask(how, mask)
@@ -206,6 +229,8 @@ The :mod:signal
module defines the fol
Availability: Unix. See the man page :manpage:sigprocmask(3)
and
:manpage:pthread_sigmask(3)
for further information.
@@ -283,6 +308,34 @@ The :mod:signal
module defines the fol
:const:SIGTERM
. A :exc:ValueError
will be raised in any other case.
+.. function:: sigpending()
+
- Examine the set of signals that are pending for delivery to the calling
- thread (i.e., the signals which have been raised while blocked). Return the
- set of the pending signals. +
- Availability: Unix (see the man page :manpage:
sigpending(2)
for further - information). +
- See also :func:
pause
, :func:pthread_sigmask
and :func:sigwait
. + - .. versionadded:: 3.3 +
+ +.. function:: sigwait(sigset) +
- Suspend execution of the calling thread until the delivery of one of the
- signals specified in the signal set sigset. The function accepts the signal
- (removes it from the pending list of signals), and returns the signal number. +
- Availability: Unix (see the man page :manpage:
sigwait(3)
for further - information). +
- See also :func:
pause
, :func:pthread_sigmask
and :func:sigpending
. + - .. versionadded:: 3.3 +
--- a/Doc/whatsnew/3.3.rst
+++ b/Doc/whatsnew/3.3.rst
@@ -123,10 +123,13 @@ sys
signal
------
-* The :mod:signal
module has a new :func:~signal.pthread_sigmask
function
- to fetch and/or change the signal mask of the calling thread.
+* The :mod:
signal
module has a new functions: - (Contributed by Jean-Paul Calderone in :issue:
8407
)
--- a/Lib/test/test_signal.py +++ b/Lib/test/test_signal.py @@ -8,6 +8,10 @@ import signal import subprocess import traceback import sys, os, time, errno +try:
if sys.platform in ('os2', 'riscos'): raise unittest.SkipTest("Can't test signal on %s" % sys.platform) @@ -187,7 +191,7 @@ class InterProcessSignalTests(unittest.T @unittest.skipIf(sys.platform == "win32", "Not valid on Windows") -class BasicSignalTests(unittest.TestCase): +class PosixTests(unittest.TestCase): def trivial_signal_handler(self, *args): pass @@ -484,50 +488,121 @@ class ItimerTest(unittest.TestCase): self.assertEqual(self.hndl_called, True) -@unittest.skipUnless(hasattr(signal, 'pthread_sigmask'),
'need signal.pthread_sigmask()')[](#l4.28)
class PendingSignalsTests(unittest.TestCase): """
- Test pthread_sigmask(), pthread_kill(), sigpending() and sigwait()
- functions. """
- def setUp(self):
self.has_pthread_kill = hasattr(signal, 'pthread_kill')[](#l4.36)
+ def handler(self, signum, frame): 1/0 def read_sigmask(self): return signal.pthread_sigmask(signal.SIG_BLOCK, [])
- def test_pthread_sigmask_arguments(self):
self.assertRaises(TypeError, signal.pthread_sigmask)[](#l4.45)
self.assertRaises(TypeError, signal.pthread_sigmask, 1)[](#l4.46)
self.assertRaises(TypeError, signal.pthread_sigmask, 1, 2, 3)[](#l4.47)
self.assertRaises(RuntimeError, signal.pthread_sigmask, 1700, [])[](#l4.48)
- def can_test_blocked_signals(self, skip):
"""[](#l4.50)
Check if a blocked signal can be raised to the main thread without[](#l4.51)
calling its signal handler. We need pthread_kill() or exactly one[](#l4.52)
thread (the main thread).[](#l4.53)
- def test_pthread_sigmask(self):
import faulthandler[](#l4.56)
pid = os.getpid()[](#l4.57)
signum = signal.SIGUSR1[](#l4.58)
Return True if it's possible. Otherwise, return False and print a[](#l4.59)
warning if skip is False, or raise a SkipTest exception if skip is[](#l4.60)
True.[](#l4.61)
"""[](#l4.62)
if self.has_pthread_kill:[](#l4.63)
return True[](#l4.64)
# The fault handler timeout thread masks all signals. If the main # thread masks also SIGUSR1, all threads mask this signal. In this # case, if we send SIGUSR1 to the process, the signal is pending in the # main or the faulthandler timeout thread. Unblock SIGUSR1 in the main # thread calls the signal handler only if the signal is pending for the
# main thread.[](#l4.71)
#[](#l4.72)
# Stop the faulthandler timeout thread to workaround this problem.[](#l4.73)
# Another solution would be to send the signal directly to the main[](#l4.74)
# thread using pthread_kill(), but Python doesn't expose this[](#l4.75)
# function.[](#l4.76)
# main thread. Stop the faulthandler timeout thread to workaround this[](#l4.77)
# problem.[](#l4.78)
import faulthandler[](#l4.79) faulthandler.cancel_dump_tracebacks_later()[](#l4.80)
# Issue #11998: The _tkinter module loads the Tcl library which creates[](#l4.82)
# a thread waiting events in select(). This thread receives signals[](#l4.83)
# blocked by all other threads. We cannot test blocked signals if the[](#l4.84)
# _tkinter module is loaded.[](#l4.85)
can_test_blocked_signals = ('_tkinter' not in sys.modules)[](#l4.86)
if not can_test_blocked_signals:[](#l4.87)
print("WARNING: _tkinter is loaded, cannot test signals "[](#l4.88)
"blocked by pthread_sigmask() (issue #11998)")[](#l4.89)
# Issue #11998: The _tkinter module loads the Tcl library which[](#l4.90)
# creates a thread waiting events in select(). This thread receives[](#l4.91)
# signals blocked by all other threads. We cannot test blocked[](#l4.92)
# signals[](#l4.93)
if '_tkinter' in sys.modules:[](#l4.94)
message = ("_tkinter is loaded and pthread_kill() is missing, "[](#l4.95)
"cannot test blocked signals (issue #11998)")[](#l4.96)
if skip:[](#l4.97)
self.skipTest(message)[](#l4.98)
else:[](#l4.99)
print("WARNING: %s" % message)[](#l4.100)
return False[](#l4.101)
return True[](#l4.102)
- def kill(self, signum):
if self.has_pthread_kill:[](#l4.105)
tid = threading.current_thread().ident[](#l4.106)
signal.pthread_kill(tid, signum)[](#l4.107)
else:[](#l4.108)
pid = os.getpid()[](#l4.109)
os.kill(pid, signum)[](#l4.110)
- @unittest.skipUnless(hasattr(signal, 'sigpending'),
'need signal.sigpending()')[](#l4.113)
- def test_sigpending_empty(self):
self.assertEqual(signal.sigpending(), set())[](#l4.115)
- @unittest.skipUnless(hasattr(signal, 'pthread_sigmask'),
'need signal.pthread_sigmask()')[](#l4.118)
- @unittest.skipUnless(hasattr(signal, 'sigpending'),
'need signal.sigpending()')[](#l4.120)
- def test_sigpending(self):
self.can_test_blocked_signals(True)[](#l4.122)
signum = signal.SIGUSR1[](#l4.124)
old_handler = signal.signal(signum, self.handler)[](#l4.125)
self.addCleanup(signal.signal, signum, old_handler)[](#l4.126)
signal.pthread_sigmask(signal.SIG_BLOCK, [signum])[](#l4.128)
self.kill(signum)[](#l4.129)
self.assertEqual(signal.sigpending(), {signum})[](#l4.130)
with self.assertRaises(ZeroDivisionError):[](#l4.131)
signal.pthread_sigmask(signal.SIG_UNBLOCK, [signum])[](#l4.132)
- @unittest.skipUnless(hasattr(signal, 'pthread_kill'),
'need signal.pthread_kill()')[](#l4.135)
- def test_pthread_kill(self):
signum = signal.SIGUSR1[](#l4.137)
current = threading.current_thread().ident[](#l4.138)
old_handler = signal.signal(signum, self.handler)[](#l4.140)
self.addCleanup(signal.signal, signum, old_handler)[](#l4.141)
with self.assertRaises(ZeroDivisionError):[](#l4.143)
signal.pthread_kill(current, signum)[](#l4.144)
- @unittest.skipUnless(hasattr(signal, 'sigwait'),
'need signal.sigwait()')[](#l4.147)
- def test_sigwait(self):
old_handler = signal.signal(signal.SIGALRM, self.handler)[](#l4.149)
self.addCleanup(signal.signal, signal.SIGALRM, old_handler)[](#l4.150)
signal.alarm(1)[](#l4.152)
self.assertEqual(signal.sigwait([signal.SIGALRM]), signal.SIGALRM)[](#l4.153)
- @unittest.skipUnless(hasattr(signal, 'pthread_sigmask'),
'need signal.pthread_sigmask()')[](#l4.156)
- def test_pthread_sigmask_arguments(self):
self.assertRaises(TypeError, signal.pthread_sigmask)[](#l4.158)
self.assertRaises(TypeError, signal.pthread_sigmask, 1)[](#l4.159)
self.assertRaises(TypeError, signal.pthread_sigmask, 1, 2, 3)[](#l4.160)
self.assertRaises(OSError, signal.pthread_sigmask, 1700, [])[](#l4.161)
- @unittest.skipUnless(hasattr(signal, 'pthread_sigmask'),
'need signal.pthread_sigmask()')[](#l4.164)
- def test_pthread_sigmask(self):
test_blocked_signals = self.can_test_blocked_signals(False)[](#l4.166)
signum = signal.SIGUSR1[](#l4.167)
# Install our signal handler old_handler = signal.signal(signum, self.handler) @@ -537,13 +612,13 @@ class PendingSignalsTests(unittest.TestC old_mask = signal.pthread_sigmask(signal.SIG_UNBLOCK, [signum]) self.addCleanup(signal.pthread_sigmask, signal.SIG_SETMASK, old_mask) with self.assertRaises(ZeroDivisionError):
os.kill(pid, signum)[](#l4.175)
self.kill(signum)[](#l4.176)
# Block and then raise SIGUSR1. The signal is blocked: the signal # handler is not called, and the signal is now pending signal.pthread_sigmask(signal.SIG_BLOCK, [signum])
if can_test_blocked_signals:[](#l4.181)
os.kill(pid, signum)[](#l4.182)
if test_blocked_signals:[](#l4.183)
self.kill(signum)[](#l4.184)
# Check the new mask blocked = self.read_sigmask() @@ -551,14 +626,14 @@ class PendingSignalsTests(unittest.TestC self.assertEqual(old_mask ^ blocked, {signum}) # Unblock SIGUSR1
if can_test_blocked_signals:[](#l4.192)
if test_blocked_signals:[](#l4.193) with self.assertRaises(ZeroDivisionError):[](#l4.194) # unblock the pending signal calls immediatly the signal handler[](#l4.195) signal.pthread_sigmask(signal.SIG_UNBLOCK, [signum])[](#l4.196) else:[](#l4.197) signal.pthread_sigmask(signal.SIG_UNBLOCK, [signum])[](#l4.198) with self.assertRaises(ZeroDivisionError):[](#l4.199)
os.kill(pid, signum)[](#l4.200)
self.kill(signum)[](#l4.201)
# Check the new mask unblocked = self.read_sigmask() @@ -570,7 +645,7 @@ class PendingSignalsTests(unittest.TestC def test_main(): try:
support.run_unittest(BasicSignalTests, InterProcessSignalTests,[](#l4.209)
support.run_unittest(PosixTests, InterProcessSignalTests,[](#l4.210) WakeupSignalTests, SiginterruptTest,[](#l4.211) ItimerTest, WindowsSignalTests,[](#l4.212) PendingSignalsTests)[](#l4.213)
--- a/Misc/NEWS +++ b/Misc/NEWS @@ -140,6 +140,9 @@ Core and Builtins Library ------- +- Issue #8407: Add pthread_kill(), sigpending() and sigwait() functions to the
--- a/Modules/signalmodule.c +++ b/Modules/signalmodule.c @@ -503,7 +503,7 @@ PyDoc_STRVAR(getitimer_doc, Returns current value of given itimer."); #endif -#ifdef PYPTHREAD_SIGMASK +#if defined(PYPTHREAD_SIGMASK) || defined(HAVE_SIGWAIT) /* Convert an iterable to a sigset. Return 0 on success, return -1 and raise an exception on error. / @@ -551,7 +551,9 @@ error: Py_XDECREF(iterator); return result; } +#endif +#if defined(PYPTHREAD_SIGMASK) || defined(HAVE_SIGPENDING) static PyObject sigset_to_set(sigset_t mask) { @@ -585,7 +587,9 @@ sigset_to_set(sigset_t mask) } return result; } +#endif +#ifdef PYPTHREAD_SIGMASK static PyObject * signal_pthread_sigmask(PyObject *self, PyObject *args) { @@ -603,7 +607,7 @@ signal_pthread_sigmask(PyObject *self, P err = pthread_sigmask(how, &mask, &previous); if (err != 0) { errno = err;
PyErr_SetFromErrno(PyExc_RuntimeError);[](#l6.36)
} @@ -621,6 +625,88 @@ Fetch and/or change the signal mask of tPyErr_SetFromErrno(PyExc_OSError);[](#l6.37) return NULL;[](#l6.38)
#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);[](#l6.53)
- return sigset_to_set(mask);
+} + +PyDoc_STRVAR(signal_sigpending_doc, +"sigpending() -> list\n[](#l6.58) +\n[](#l6.59) +Examine pending signals."); +#endif /* #ifdef HAVE_SIGPENDING */ + + +#ifdef HAVE_SIGWAIT +static PyObject * +signal_sigwait(PyObject *self, PyObject *args) +{
- err = sigwait(&set, &signum);
- if (err) {
errno = err;[](#l6.80)
return PyErr_SetFromErrno(PyExc_OSError);[](#l6.81)
- }
+} + +PyDoc_STRVAR(signal_sigwait_doc, +"sigwait(sigset) -> signum\n[](#l6.88) +\n[](#l6.89) +Wait a signal."); +#endif /* #ifdef HAVE_SIGPENDING */ + + +#if defined(HAVE_PTHREAD_KILL) && defined(WITH_THREAD) +static PyObject * +signal_pthread_kill(PyObject *self, PyObject *args) +{
- err = pthread_kill(tid, signum);
- if (err != 0) {
errno = err;[](#l6.107)
PyErr_SetFromErrno(PyExc_OSError);[](#l6.108)
return NULL;[](#l6.109)
- }
- /* the signal may have been send to the current thread */
- if (PyErr_CheckSignals())
return NULL;[](#l6.114)
+} + +PyDoc_STRVAR(signal_pthread_kill_doc, +"pthread_kill(thread_id, signum)\n[](#l6.120) +\n[](#l6.121) +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[] = { #ifdef HAVE_ALARM @@ -644,10 +730,22 @@ static PyMethodDef signal_methods[] = { #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},[](#l6.136)
+#endif #ifdef PYPTHREAD_SIGMASK {"pthread_sigmask", (PyCFunction)signal_pthread_sigmask, METH_VARARGS, signal_pthread_sigmask_doc}, #endif +#ifdef HAVE_SIGPENDING
+#endif {NULL, NULL} /* sentinel */ };
--- a/configure +++ b/configure @@ -9258,11 +9258,12 @@ for ac_func in alarm accept4 setitimer g initgroups kill killpg lchmod lchown lockf linkat lstat lutimes mbrtowc mkdirat mkfifo [](#l7.4) mkfifoat mknod mknodat mktime mremap nice openat pathconf pause plock poll [](#l7.5) posix_fallocate posix_fadvise pread [](#l7.6)
- pthread_init putenv pwrite readlink readlinkat readv realpath renameat [](#l7.7)
- pthread_init pthread_kill putenv pwrite readlink readlinkat readv realpath renameat [](#l7.8) select sem_open sem_timedwait sem_getvalue sem_unlink sendfile setegid seteuid [](#l7.9) setgid sethostname [](#l7.10) setlocale setregid setreuid setresuid setresgid setsid setpgid setpgrp setpriority setuid setvbuf [](#l7.11)
- sigaction sigaltstack siginterrupt sigrelse snprintf strftime strlcpy symlinkat sync [](#l7.12)
- sigaction sigaltstack siginterrupt sigpending [](#l7.13)
- sigrelse sigwait snprintf strftime strlcpy symlinkat sync [](#l7.14) sysconf tcgetpgrp tcsetpgrp tempnam timegm times tmpfile tmpnam tmpnam_r [](#l7.15) truncate uname unlinkat unsetenv utimensat utimes waitid waitpid wait3 wait4 [](#l7.16) wcscoll wcsftime wcsxfrm writev _getpty
--- a/configure.in +++ b/configure.in @@ -2503,11 +2503,12 @@ AC_CHECK_FUNCS(alarm accept4 setitimer g initgroups kill killpg lchmod lchown lockf linkat lstat lutimes mbrtowc mkdirat mkfifo [](#l8.4) mkfifoat mknod mknodat mktime mremap nice openat pathconf pause plock poll [](#l8.5) posix_fallocate posix_fadvise pread [](#l8.6)
- pthread_init putenv pwrite readlink readlinkat readv realpath renameat [](#l8.7)
- pthread_init pthread_kill putenv pwrite readlink readlinkat readv realpath renameat [](#l8.8) select sem_open sem_timedwait sem_getvalue sem_unlink sendfile setegid seteuid [](#l8.9) setgid sethostname [](#l8.10) setlocale setregid setreuid setresuid setresgid setsid setpgid setpgrp setpriority setuid setvbuf [](#l8.11)
- sigaction sigaltstack siginterrupt sigrelse snprintf strftime strlcpy symlinkat sync [](#l8.12)
- sigaction sigaltstack siginterrupt sigpending [](#l8.13)
- sigrelse sigwait snprintf strftime strlcpy symlinkat sync [](#l8.14) sysconf tcgetpgrp tcsetpgrp tempnam timegm times tmpfile tmpnam tmpnam_r [](#l8.15) truncate uname unlinkat unsetenv utimensat utimes waitid waitpid wait3 wait4 [](#l8.16) wcscoll wcsftime wcsxfrm writev _getpty)
--- a/pyconfig.h.in
+++ b/pyconfig.h.in
@@ -590,6 +590,9 @@
/* Define to 1 if you have the pthread_sigmask' function. */[](#l9.4) #undef HAVE_PTHREAD_SIGMASK[](#l9.5) [](#l9.6) +/* Define to 1 if you have the
pthread_kill' function. /
+#undef HAVE_PTHREAD_KILL
+
/ Define to 1 if you have the <pty.h> header file. /
#undef HAVE_PTY_H
@@ -719,12 +722,18 @@
/ Define to 1 if you have the siginterrupt' function. */[](#l9.14) #undef HAVE_SIGINTERRUPT[](#l9.15) [](#l9.16) +/* Define to 1 if you have the
sigpending' function. /
+#undef HAVE_SIGPENDING
+
/ Define to 1 if you have the <signal.h> header file. /
#undef HAVE_SIGNAL_H
/ Define to 1 if you have the sigrelse' function. */[](#l9.23) #undef HAVE_SIGRELSE[](#l9.24) [](#l9.25) +/* Define to 1 if you have the
sigwait' function. /
+#undef HAVE_SIGWAIT
+
/ Define to 1 if you have the `snprintf' function. */
#undef HAVE_SNPRINTF