Issue #13964: signal.sigtimedwait() timeout is now a float instead of a tuple

Add a private API to convert an int or float to a C timespec structure.
This commit is contained in:
Victor Stinner 2012-03-02 22:54:03 +01:00
parent 1c13f84f55
commit 643cd68ea4
7 changed files with 106 additions and 20 deletions

View File

@ -369,12 +369,11 @@ The :mod:`signal` module defines the following functions:
.. versionadded:: 3.3
.. function:: sigtimedwait(sigset, (timeout_sec, timeout_nsec))
.. function:: sigtimedwait(sigset, timeout)
Like :func:`sigtimedwait`, but takes a tuple of ``(seconds, nanoseconds)``
as an additional argument specifying a timeout. If both *timeout_sec* and
*timeout_nsec* are specified as :const:`0`, a poll is performed. Returns
:const:`None` if a timeout occurs.
Like :func:`sigwaitinfo`, but takes an additional *timeout* argument
specifying a timeout. If *timeout* is specified as :const:`0`, a poll is
performed. Returns :const:`None` if a timeout occurs.
Availability: Unix (see the man page :manpage:`sigtimedwait(2)` for further
information).

View File

@ -3,6 +3,7 @@
#define Py_PYTIME_H
#include "pyconfig.h" /* include for defines */
#include "object.h"
/**************************************************************************
Symbols and macros to supply platform-independent interfaces to time related
@ -37,6 +38,16 @@ do { \
((tv_end.tv_sec - tv_start.tv_sec) + \
(tv_end.tv_usec - tv_start.tv_usec) * 0.000001)
#ifndef Py_LIMITED_API
/* Convert a number of seconds, int or float, to a timespec structure.
nsec is always in the range [0; 999999999]. For example, -1.2 is converted
to (-2, 800000000). */
PyAPI_FUNC(int) _PyTime_ObjectToTimespec(
PyObject *obj,
time_t *sec,
long *nsec);
#endif
/* Dummy to force linking. */
PyAPI_FUNC(void) _PyTime_Init(void);

View File

@ -662,7 +662,7 @@ class PendingSignalsTests(unittest.TestCase):
self.wait_helper(signal.SIGALRM, '''
def test(signum):
signal.alarm(1)
info = signal.sigtimedwait([signum], (10, 1000))
info = signal.sigtimedwait([signum], 10.1000)
if info.si_signo != signum:
raise Exception('info.si_signo != %s' % signum)
''')
@ -675,7 +675,7 @@ class PendingSignalsTests(unittest.TestCase):
def test(signum):
import os
os.kill(os.getpid(), signum)
info = signal.sigtimedwait([signum], (0, 0))
info = signal.sigtimedwait([signum], 0)
if info.si_signo != signum:
raise Exception('info.si_signo != %s' % signum)
''')
@ -685,7 +685,7 @@ class PendingSignalsTests(unittest.TestCase):
def test_sigtimedwait_timeout(self):
self.wait_helper(signal.SIGALRM, '''
def test(signum):
received = signal.sigtimedwait([signum], (1, 0))
received = signal.sigtimedwait([signum], 1.0)
if received is not None:
raise Exception("received=%r" % (received,))
''')
@ -694,9 +694,7 @@ class PendingSignalsTests(unittest.TestCase):
'need signal.sigtimedwait()')
def test_sigtimedwait_negative_timeout(self):
signum = signal.SIGALRM
self.assertRaises(ValueError, signal.sigtimedwait, [signum], (-1, -1))
self.assertRaises(ValueError, signal.sigtimedwait, [signum], (0, -1))
self.assertRaises(ValueError, signal.sigtimedwait, [signum], (-1, 0))
self.assertRaises(ValueError, signal.sigtimedwait, [signum], -1.0)
@unittest.skipUnless(hasattr(signal, 'sigwaitinfo'),
'need signal.sigwaitinfo()')

View File

@ -497,12 +497,31 @@ class TestStrftime4dyear(_TestStrftimeYear, _Test4dYear):
pass
class TestPytime(unittest.TestCase):
def test_timespec(self):
from _testcapi import pytime_object_to_timespec
for obj, timespec in (
(0, (0, 0)),
(-1, (-1, 0)),
(-1.0, (-1, 0)),
(-1e-9, (-1, 999999999)),
(-1.2, (-2, 800000000)),
(1.123456789, (1, 123456789)),
):
self.assertEqual(pytime_object_to_timespec(obj), timespec)
for invalid in (-(2 ** 100), -(2.0 ** 100.0), 2 ** 100, 2.0 ** 100.0):
self.assertRaises(OverflowError, pytime_object_to_timespec, invalid)
def test_main():
support.run_unittest(
TimeTestCase,
TestLocale,
TestAsctime4dyear,
TestStrftime4dyear)
TestStrftime4dyear,
TestPytime)
if __name__ == "__main__":
test_main()

View File

@ -2323,6 +2323,24 @@ run_in_subinterp(PyObject *self, PyObject *args)
return PyLong_FromLong(r);
}
static PyObject *
test_pytime_object_to_timespec(PyObject *self, PyObject *args)
{
PyObject *obj;
time_t sec;
long nsec;
if (!PyArg_ParseTuple(args, "O:pytime_object_to_timespec", &obj))
return NULL;
if (_PyTime_ObjectToTimespec(obj, &sec, &nsec) == -1)
return NULL;
#if defined(HAVE_LONG_LONG) && SIZEOF_TIME_T == SIZEOF_LONG_LONG
return Py_BuildValue("Ll", (PY_LONG_LONG)sec, nsec);
#else
assert(sizeof(time_t) <= sizeof(long));
return Py_BuildValue("ll", (long)sec, nsec);
#endif
}
static PyMethodDef TestMethods[] = {
{"raise_exception", raise_exception, METH_VARARGS},
@ -2412,6 +2430,7 @@ static PyMethodDef TestMethods[] = {
METH_NOARGS},
{"crash_no_current_thread", (PyCFunction)crash_no_current_thread, METH_NOARGS},
{"run_in_subinterp", run_in_subinterp, METH_VARARGS},
{"pytime_object_to_timespec", test_pytime_object_to_timespec, METH_VARARGS},
{NULL, NULL} /* sentinel */
};

View File

@ -783,16 +783,11 @@ signal_sigtimedwait(PyObject *self, PyObject *args)
siginfo_t si;
int err;
if (!PyArg_ParseTuple(args, "OO:sigtimedwait", &signals, &timeout))
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)))
if (_PyTime_ObjectToTimespec(timeout, &buf.tv_sec, &buf.tv_nsec) == -1)
return NULL;
if (buf.tv_sec < 0 || buf.tv_nsec < 0) {

View File

@ -70,6 +70,51 @@ _PyTime_gettimeofday(_PyTime_timeval *tp)
#endif /* MS_WINDOWS */
}
int
_PyTime_ObjectToTimespec(PyObject *obj, time_t *sec, long *nsec)
{
if (PyFloat_Check(obj)) {
double d, intpart, floatpart, err;
d = PyFloat_AsDouble(obj);
floatpart = modf(d, &intpart);
if (floatpart < 0) {
floatpart = 1.0 + floatpart;
intpart -= 1.0;
}
*sec = (time_t)intpart;
err = intpart - (double)*sec;
if (err <= -1.0 || err >= 1.0)
goto overflow;
floatpart *= 1e9;
*nsec = (long)floatpart;
return 0;
}
else {
#if defined(HAVE_LONG_LONG) && SIZEOF_TIME_T == SIZEOF_LONG_LONG
*sec = PyLong_AsLongLong(obj);
#else
assert(sizeof(time_t) <= sizeof(long));
*sec = PyLong_AsLong(obj);
#endif
if (*sec == -1 && PyErr_Occurred()) {
if (PyErr_ExceptionMatches(PyExc_OverflowError))
goto overflow;
else
return -1;
}
*nsec = 0;
return 0;
}
overflow:
PyErr_SetString(PyExc_OverflowError,
"timestamp out of range for platform time_t");
return -1;
}
void
_PyTime_Init()
{