]> granicus.if.org Git - python/commitdiff
Issue #22117: The signal modules uses the new _PyTime_t API
authorVictor Stinner <victor.stinner@gmail.com>
Fri, 27 Mar 2015 17:19:03 +0000 (18:19 +0100)
committerVictor Stinner <victor.stinner@gmail.com>
Fri, 27 Mar 2015 17:19:03 +0000 (18:19 +0100)
* Add _PyTime_AsTimespec()
* Add unit tests for _PyTime_AsTimespec()

Include/pytime.h
Lib/test/test_time.py
Modules/_testcapimodule.c
Modules/signalmodule.c
Python/pytime.c

index 1648d039116d1be8efc822b400a68f17584e579e..17d5ea17c4ff3decaaa8e29d2bc7f51aaa2ab1bd 100644 (file)
@@ -145,6 +145,12 @@ PyAPI_FUNC(int) _PyTime_AsTimeval(_PyTime_t t,
     struct timeval *tv,
     _PyTime_round_t round);
 
+#ifdef HAVE_CLOCK_GETTIME
+/* Convert a timestamp to a timespec structure (nanosecond resolution).
+   Raise an exception and return -1 on error, return 0 on success. */
+PyAPI_FUNC(int) _PyTime_AsTimespec(_PyTime_t t, struct timespec *ts);
+#endif
+
 /* Get the current time from the system clock.
  * Fill clock information if info is not NULL.
  * Raise an exception and return -1 on error, return 0 on success.
index 817da8a01d42b005e77fd21a215a754d7d744825..cfec329f014de709847ace8e6322762aa68b95b7 100644 (file)
@@ -10,6 +10,11 @@ try:
     import threading
 except ImportError:
     threading = None
+try:
+    import _testcapi
+except ImportError:
+    _testcapi = None
+
 
 # Max year is only limited by the size of C int.
 SIZEOF_INT = sysconfig.get_config_var('SIZEOF_INT') or 4
@@ -768,7 +773,8 @@ class TestPytime(unittest.TestCase):
         self.assertIs(lt.tm_zone, None)
 
 
-@support.cpython_only
+@unittest.skipUnless(_testcapi is not None,
+                     'need the _testcapi module')
 class TestPyTime_t(unittest.TestCase):
     def test_FromSecondsObject(self):
         from _testcapi import PyTime_FromSecondsObject
@@ -896,6 +902,27 @@ class TestPyTime_t(unittest.TestCase):
                 self.assertEqual(PyTime_AsSecondsDouble(nanoseconds),
                                  seconds)
 
+    @unittest.skipUnless(hasattr(_testcapi, 'PyTime_AsTimespec'),
+                         'need _testcapi.PyTime_AsTimespec')
+    def test_timespec(self):
+        from _testcapi import PyTime_AsTimespec
+        for ns, ts in (
+            # nanoseconds
+            (0, (0, 0)),
+            (1, (0, 1)),
+            (-1, (-1, 999999999)),
+
+            # seconds
+            (2 * SEC_TO_NS, (2, 0)),
+            (-3 * SEC_TO_NS, (-3, 0)),
+
+            # seconds + nanoseconds
+            (1234567890, (1, 234567890)),
+            (-1234567890, (-2, 765432110)),
+        ):
+            with self.subTest(nanoseconds=ns, timespec=ts):
+                self.assertEqual(PyTime_AsTimespec(ns), ts)
+
 
 if __name__ == "__main__":
     unittest.main()
index b3820811583e493f63fd7e302fede71a849ad36d..5029105a882d369545e03661dd40a77ff1647b65 100644 (file)
@@ -3408,6 +3408,23 @@ test_pytime_assecondsdouble(PyObject *self, PyObject *args)
     return PyFloat_FromDouble(d);
 }
 
+#ifdef HAVE_CLOCK_GETTIME
+static PyObject *
+test_PyTime_AsTimespec(PyObject *self, PyObject *args)
+{
+    PY_LONG_LONG ns;
+    _PyTime_t t;
+    struct timespec ts;
+
+    if (!PyArg_ParseTuple(args, "L", &ns))
+        return NULL;
+    t = _PyTime_FromNanoseconds(ns);
+    if (_PyTime_AsTimespec(t, &ts) == -1)
+        return NULL;
+    return Py_BuildValue("Nl", _PyLong_FromTime_t(ts.tv_sec), ts.tv_nsec);
+}
+#endif
+
 
 static PyMethodDef TestMethods[] = {
     {"raise_exception",         raise_exception,                 METH_VARARGS},
@@ -3573,6 +3590,9 @@ static PyMethodDef TestMethods[] = {
         return_result_with_error, METH_NOARGS},
     {"PyTime_FromSecondsObject", test_pytime_fromsecondsobject,  METH_VARARGS},
     {"PyTime_AsSecondsDouble", test_pytime_assecondsdouble, METH_VARARGS},
+#ifdef HAVE_CLOCK_GETTIME
+    {"PyTime_AsTimespec", test_PyTime_AsTimespec, METH_VARARGS},
+#endif
     {NULL, NULL} /* sentinel */
 };
 
index 5dba8b1fd32a07de74cc8af6833ebeb1a2a96d84..f3b2e2986788964a611dab927a266d9e553ef8ee 100644 (file)
@@ -966,16 +966,18 @@ Returns a struct_siginfo containing information about the signal.");
 static PyObject *
 signal_sigtimedwait(PyObject *self, PyObject *args)
 {
-    PyObject *signals;
-    double timeout, frac;
+    PyObject *signals, *timeout_obj;
     struct timespec ts;
     sigset_t set;
     siginfo_t si;
     int res;
-    _PyTime_timeval deadline, monotonic;
+    _PyTime_t timeout, deadline, monotonic;
+
+    if (!PyArg_ParseTuple(args, "OO:sigtimedwait",
+                          &signals, &timeout_obj))
+        return NULL;
 
-    if (!PyArg_ParseTuple(args, "Od:sigtimedwait",
-                          &signals, &timeout))
+    if (_PyTime_FromSecondsObject(&timeout, timeout_obj, _PyTime_ROUND_UP) < 0)
         return NULL;
 
     if (timeout < 0) {
@@ -986,14 +988,11 @@ signal_sigtimedwait(PyObject *self, PyObject *args)
     if (iterable_to_sigset(signals, &set))
         return NULL;
 
-    _PyTime_monotonic(&deadline);
-    _PyTime_AddDouble(&deadline, timeout, _PyTime_ROUND_UP);
+    deadline = _PyTime_GetMonotonicClock() + timeout;
 
     do {
-        frac = fmod(timeout, 1.0);
-        timeout = floor(timeout);
-        ts.tv_sec = (long)timeout;
-        ts.tv_nsec = (long)(frac*1e9);
+        if (_PyTime_AsTimespec(timeout, &ts) < 0)
+            return NULL;
 
         Py_BEGIN_ALLOW_THREADS
         res = sigtimedwait(&set, &si, &ts);
@@ -1013,9 +1012,9 @@ signal_sigtimedwait(PyObject *self, PyObject *args)
         if (PyErr_CheckSignals())
             return NULL;
 
-        _PyTime_monotonic(&monotonic);
-        timeout = _PyTime_INTERVAL(monotonic, deadline);
-        if (timeout <= 0.0)
+        monotonic = _PyTime_GetMonotonicClock();
+        timeout = deadline - monotonic;
+        if (timeout <= 0)
             break;
     } while (1);
 
index 0f30f428d8e16499a9ed35cb4950eea43dec75b0..bd94787241d7a178db9a7afce94d985f226bcf33 100644 (file)
@@ -572,6 +572,27 @@ _PyTime_AsTimeval(_PyTime_t t, struct timeval *tv, _PyTime_round_t round)
     return 0;
 }
 
+#ifdef HAVE_CLOCK_GETTIME
+int
+_PyTime_AsTimespec(_PyTime_t t, struct timespec *ts)
+{
+    _PyTime_t sec, nsec;
+    sec = t / SEC_TO_NS;
+    nsec = t % SEC_TO_NS;
+    if (nsec < 0) {
+        nsec += SEC_TO_NS;
+        sec -= 1;
+    }
+    ts->tv_sec = (time_t)sec;
+    if ((_PyTime_t)ts->tv_sec != sec) {
+        _PyTime_overflow();
+        return -1;
+    }
+    ts->tv_nsec = nsec;
+    return 0;
+}
+#endif
+
 static int
 pygettimeofday_new(_PyTime_t *tp, _Py_clock_info_t *info, int raise)
 {