mirror of
https://github.com/python/cpython.git
synced 2025-07-23 11:15:24 +00:00
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:
parent
1c13f84f55
commit
643cd68ea4
7 changed files with 106 additions and 20 deletions
|
@ -369,12 +369,11 @@ The :mod:`signal` module defines the following functions:
|
||||||
.. versionadded:: 3.3
|
.. versionadded:: 3.3
|
||||||
|
|
||||||
|
|
||||||
.. function:: sigtimedwait(sigset, (timeout_sec, timeout_nsec))
|
.. function:: sigtimedwait(sigset, timeout)
|
||||||
|
|
||||||
Like :func:`sigtimedwait`, but takes a tuple of ``(seconds, nanoseconds)``
|
Like :func:`sigwaitinfo`, but takes an additional *timeout* argument
|
||||||
as an additional argument specifying a timeout. If both *timeout_sec* and
|
specifying a timeout. If *timeout* is specified as :const:`0`, a poll is
|
||||||
*timeout_nsec* are specified as :const:`0`, a poll is performed. Returns
|
performed. Returns :const:`None` if a timeout occurs.
|
||||||
:const:`None` if a timeout occurs.
|
|
||||||
|
|
||||||
Availability: Unix (see the man page :manpage:`sigtimedwait(2)` for further
|
Availability: Unix (see the man page :manpage:`sigtimedwait(2)` for further
|
||||||
information).
|
information).
|
||||||
|
|
|
@ -3,6 +3,7 @@
|
||||||
#define Py_PYTIME_H
|
#define Py_PYTIME_H
|
||||||
|
|
||||||
#include "pyconfig.h" /* include for defines */
|
#include "pyconfig.h" /* include for defines */
|
||||||
|
#include "object.h"
|
||||||
|
|
||||||
/**************************************************************************
|
/**************************************************************************
|
||||||
Symbols and macros to supply platform-independent interfaces to time related
|
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_sec - tv_start.tv_sec) + \
|
||||||
(tv_end.tv_usec - tv_start.tv_usec) * 0.000001)
|
(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. */
|
/* Dummy to force linking. */
|
||||||
PyAPI_FUNC(void) _PyTime_Init(void);
|
PyAPI_FUNC(void) _PyTime_Init(void);
|
||||||
|
|
||||||
|
|
|
@ -662,7 +662,7 @@ class PendingSignalsTests(unittest.TestCase):
|
||||||
self.wait_helper(signal.SIGALRM, '''
|
self.wait_helper(signal.SIGALRM, '''
|
||||||
def test(signum):
|
def test(signum):
|
||||||
signal.alarm(1)
|
signal.alarm(1)
|
||||||
info = signal.sigtimedwait([signum], (10, 1000))
|
info = signal.sigtimedwait([signum], 10.1000)
|
||||||
if info.si_signo != signum:
|
if info.si_signo != signum:
|
||||||
raise Exception('info.si_signo != %s' % signum)
|
raise Exception('info.si_signo != %s' % signum)
|
||||||
''')
|
''')
|
||||||
|
@ -675,7 +675,7 @@ class PendingSignalsTests(unittest.TestCase):
|
||||||
def test(signum):
|
def test(signum):
|
||||||
import os
|
import os
|
||||||
os.kill(os.getpid(), signum)
|
os.kill(os.getpid(), signum)
|
||||||
info = signal.sigtimedwait([signum], (0, 0))
|
info = signal.sigtimedwait([signum], 0)
|
||||||
if info.si_signo != signum:
|
if info.si_signo != signum:
|
||||||
raise Exception('info.si_signo != %s' % signum)
|
raise Exception('info.si_signo != %s' % signum)
|
||||||
''')
|
''')
|
||||||
|
@ -685,7 +685,7 @@ class PendingSignalsTests(unittest.TestCase):
|
||||||
def test_sigtimedwait_timeout(self):
|
def test_sigtimedwait_timeout(self):
|
||||||
self.wait_helper(signal.SIGALRM, '''
|
self.wait_helper(signal.SIGALRM, '''
|
||||||
def test(signum):
|
def test(signum):
|
||||||
received = signal.sigtimedwait([signum], (1, 0))
|
received = signal.sigtimedwait([signum], 1.0)
|
||||||
if received is not None:
|
if received is not None:
|
||||||
raise Exception("received=%r" % (received,))
|
raise Exception("received=%r" % (received,))
|
||||||
''')
|
''')
|
||||||
|
@ -694,9 +694,7 @@ class PendingSignalsTests(unittest.TestCase):
|
||||||
'need signal.sigtimedwait()')
|
'need signal.sigtimedwait()')
|
||||||
def test_sigtimedwait_negative_timeout(self):
|
def test_sigtimedwait_negative_timeout(self):
|
||||||
signum = signal.SIGALRM
|
signum = signal.SIGALRM
|
||||||
self.assertRaises(ValueError, signal.sigtimedwait, [signum], (-1, -1))
|
self.assertRaises(ValueError, signal.sigtimedwait, [signum], -1.0)
|
||||||
self.assertRaises(ValueError, signal.sigtimedwait, [signum], (0, -1))
|
|
||||||
self.assertRaises(ValueError, signal.sigtimedwait, [signum], (-1, 0))
|
|
||||||
|
|
||||||
@unittest.skipUnless(hasattr(signal, 'sigwaitinfo'),
|
@unittest.skipUnless(hasattr(signal, 'sigwaitinfo'),
|
||||||
'need signal.sigwaitinfo()')
|
'need signal.sigwaitinfo()')
|
||||||
|
|
|
@ -497,12 +497,31 @@ class TestStrftime4dyear(_TestStrftimeYear, _Test4dYear):
|
||||||
pass
|
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():
|
def test_main():
|
||||||
support.run_unittest(
|
support.run_unittest(
|
||||||
TimeTestCase,
|
TimeTestCase,
|
||||||
TestLocale,
|
TestLocale,
|
||||||
TestAsctime4dyear,
|
TestAsctime4dyear,
|
||||||
TestStrftime4dyear)
|
TestStrftime4dyear,
|
||||||
|
TestPytime)
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
test_main()
|
test_main()
|
||||||
|
|
|
@ -2323,6 +2323,24 @@ run_in_subinterp(PyObject *self, PyObject *args)
|
||||||
return PyLong_FromLong(r);
|
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[] = {
|
static PyMethodDef TestMethods[] = {
|
||||||
{"raise_exception", raise_exception, METH_VARARGS},
|
{"raise_exception", raise_exception, METH_VARARGS},
|
||||||
|
@ -2412,6 +2430,7 @@ static PyMethodDef TestMethods[] = {
|
||||||
METH_NOARGS},
|
METH_NOARGS},
|
||||||
{"crash_no_current_thread", (PyCFunction)crash_no_current_thread, METH_NOARGS},
|
{"crash_no_current_thread", (PyCFunction)crash_no_current_thread, METH_NOARGS},
|
||||||
{"run_in_subinterp", run_in_subinterp, METH_VARARGS},
|
{"run_in_subinterp", run_in_subinterp, METH_VARARGS},
|
||||||
|
{"pytime_object_to_timespec", test_pytime_object_to_timespec, METH_VARARGS},
|
||||||
{NULL, NULL} /* sentinel */
|
{NULL, NULL} /* sentinel */
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -783,16 +783,11 @@ signal_sigtimedwait(PyObject *self, PyObject *args)
|
||||||
siginfo_t si;
|
siginfo_t si;
|
||||||
int err;
|
int err;
|
||||||
|
|
||||||
if (!PyArg_ParseTuple(args, "OO:sigtimedwait", &signals, &timeout))
|
if (!PyArg_ParseTuple(args, "OO:sigtimedwait",
|
||||||
|
&signals, &timeout))
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
if (!PyTuple_Check(timeout) || PyTuple_Size(timeout) != 2) {
|
if (_PyTime_ObjectToTimespec(timeout, &buf.tv_sec, &buf.tv_nsec) == -1)
|
||||||
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)))
|
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|
||||||
if (buf.tv_sec < 0 || buf.tv_nsec < 0) {
|
if (buf.tv_sec < 0 || buf.tv_nsec < 0) {
|
||||||
|
|
|
@ -70,6 +70,51 @@ _PyTime_gettimeofday(_PyTime_timeval *tp)
|
||||||
#endif /* MS_WINDOWS */
|
#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
|
void
|
||||||
_PyTime_Init()
|
_PyTime_Init()
|
||||||
{
|
{
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue