Patch #2240: Implement signal.setitimer and signal.getitimer.

This commit is contained in:
Martin v. Löwis 2008-03-24 13:31:16 +00:00
parent cdbc977c03
commit aef18b1c67
8 changed files with 313 additions and 16 deletions

View File

@ -39,12 +39,13 @@ rules for working with signals and their handlers:
* Some care must be taken if both signals and threads are used in the same * Some care must be taken if both signals and threads are used in the same
program. The fundamental thing to remember in using signals and threads program. The fundamental thing to remember in using signals and threads
simultaneously is: always perform :func:`signal` operations in the main thread simultaneously is: always perform :func:`signal` operations in the main thread
of execution. Any thread can perform an :func:`alarm`, :func:`getsignal`, or of execution. Any thread can perform an :func:`alarm`, :func:`getsignal`,
:func:`pause`; only the main thread can set a new signal handler, and the main :func:`pause`, :func:`setitimer` or :func:`getitimer`; only the main thread
thread will be the only one to receive signals (this is enforced by the Python can set a new signal handler, and the main thread will be the only one to
:mod:`signal` module, even if the underlying thread implementation supports receive signals (this is enforced by the Python :mod:`signal` module, even
sending signals to individual threads). This means that signals can't be used if the underlying thread implementation supports sending signals to
as a means of inter-thread communication. Use locks instead. individual threads). This means that signals can't be used as a means of
inter-thread communication. Use locks instead.
The variables defined in the :mod:`signal` module are: The variables defined in the :mod:`signal` module are:
@ -78,6 +79,36 @@ The variables defined in the :mod:`signal` module are:
One more than the number of the highest signal number. One more than the number of the highest signal number.
.. data:: ITIMER_REAL
Decrements interval timer in real time, and delivers SIGALRM upon expiration.
.. data:: ITIMER_VIRTUAL
Decrements interval timer only when the process is executing, and delivers
SIGVTALRM upon expiration.
.. data:: ITIMER_PROF
Decrements interval timer both when the process executes and when the
system is executing on behalf of the process. Coupled with ITIMER_VIRTUAL,
this timer is usually used to profile the time spent by the application
in user and kernel space. SIGPROF is delivered upon expiration.
The :mod:`signal` module defines one exception:
.. exception:: ItimerError
Raised to signal an error from the underlying :func:`setitimer` or
:func:`getitimer` implementation. Expect this error if an invalid
interval timer or a negative time is passed to :func:`setitimer`.
This error is a subtype of :exc:`IOError`.
The :mod:`signal` module defines the following functions: The :mod:`signal` module defines the following functions:
@ -110,6 +141,29 @@ The :mod:`signal` module defines the following functions:
:manpage:`signal(2)`.) :manpage:`signal(2)`.)
.. function:: setitimer(which, seconds[, interval])
Sets given itimer (one of :const:`signal.ITIMER_REAL`,
:const:`signal.ITIMER_VIRTUAL` or :const:`signal.ITIMER_PROF`) especified
by *which* to fire after *seconds* (float is accepted, different from
:func:`alarm`) and after that every *interval* seconds. The interval
timer specified by *which* can be cleared by setting seconds to zero.
The old values are returned as a tuple: (delay, interval).
Attempting to pass an invalid interval timer will cause a
:exc:`ItimerError`.
.. versionadded:: 2.6
.. function:: getitimer(which)
Returns current value of a given itimer especified by *which*.
.. versionadded:: 2.6
.. function:: set_wakeup_fd(fd) .. function:: set_wakeup_fd(fd)
Set the wakeup fd to *fd*. When a signal is received, a ``'\0'`` byte is Set the wakeup fd to *fd*. When a signal is received, a ``'\0'`` byte is
@ -124,7 +178,6 @@ The :mod:`signal` module defines the following functions:
exception to be raised. exception to be raised.
.. function:: siginterrupt(signalnum, flag) .. function:: siginterrupt(signalnum, flag)
Change system call restart behaviour: if *flag* is :const:`False`, system calls Change system call restart behaviour: if *flag* is :const:`False`, system calls

View File

@ -258,9 +258,93 @@ class SiginterruptTest(unittest.TestCase):
i=self.readpipe_interrupted(lambda: signal.siginterrupt(self.signum, 0)) i=self.readpipe_interrupted(lambda: signal.siginterrupt(self.signum, 0))
self.assertEquals(i, False) self.assertEquals(i, False)
class ItimerTest(unittest.TestCase):
def setUp(self):
self.hndl_called = False
self.hndl_count = 0
self.itimer = None
def tearDown(self):
if self.itimer is not None: # test_itimer_exc doesn't change this attr
# just ensure that itimer is stopped
signal.setitimer(self.itimer, 0)
def sig_alrm(self, *args):
self.hndl_called = True
if test_support.verbose:
print("SIGALRM handler invoked", args)
def sig_vtalrm(self, *args):
self.hndl_called = True
if self.hndl_count > 3:
# it shouldn't be here, because it should have been disabled.
raise signal.ItimerError("setitimer didn't disable ITIMER_VIRTUAL "
"timer.")
elif self.hndl_count == 3:
# disable ITIMER_VIRTUAL, this function shouldn't be called anymore
signal.setitimer(signal.ITIMER_VIRTUAL, 0)
if test_support.verbose:
print("last SIGVTALRM handler call")
self.hndl_count += 1
if test_support.verbose:
print("SIGVTALRM handler invoked", args)
def sig_prof(self, *args):
self.hndl_called = True
signal.setitimer(signal.ITIMER_PROF, 0)
if test_support.verbose:
print("SIGPROF handler invoked", args)
def test_itimer_exc(self):
# XXX I'm assuming -1 is an invalid itimer, but maybe some platform
# defines it ?
self.assertRaises(signal.ItimerError, signal.setitimer, -1, 0)
# negative time
self.assertRaises(signal.ItimerError, signal.setitimer,
signal.ITIMER_REAL, -1)
def test_itimer_real(self):
self.itimer = signal.ITIMER_REAL
signal.signal(signal.SIGALRM, self.sig_alrm)
signal.setitimer(self.itimer, 1.0)
if test_support.verbose:
print("\ncall pause()...")
signal.pause()
self.assertEqual(self.hndl_called, True)
def test_itimer_virtual(self):
self.itimer = signal.ITIMER_VIRTUAL
signal.signal(signal.SIGVTALRM, self.sig_vtalrm)
signal.setitimer(self.itimer, 0.3, 0.2)
for i in xrange(100000000):
if signal.getitimer(self.itimer) == (0.0, 0.0):
break # sig_vtalrm handler stopped this itimer
# virtual itimer should be (0.0, 0.0) now
self.assertEquals(signal.getitimer(self.itimer), (0.0, 0.0))
# and the handler should have been called
self.assertEquals(self.hndl_called, True)
def test_itimer_prof(self):
self.itimer = signal.ITIMER_PROF
signal.signal(signal.SIGPROF, self.sig_prof)
signal.setitimer(self.itimer, 0.2)
for i in xrange(100000000):
if signal.getitimer(self.itimer) == (0.0, 0.0):
break # sig_prof handler stopped this itimer
self.assertEqual(self.hndl_called, True)
def test_main(): def test_main():
test_support.run_unittest(BasicSignalTests, InterProcessSignalTests, test_support.run_unittest(BasicSignalTests, InterProcessSignalTests,
WakeupSignalTests, SiginterruptTest) WakeupSignalTests, SiginterruptTest, ItimerTest)
if __name__ == "__main__": if __name__ == "__main__":

View File

@ -528,6 +528,7 @@ Fran
Zach Pincus Zach Pincus
Michael Piotrowski Michael Piotrowski
Antoine Pitrou Antoine Pitrou
Guilherme Polo
Michael Pomraning Michael Pomraning
Iustin Pop Iustin Pop
John Popplewell John Popplewell

View File

@ -60,6 +60,11 @@ Core and builtins
- Issue #2143: Fix embedded readline() hang on SSL socket EOF. - Issue #2143: Fix embedded readline() hang on SSL socket EOF.
Extensions Modules
------------------
- Patch #2240: Implement signal.setitimer and signal.getitimer.
Library Library
------- -------

View File

@ -13,6 +13,7 @@
#include <signal.h> #include <signal.h>
#include <sys/stat.h> #include <sys/stat.h>
#include <sys/time.h>
#ifndef SIG_ERR #ifndef SIG_ERR
#define SIG_ERR ((PyOS_sighandler_t)(-1)) #define SIG_ERR ((PyOS_sighandler_t)(-1))
@ -93,6 +94,49 @@ static PyObject *IntHandler;
static PyOS_sighandler_t old_siginthandler = SIG_DFL; static PyOS_sighandler_t old_siginthandler = SIG_DFL;
#ifdef HAVE_GETITIMER
static PyObject *ItimerError;
/* auxiliary functions for setitimer/getitimer */
static void
timeval_from_double(double d, struct timeval *tv)
{
tv->tv_sec = floor(d);
tv->tv_usec = fmod(d, 1.0) * 1000000.0;
}
static inline double
double_from_timeval(struct timeval *tv)
{
return tv->tv_sec + (double)(tv->tv_usec / 1000000.0);
}
static PyObject *
itimer_retval(struct itimerval *iv)
{
PyObject *r, *v;
r = PyTuple_New(2);
if (r == NULL)
return NULL;
if(!(v = PyFloat_FromDouble(double_from_timeval(&iv->it_value)))) {
Py_DECREF(r);
return NULL;
}
PyTuple_SET_ITEM(r, 0, v);
if(!(v = PyFloat_FromDouble(double_from_timeval(&iv->it_interval)))) {
Py_DECREF(r);
return NULL;
}
PyTuple_SET_ITEM(r, 1, v);
return r;
}
#endif
static PyObject * static PyObject *
signal_default_int_handler(PyObject *self, PyObject *args) signal_default_int_handler(PyObject *self, PyObject *args)
@ -347,10 +391,76 @@ PySignal_SetWakeupFd(int fd)
} }
#ifdef HAVE_SETITIMER
static PyObject *
signal_setitimer(PyObject *self, PyObject *args)
{
double first;
double interval = 0;
int which;
struct itimerval new, old;
if(!PyArg_ParseTuple(args, "id|d:setitimer", &which, &first, &interval))
return NULL;
timeval_from_double(first, &new.it_value);
timeval_from_double(interval, &new.it_interval);
/* Let OS check "which" value */
if (setitimer(which, &new, &old) != 0) {
PyErr_SetFromErrno(ItimerError);
return NULL;
}
return itimer_retval(&old);
}
PyDoc_STRVAR(setitimer_doc,
"setitimer(which, seconds[, interval])\n\
\n\
Sets given itimer (one of ITIMER_REAL, ITIMER_VIRTUAL\n\
or ITIMER_PROF) to fire after value seconds and after\n\
that every interval seconds.\n\
The itimer can be cleared by setting seconds to zero.\n\
\n\
Returns old values as a tuple: (delay, interval).");
#endif
#ifdef HAVE_GETITIMER
static PyObject *
signal_getitimer(PyObject *self, PyObject *args)
{
int which;
struct itimerval old;
if (!PyArg_ParseTuple(args, "i:getitimer", &which))
return NULL;
if (getitimer(which, &old) != 0) {
PyErr_SetFromErrno(ItimerError);
return NULL;
}
return itimer_retval(&old);
}
PyDoc_STRVAR(getitimer_doc,
"getitimer(which)\n\
\n\
Returns current value of given itimer.");
#endif
/* List of functions defined in the module */ /* List of functions defined in the module */
static PyMethodDef signal_methods[] = { static PyMethodDef signal_methods[] = {
#ifdef HAVE_ALARM #ifdef HAVE_ALARM
{"alarm", signal_alarm, METH_VARARGS, alarm_doc}, {"alarm", signal_alarm, METH_VARARGS, alarm_doc},
#endif
#ifdef HAVE_SETITIMER
{"setitimer", signal_setitimer, METH_VARARGS, setitimer_doc},
#endif
#ifdef HAVE_GETITIMER
{"getitimer", signal_getitimer, METH_VARARGS, getitimer_doc},
#endif #endif
{"signal", signal_signal, METH_VARARGS, signal_doc}, {"signal", signal_signal, METH_VARARGS, signal_doc},
{"getsignal", signal_getsignal, METH_VARARGS, getsignal_doc}, {"getsignal", signal_getsignal, METH_VARARGS, getsignal_doc},
@ -374,19 +484,32 @@ PyDoc_STRVAR(module_doc,
Functions:\n\ Functions:\n\
\n\ \n\
alarm() -- cause SIGALRM after a specified time [Unix only]\n\ alarm() -- cause SIGALRM after a specified time [Unix only]\n\
setitimer() -- cause a signal (described below) after a specified\n\
float time and the timer may restart then [Unix only]\n\
getitimer() -- get current value of timer [Unix only]\n\
signal() -- set the action for a given signal\n\ signal() -- set the action for a given signal\n\
getsignal() -- get the signal action for a given signal\n\ getsignal() -- get the signal action for a given signal\n\
pause() -- wait until a signal arrives [Unix only]\n\ pause() -- wait until a signal arrives [Unix only]\n\
default_int_handler() -- default SIGINT handler\n\ default_int_handler() -- default SIGINT handler\n\
\n\ \n\
Constants:\n\ signal constants:\n\
\n\
SIG_DFL -- used to refer to the system default handler\n\ SIG_DFL -- used to refer to the system default handler\n\
SIG_IGN -- used to ignore the signal\n\ SIG_IGN -- used to ignore the signal\n\
NSIG -- number of defined signals\n\ NSIG -- number of defined signals\n\
\n\
SIGINT, SIGTERM, etc. -- signal numbers\n\ SIGINT, SIGTERM, etc. -- signal numbers\n\
\n\ \n\
itimer constants:\n\
ITIMER_REAL -- decrements in real time, and delivers SIGALRM upon\n\
expiration\n\
ITIMER_VIRTUAL -- decrements only when the process is executing,\n\
and delivers SIGVTALRM upon expiration\n\
ITIMER_PROF -- decrements both when the process is executing and\n\
when the system is executing on behalf of the process.\n\
Coupled with ITIMER_VIRTUAL, this timer is usually\n\
used to profile the time spent by the application\n\
in user and kernel space. SIGPROF is delivered upon\n\
expiration.\n\
\n\n\
*** IMPORTANT NOTICE ***\n\ *** IMPORTANT NOTICE ***\n\
A signal handler function is called with two arguments:\n\ A signal handler function is called with two arguments:\n\
the first is the signal number, the second is the interrupted stack frame."); the first is the signal number, the second is the interrupted stack frame.");
@ -639,6 +762,29 @@ initsignal(void)
PyDict_SetItemString(d, "SIGINFO", x); PyDict_SetItemString(d, "SIGINFO", x);
Py_XDECREF(x); Py_XDECREF(x);
#endif #endif
#ifdef ITIMER_REAL
x = PyLong_FromLong(ITIMER_REAL);
PyDict_SetItemString(d, "ITIMER_REAL", x);
Py_DECREF(x);
#endif
#ifdef ITIMER_VIRTUAL
x = PyLong_FromLong(ITIMER_VIRTUAL);
PyDict_SetItemString(d, "ITIMER_VIRTUAL", x);
Py_DECREF(x);
#endif
#ifdef ITIMER_PROF
x = PyLong_FromLong(ITIMER_PROF);
PyDict_SetItemString(d, "ITIMER_PROF", x);
Py_DECREF(x);
#endif
#if defined (HAVE_SETITIMER) || defined (HAVE_GETITIMER)
ItimerError = PyErr_NewException("signal.ItimerError",
PyExc_IOError, NULL);
PyDict_SetItemString(d, "ItimerError", ItimerError);
#endif
if (!PyErr_Occurred()) if (!PyErr_Occurred())
return; return;

8
configure vendored
View File

@ -1,5 +1,5 @@
#! /bin/sh #! /bin/sh
# From configure.in Revision: 61436 . # From configure.in Revision: 61722 .
# Guess values for system-dependent variables and create Makefiles. # Guess values for system-dependent variables and create Makefiles.
# Generated by GNU Autoconf 2.61 for python 2.6. # Generated by GNU Autoconf 2.61 for python 2.6.
# #
@ -15506,8 +15506,10 @@ echo "${ECHO_T}MACHDEP_OBJS" >&6; }
for ac_func in alarm bind_textdomain_codeset chown clock confstr \
ctermid execv fchmod fchown fork fpathconf ftime ftruncate \
for ac_func in alarm setitimer getitimer bind_textdomain_codeset chown \
clock confstr ctermid execv fchmod fchown fork fpathconf ftime ftruncate \
gai_strerror getgroups getlogin getloadavg getpeername getpgid getpid \ gai_strerror getgroups getlogin getloadavg getpeername getpgid getpid \
getpriority getpwent getspnam getspent getsid getwd \ getpriority getpwent getspnam getspent getsid getwd \
kill killpg lchmod lchown lstat mkfifo mknod mktime \ kill killpg lchmod lchown lstat mkfifo mknod mktime \

View File

@ -2303,8 +2303,8 @@ fi
AC_MSG_RESULT(MACHDEP_OBJS) AC_MSG_RESULT(MACHDEP_OBJS)
# checks for library functions # checks for library functions
AC_CHECK_FUNCS(alarm bind_textdomain_codeset chown clock confstr \ AC_CHECK_FUNCS(alarm setitimer getitimer bind_textdomain_codeset chown \
ctermid execv fchmod fchown fork fpathconf ftime ftruncate \ clock confstr ctermid execv fchmod fchown fork fpathconf ftime ftruncate \
gai_strerror getgroups getlogin getloadavg getpeername getpgid getpid \ gai_strerror getgroups getlogin getloadavg getpeername getpgid getpid \
getpriority getpwent getspnam getspent getsid getwd \ getpriority getpwent getspnam getspent getsid getwd \
kill killpg lchmod lchown lstat mkfifo mknod mktime \ kill killpg lchmod lchown lstat mkfifo mknod mktime \

View File

@ -243,6 +243,9 @@
/* Define this if you have the 6-arg version of gethostbyname_r(). */ /* Define this if you have the 6-arg version of gethostbyname_r(). */
#undef HAVE_GETHOSTBYNAME_R_6_ARG #undef HAVE_GETHOSTBYNAME_R_6_ARG
/* Define to 1 if you have the `getitimer' function. */
#undef HAVE_GETITIMER
/* Define to 1 if you have the `getloadavg' function. */ /* Define to 1 if you have the `getloadavg' function. */
#undef HAVE_GETLOADAVG #undef HAVE_GETLOADAVG
@ -501,6 +504,9 @@
/* Define if you have the 'setgroups' function. */ /* Define if you have the 'setgroups' function. */
#undef HAVE_SETGROUPS #undef HAVE_SETGROUPS
/* Define to 1 if you have the `setitimer' function. */
#undef HAVE_SETITIMER
/* Define to 1 if you have the `setlocale' function. */ /* Define to 1 if you have the `setlocale' function. */
#undef HAVE_SETLOCALE #undef HAVE_SETLOCALE