Skip to content

Commit 823725e

Browse files
committed
Merged revisions 61846-61847 via svnmerge from
svn+ssh://pythondev@svn.python.org/python/trunk ........ r61846 | martin.v.loewis | 2008-03-24 13:57:53 +0100 (Mo, 24 Mär 2008) | 2 lines Install 2to3 script. ........ r61847 | martin.v.loewis | 2008-03-24 14:31:16 +0100 (Mo, 24 Mär 2008) | 2 lines Patch python#2240: Implement signal.setitimer and signal.getitimer. ........
1 parent 6cf49cf commit 823725e

9 files changed

Lines changed: 314 additions & 16 deletions

File tree

Doc/library/signal.rst

Lines changed: 60 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -39,12 +39,13 @@ rules for working with signals and their handlers:
3939
* Some care must be taken if both signals and threads are used in the same
4040
program. The fundamental thing to remember in using signals and threads
4141
simultaneously is: always perform :func:`signal` operations in the main thread
42-
of execution. Any thread can perform an :func:`alarm`, :func:`getsignal`, or
43-
:func:`pause`; only the main thread can set a new signal handler, and the main
44-
thread will be the only one to receive signals (this is enforced by the Python
45-
:mod:`signal` module, even if the underlying thread implementation supports
46-
sending signals to individual threads). This means that signals can't be used
47-
as a means of inter-thread communication. Use locks instead.
42+
of execution. Any thread can perform an :func:`alarm`, :func:`getsignal`,
43+
:func:`pause`, :func:`setitimer` or :func:`getitimer`; only the main thread
44+
can set a new signal handler, and the main thread will be the only one to
45+
receive signals (this is enforced by the Python :mod:`signal` module, even
46+
if the underlying thread implementation supports sending signals to
47+
individual threads). This means that signals can't be used as a means of
48+
inter-thread communication. Use locks instead.
4849

4950
The variables defined in the :mod:`signal` module are:
5051

@@ -78,6 +79,36 @@ The variables defined in the :mod:`signal` module are:
7879

7980
One more than the number of the highest signal number.
8081

82+
83+
.. data:: ITIMER_REAL
84+
85+
Decrements interval timer in real time, and delivers SIGALRM upon expiration.
86+
87+
88+
.. data:: ITIMER_VIRTUAL
89+
90+
Decrements interval timer only when the process is executing, and delivers
91+
SIGVTALRM upon expiration.
92+
93+
94+
.. data:: ITIMER_PROF
95+
96+
Decrements interval timer both when the process executes and when the
97+
system is executing on behalf of the process. Coupled with ITIMER_VIRTUAL,
98+
this timer is usually used to profile the time spent by the application
99+
in user and kernel space. SIGPROF is delivered upon expiration.
100+
101+
102+
The :mod:`signal` module defines one exception:
103+
104+
.. exception:: ItimerError
105+
106+
Raised to signal an error from the underlying :func:`setitimer` or
107+
:func:`getitimer` implementation. Expect this error if an invalid
108+
interval timer or a negative time is passed to :func:`setitimer`.
109+
This error is a subtype of :exc:`IOError`.
110+
111+
81112
The :mod:`signal` module defines the following functions:
82113

83114

@@ -110,6 +141,29 @@ The :mod:`signal` module defines the following functions:
110141
:manpage:`signal(2)`.)
111142

112143

144+
.. function:: setitimer(which, seconds[, interval])
145+
146+
Sets given itimer (one of :const:`signal.ITIMER_REAL`,
147+
:const:`signal.ITIMER_VIRTUAL` or :const:`signal.ITIMER_PROF`) especified
148+
by *which* to fire after *seconds* (float is accepted, different from
149+
:func:`alarm`) and after that every *interval* seconds. The interval
150+
timer specified by *which* can be cleared by setting seconds to zero.
151+
152+
The old values are returned as a tuple: (delay, interval).
153+
154+
Attempting to pass an invalid interval timer will cause a
155+
:exc:`ItimerError`.
156+
157+
.. versionadded:: 2.6
158+
159+
160+
.. function:: getitimer(which)
161+
162+
Returns current value of a given itimer especified by *which*.
163+
164+
.. versionadded:: 2.6
165+
166+
113167
.. function:: set_wakeup_fd(fd)
114168

115169
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:
124178
exception to be raised.
125179

126180

127-
128181
.. function:: siginterrupt(signalnum, flag)
129182

130183
Change system call restart behaviour: if *flag* is :const:`False`, system calls

Lib/test/test_signal.py

Lines changed: 85 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -258,9 +258,93 @@ def test_siginterrupt_off(self):
258258
i=self.readpipe_interrupted(lambda: signal.siginterrupt(self.signum, 0))
259259
self.assertEquals(i, False)
260260

261+
class ItimerTest(unittest.TestCase):
262+
def setUp(self):
263+
self.hndl_called = False
264+
self.hndl_count = 0
265+
self.itimer = None
266+
267+
def tearDown(self):
268+
if self.itimer is not None: # test_itimer_exc doesn't change this attr
269+
# just ensure that itimer is stopped
270+
signal.setitimer(self.itimer, 0)
271+
272+
def sig_alrm(self, *args):
273+
self.hndl_called = True
274+
if test_support.verbose:
275+
print("SIGALRM handler invoked", args)
276+
277+
def sig_vtalrm(self, *args):
278+
self.hndl_called = True
279+
280+
if self.hndl_count > 3:
281+
# it shouldn't be here, because it should have been disabled.
282+
raise signal.ItimerError("setitimer didn't disable ITIMER_VIRTUAL "
283+
"timer.")
284+
elif self.hndl_count == 3:
285+
# disable ITIMER_VIRTUAL, this function shouldn't be called anymore
286+
signal.setitimer(signal.ITIMER_VIRTUAL, 0)
287+
if test_support.verbose:
288+
print("last SIGVTALRM handler call")
289+
290+
self.hndl_count += 1
291+
292+
if test_support.verbose:
293+
print("SIGVTALRM handler invoked", args)
294+
295+
def sig_prof(self, *args):
296+
self.hndl_called = True
297+
signal.setitimer(signal.ITIMER_PROF, 0)
298+
299+
if test_support.verbose:
300+
print("SIGPROF handler invoked", args)
301+
302+
def test_itimer_exc(self):
303+
# XXX I'm assuming -1 is an invalid itimer, but maybe some platform
304+
# defines it ?
305+
self.assertRaises(signal.ItimerError, signal.setitimer, -1, 0)
306+
# negative time
307+
self.assertRaises(signal.ItimerError, signal.setitimer,
308+
signal.ITIMER_REAL, -1)
309+
310+
def test_itimer_real(self):
311+
self.itimer = signal.ITIMER_REAL
312+
signal.signal(signal.SIGALRM, self.sig_alrm)
313+
signal.setitimer(self.itimer, 1.0)
314+
if test_support.verbose:
315+
print("\ncall pause()...")
316+
signal.pause()
317+
318+
self.assertEqual(self.hndl_called, True)
319+
320+
def test_itimer_virtual(self):
321+
self.itimer = signal.ITIMER_VIRTUAL
322+
signal.signal(signal.SIGVTALRM, self.sig_vtalrm)
323+
signal.setitimer(self.itimer, 0.3, 0.2)
324+
325+
for i in range(100000000):
326+
if signal.getitimer(self.itimer) == (0.0, 0.0):
327+
break # sig_vtalrm handler stopped this itimer
328+
329+
# virtual itimer should be (0.0, 0.0) now
330+
self.assertEquals(signal.getitimer(self.itimer), (0.0, 0.0))
331+
# and the handler should have been called
332+
self.assertEquals(self.hndl_called, True)
333+
334+
def test_itimer_prof(self):
335+
self.itimer = signal.ITIMER_PROF
336+
signal.signal(signal.SIGPROF, self.sig_prof)
337+
signal.setitimer(self.itimer, 0.2)
338+
339+
for i in range(100000000):
340+
if signal.getitimer(self.itimer) == (0.0, 0.0):
341+
break # sig_prof handler stopped this itimer
342+
343+
self.assertEqual(self.hndl_called, True)
344+
261345
def test_main():
262346
test_support.run_unittest(BasicSignalTests, InterProcessSignalTests,
263-
WakeupSignalTests, SiginterruptTest)
347+
WakeupSignalTests, SiginterruptTest, ItimerTest)
264348

265349

266350
if __name__ == "__main__":

Misc/ACKS

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -531,6 +531,7 @@ Fran
531531
Zach Pincus
532532
Michael Piotrowski
533533
Antoine Pitrou
534+
Guilherme Polo
534535
Michael Pomraning
535536
Iustin Pop
536537
John Popplewell

Modules/signalmodule.c

Lines changed: 149 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
#include <signal.h>
1414

1515
#include <sys/stat.h>
16+
#include <sys/time.h>
1617

1718
#ifndef SIG_ERR
1819
#define SIG_ERR ((PyOS_sighandler_t)(-1))
@@ -93,6 +94,49 @@ static PyObject *IntHandler;
9394

9495
static PyOS_sighandler_t old_siginthandler = SIG_DFL;
9596

97+
#ifdef HAVE_GETITIMER
98+
static PyObject *ItimerError;
99+
100+
/* auxiliary functions for setitimer/getitimer */
101+
static void
102+
timeval_from_double(double d, struct timeval *tv)
103+
{
104+
tv->tv_sec = floor(d);
105+
tv->tv_usec = fmod(d, 1.0) * 1000000.0;
106+
}
107+
108+
static inline double
109+
double_from_timeval(struct timeval *tv)
110+
{
111+
return tv->tv_sec + (double)(tv->tv_usec / 1000000.0);
112+
}
113+
114+
static PyObject *
115+
itimer_retval(struct itimerval *iv)
116+
{
117+
PyObject *r, *v;
118+
119+
r = PyTuple_New(2);
120+
if (r == NULL)
121+
return NULL;
122+
123+
if(!(v = PyFloat_FromDouble(double_from_timeval(&iv->it_value)))) {
124+
Py_DECREF(r);
125+
return NULL;
126+
}
127+
128+
PyTuple_SET_ITEM(r, 0, v);
129+
130+
if(!(v = PyFloat_FromDouble(double_from_timeval(&iv->it_interval)))) {
131+
Py_DECREF(r);
132+
return NULL;
133+
}
134+
135+
PyTuple_SET_ITEM(r, 1, v);
136+
137+
return r;
138+
}
139+
#endif
96140

97141
static PyObject *
98142
signal_default_int_handler(PyObject *self, PyObject *args)
@@ -347,10 +391,76 @@ PySignal_SetWakeupFd(int fd)
347391
}
348392

349393

394+
#ifdef HAVE_SETITIMER
395+
static PyObject *
396+
signal_setitimer(PyObject *self, PyObject *args)
397+
{
398+
double first;
399+
double interval = 0;
400+
int which;
401+
struct itimerval new, old;
402+
403+
if(!PyArg_ParseTuple(args, "id|d:setitimer", &which, &first, &interval))
404+
return NULL;
405+
406+
timeval_from_double(first, &new.it_value);
407+
timeval_from_double(interval, &new.it_interval);
408+
/* Let OS check "which" value */
409+
if (setitimer(which, &new, &old) != 0) {
410+
PyErr_SetFromErrno(ItimerError);
411+
return NULL;
412+
}
413+
414+
return itimer_retval(&old);
415+
}
416+
417+
PyDoc_STRVAR(setitimer_doc,
418+
"setitimer(which, seconds[, interval])\n\
419+
\n\
420+
Sets given itimer (one of ITIMER_REAL, ITIMER_VIRTUAL\n\
421+
or ITIMER_PROF) to fire after value seconds and after\n\
422+
that every interval seconds.\n\
423+
The itimer can be cleared by setting seconds to zero.\n\
424+
\n\
425+
Returns old values as a tuple: (delay, interval).");
426+
#endif
427+
428+
429+
#ifdef HAVE_GETITIMER
430+
static PyObject *
431+
signal_getitimer(PyObject *self, PyObject *args)
432+
{
433+
int which;
434+
struct itimerval old;
435+
436+
if (!PyArg_ParseTuple(args, "i:getitimer", &which))
437+
return NULL;
438+
439+
if (getitimer(which, &old) != 0) {
440+
PyErr_SetFromErrno(ItimerError);
441+
return NULL;
442+
}
443+
444+
return itimer_retval(&old);
445+
}
446+
447+
PyDoc_STRVAR(getitimer_doc,
448+
"getitimer(which)\n\
449+
\n\
450+
Returns current value of given itimer.");
451+
#endif
452+
453+
350454
/* List of functions defined in the module */
351455
static PyMethodDef signal_methods[] = {
352456
#ifdef HAVE_ALARM
353457
{"alarm", signal_alarm, METH_VARARGS, alarm_doc},
458+
#endif
459+
#ifdef HAVE_SETITIMER
460+
{"setitimer", signal_setitimer, METH_VARARGS, setitimer_doc},
461+
#endif
462+
#ifdef HAVE_GETITIMER
463+
{"getitimer", signal_getitimer, METH_VARARGS, getitimer_doc},
354464
#endif
355465
{"signal", signal_signal, METH_VARARGS, signal_doc},
356466
{"getsignal", signal_getsignal, METH_VARARGS, getsignal_doc},
@@ -374,19 +484,32 @@ PyDoc_STRVAR(module_doc,
374484
Functions:\n\
375485
\n\
376486
alarm() -- cause SIGALRM after a specified time [Unix only]\n\
487+
setitimer() -- cause a signal (described below) after a specified\n\
488+
float time and the timer may restart then [Unix only]\n\
489+
getitimer() -- get current value of timer [Unix only]\n\
377490
signal() -- set the action for a given signal\n\
378491
getsignal() -- get the signal action for a given signal\n\
379492
pause() -- wait until a signal arrives [Unix only]\n\
380493
default_int_handler() -- default SIGINT handler\n\
381494
\n\
382-
Constants:\n\
383-
\n\
495+
signal constants:\n\
384496
SIG_DFL -- used to refer to the system default handler\n\
385497
SIG_IGN -- used to ignore the signal\n\
386498
NSIG -- number of defined signals\n\
387-
\n\
388499
SIGINT, SIGTERM, etc. -- signal numbers\n\
389500
\n\
501+
itimer constants:\n\
502+
ITIMER_REAL -- decrements in real time, and delivers SIGALRM upon\n\
503+
expiration\n\
504+
ITIMER_VIRTUAL -- decrements only when the process is executing,\n\
505+
and delivers SIGVTALRM upon expiration\n\
506+
ITIMER_PROF -- decrements both when the process is executing and\n\
507+
when the system is executing on behalf of the process.\n\
508+
Coupled with ITIMER_VIRTUAL, this timer is usually\n\
509+
used to profile the time spent by the application\n\
510+
in user and kernel space. SIGPROF is delivered upon\n\
511+
expiration.\n\
512+
\n\n\
390513
*** IMPORTANT NOTICE ***\n\
391514
A signal handler function is called with two arguments:\n\
392515
the first is the signal number, the second is the interrupted stack frame.");
@@ -639,6 +762,29 @@ initsignal(void)
639762
PyDict_SetItemString(d, "SIGINFO", x);
640763
Py_XDECREF(x);
641764
#endif
765+
766+
#ifdef ITIMER_REAL
767+
x = PyLong_FromLong(ITIMER_REAL);
768+
PyDict_SetItemString(d, "ITIMER_REAL", x);
769+
Py_DECREF(x);
770+
#endif
771+
#ifdef ITIMER_VIRTUAL
772+
x = PyLong_FromLong(ITIMER_VIRTUAL);
773+
PyDict_SetItemString(d, "ITIMER_VIRTUAL", x);
774+
Py_DECREF(x);
775+
#endif
776+
#ifdef ITIMER_PROF
777+
x = PyLong_FromLong(ITIMER_PROF);
778+
PyDict_SetItemString(d, "ITIMER_PROF", x);
779+
Py_DECREF(x);
780+
#endif
781+
782+
#if defined (HAVE_SETITIMER) || defined (HAVE_GETITIMER)
783+
ItimerError = PyErr_NewException("signal.ItimerError",
784+
PyExc_IOError, NULL);
785+
PyDict_SetItemString(d, "ItimerError", ItimerError);
786+
#endif
787+
642788
if (!PyErr_Occurred())
643789
return;
644790

0 commit comments

Comments
 (0)