]> granicus.if.org Git - python/commitdiff
Issue #22117: The thread module uses the new _PyTime_t timestamp API
authorVictor Stinner <victor.stinner@gmail.com>
Sat, 28 Mar 2015 02:52:05 +0000 (03:52 +0100)
committerVictor Stinner <victor.stinner@gmail.com>
Sat, 28 Mar 2015 02:52:05 +0000 (03:52 +0100)
Add also a new _PyTime_AsMicroseconds() function.

threading.TIMEOUT_MAX is now be smaller: only 292 years instead of 292,271
years on 64-bit system for example. Sorry, your threads will hang a *little
bit* shorter. Call me if you want to ensure that your locks wait longer, I can
share some tricks with you.

Include/pytime.h
Modules/_threadmodule.c
Python/pytime.c

index 30042891e290ad32531127db51640ee986c5ae86..3078d252cdfcaa720144b7f287bb62d720b29983 100644 (file)
@@ -74,24 +74,6 @@ PyAPI_FUNC(int) _PyTime_ObjectToTimespec(
     long *nsec,
     _PyTime_round_t);
 
-/* Get the time of a monotonic clock, i.e. a clock that cannot go backwards.
-   The clock is not affected by system clock updates. The reference point of
-   the returned value is undefined, so that only the difference between the
-   results of consecutive calls is valid.
-
-   The function never fails. _PyTime_Init() ensures that a monotonic clock
-   is available and works. */
-PyAPI_FUNC(void) _PyTime_monotonic(
-    _PyTime_timeval *tp);
-
-/* Similar to _PyTime_monotonic(), fill also info (if set) with information of
-   the function used to get the time.
-
-   Return 0 on success, raise an exception and return -1 on error. */
-PyAPI_FUNC(int) _PyTime_monotonic_info(
-    _PyTime_timeval *tp,
-    _Py_clock_info_t *info);
-
 /* Add interval seconds to tv */
 PyAPI_FUNC(void)
 _PyTime_AddDouble(_PyTime_timeval *tv, double interval,
@@ -105,6 +87,8 @@ PyAPI_FUNC(int) _PyTime_Init(void);
 
 #ifdef PY_INT64_T
 typedef PY_INT64_T _PyTime_t;
+#define _PyTime_MIN PY_LLONG_MIN
+#define _PyTime_MAX PY_LLONG_MAX
 #else
 #  error "_PyTime_t need signed 64-bit integer type"
 #endif
@@ -125,6 +109,10 @@ PyAPI_FUNC(double) _PyTime_AsSecondsDouble(_PyTime_t t);
 PyAPI_FUNC(_PyTime_t) _PyTime_AsMilliseconds(_PyTime_t t,
     _PyTime_round_t round);
 
+/* Convert timestamp to a number of microseconds (10^-6 seconds). */
+PyAPI_FUNC(_PyTime_t) _PyTime_AsMicroseconds(_PyTime_t t,
+    _PyTime_round_t round);
+
 /* Convert timestamp to a number of nanoseconds (10^-9 seconds) as a Python int
    object. */
 PyAPI_FUNC(PyObject *) _PyTime_AsNanosecondsObject(_PyTime_t t);
index 8f59e036b9c8de030c5ae0da50346b4d10a0142a..07b01f09c4d88ecfcb91952bf5c5f9676519d422 100644 (file)
@@ -49,21 +49,18 @@ lock_dealloc(lockobject *self)
  * timeout.
  */
 static PyLockStatus
-acquire_timed(PyThread_type_lock lock, PY_TIMEOUT_T microseconds)
+acquire_timed(PyThread_type_lock lock, _PyTime_t timeout)
 {
     PyLockStatus r;
-    _PyTime_timeval curtime;
-    _PyTime_timeval endtime;
-
-
-    if (microseconds > 0) {
-        _PyTime_monotonic(&endtime);
-        endtime.tv_sec += microseconds / (1000 * 1000);
-        endtime.tv_usec += microseconds % (1000 * 1000);
-    }
+    _PyTime_t endtime = 0;
+    _PyTime_t microseconds;
 
+    if (timeout > 0)
+        endtime = _PyTime_GetMonotonicClock() + timeout;
 
     do {
+        microseconds = _PyTime_AsMicroseconds(timeout, _PyTime_ROUND_UP);
+
         /* first a simple non-blocking try without releasing the GIL */
         r = PyThread_acquire_lock_timed(lock, 0, 0);
         if (r == PY_LOCK_FAILURE && microseconds != 0) {
@@ -82,14 +79,12 @@ acquire_timed(PyThread_type_lock lock, PY_TIMEOUT_T microseconds)
 
             /* If we're using a timeout, recompute the timeout after processing
              * signals, since those can take time.  */
-            if (microseconds > 0) {
-                _PyTime_monotonic(&curtime);
-                microseconds = ((endtime.tv_sec - curtime.tv_sec) * 1000000 +
-                                (endtime.tv_usec - curtime.tv_usec));
+            if (timeout > 0) {
+                timeout = endtime - _PyTime_GetMonotonicClock();
 
                 /* Check for negative values, since those mean block forever.
                  */
-                if (microseconds <= 0) {
+                if (timeout <= 0) {
                     r = PY_LOCK_FAILURE;
                 }
             }
@@ -99,44 +94,60 @@ acquire_timed(PyThread_type_lock lock, PY_TIMEOUT_T microseconds)
     return r;
 }
 
-static PyObject *
-lock_PyThread_acquire_lock(lockobject *self, PyObject *args, PyObject *kwds)
+static int
+lock_acquire_parse_args(PyObject *args, PyObject *kwds,
+                        _PyTime_t *timeout)
 {
     char *kwlist[] = {"blocking", "timeout", NULL};
     int blocking = 1;
-    double timeout = -1;
-    PY_TIMEOUT_T microseconds;
-    PyLockStatus r;
+    PyObject *timeout_obj = NULL;
+    const _PyTime_t unset_timeout = _PyTime_FromNanoseconds(-1000000000);
 
-    if (!PyArg_ParseTupleAndKeywords(args, kwds, "|id:acquire", kwlist,
-                                     &blocking, &timeout))
-        return NULL;
+    *timeout = unset_timeout ;
 
-    if (!blocking && timeout != -1) {
-        PyErr_SetString(PyExc_ValueError, "can't specify a timeout "
-                        "for a non-blocking call");
-        return NULL;
+    if (!PyArg_ParseTupleAndKeywords(args, kwds, "|iO:acquire", kwlist,
+                                     &blocking, &timeout_obj))
+        return -1;
+
+    if (timeout_obj
+        && _PyTime_FromSecondsObject(timeout, timeout_obj, _PyTime_ROUND_UP) < 0)
+        return -1;
+
+    if (!blocking && *timeout != unset_timeout ) {
+        PyErr_SetString(PyExc_ValueError,
+                        "can't specify a timeout for a non-blocking call");
+        return -1;
     }
-    if (timeout < 0 && timeout != -1) {
-        PyErr_SetString(PyExc_ValueError, "timeout value must be "
-                        "strictly positive");
-        return NULL;
+    if (*timeout < 0 && *timeout != unset_timeout) {
+        PyErr_SetString(PyExc_ValueError,
+                        "timeout value must be positive");
+        return -1;
     }
     if (!blocking)
-        microseconds = 0;
-    else if (timeout == -1)
-        microseconds = -1;
-    else {
-        timeout *= 1e6;
-        if (timeout >= (double) PY_TIMEOUT_MAX) {
+        *timeout = 0;
+    else if (*timeout != unset_timeout) {
+        _PyTime_t microseconds;
+
+        microseconds = _PyTime_AsMicroseconds(*timeout, _PyTime_ROUND_UP);
+        if (microseconds >= PY_TIMEOUT_MAX) {
             PyErr_SetString(PyExc_OverflowError,
                             "timeout value is too large");
-            return NULL;
+            return -1;
         }
-        microseconds = (PY_TIMEOUT_T) timeout;
     }
+    return 0;
+}
+
+static PyObject *
+lock_PyThread_acquire_lock(lockobject *self, PyObject *args, PyObject *kwds)
+{
+    _PyTime_t timeout;
+    PyLockStatus r;
 
-    r = acquire_timed(self->lock_lock, microseconds);
+    if (lock_acquire_parse_args(args, kwds, &timeout) < 0)
+        return NULL;
+
+    r = acquire_timed(self->lock_lock, timeout);
     if (r == PY_LOCK_INTR) {
         return NULL;
     }
@@ -281,41 +292,13 @@ rlock_dealloc(rlockobject *self)
 static PyObject *
 rlock_acquire(rlockobject *self, PyObject *args, PyObject *kwds)
 {
-    char *kwlist[] = {"blocking", "timeout", NULL};
-    int blocking = 1;
-    double timeout = -1;
-    PY_TIMEOUT_T microseconds;
+    _PyTime_t timeout;
     long tid;
     PyLockStatus r = PY_LOCK_ACQUIRED;
 
-    if (!PyArg_ParseTupleAndKeywords(args, kwds, "|id:acquire", kwlist,
-                                     &blocking, &timeout))
+    if (lock_acquire_parse_args(args, kwds, &timeout) < 0)
         return NULL;
 
-    if (!blocking && timeout != -1) {
-        PyErr_SetString(PyExc_ValueError, "can't specify a timeout "
-                        "for a non-blocking call");
-        return NULL;
-    }
-    if (timeout < 0 && timeout != -1) {
-        PyErr_SetString(PyExc_ValueError, "timeout value must be "
-                        "strictly positive");
-        return NULL;
-    }
-    if (!blocking)
-        microseconds = 0;
-    else if (timeout == -1)
-        microseconds = -1;
-    else {
-        timeout *= 1e6;
-        if (timeout >= (double) PY_TIMEOUT_MAX) {
-            PyErr_SetString(PyExc_OverflowError,
-                            "timeout value is too large");
-            return NULL;
-        }
-        microseconds = (PY_TIMEOUT_T) timeout;
-    }
-
     tid = PyThread_get_thread_ident();
     if (self->rlock_count > 0 && tid == self->rlock_owner) {
         unsigned long count = self->rlock_count + 1;
@@ -327,7 +310,7 @@ rlock_acquire(rlockobject *self, PyObject *args, PyObject *kwds)
         self->rlock_count = count;
         Py_RETURN_TRUE;
     }
-    r = acquire_timed(self->rlock_lock, microseconds);
+    r = acquire_timed(self->rlock_lock, timeout);
     if (r == PY_LOCK_ACQUIRED) {
         assert(self->rlock_count == 0);
         self->rlock_owner = tid;
@@ -1362,7 +1345,9 @@ static struct PyModuleDef threadmodule = {
 PyMODINIT_FUNC
 PyInit__thread(void)
 {
-    PyObject *m, *d, *timeout_max;
+    PyObject *m, *d, *v;
+    double time_max;
+    double timeout_max;
 
     /* Initialize types: */
     if (PyType_Ready(&localdummytype) < 0)
@@ -1379,10 +1364,14 @@ PyInit__thread(void)
     if (m == NULL)
         return NULL;
 
-    timeout_max = PyFloat_FromDouble(PY_TIMEOUT_MAX / 1000000);
-    if (!timeout_max)
+    timeout_max = PY_TIMEOUT_MAX / 1000000;
+    time_max = floor(_PyTime_AsSecondsDouble(_PyTime_MAX));
+    timeout_max = Py_MIN(timeout_max, time_max);
+
+    v = PyFloat_FromDouble(timeout_max);
+    if (!v)
         return NULL;
-    if (PyModule_AddObject(m, "TIMEOUT_MAX", timeout_max) < 0)
+    if (PyModule_AddObject(m, "TIMEOUT_MAX", v) < 0)
         return NULL;
 
     /* Add a symbolic constant */
index 9893116465d1ede309597f6f84c36f6a8e0d700a..aa64977552b029ea1c81d214d2471238cccc38f7 100644 (file)
@@ -119,128 +119,6 @@ _PyTime_gettimeofday(_PyTime_timeval *tp)
     }
 }
 
-static int
-pymonotonic(_PyTime_timeval *tp, _Py_clock_info_t *info, int raise)
-{
-#ifdef Py_DEBUG
-    static _PyTime_timeval last = {0, -1};
-#endif
-#if defined(MS_WINDOWS)
-    ULONGLONG result;
-
-    assert(info == NULL || raise);
-
-    result = GetTickCount64();
-
-    tp->tv_sec = result / SEC_TO_MS;
-    tp->tv_usec = (result % SEC_TO_MS) * MS_TO_US;
-
-    if (info) {
-        DWORD timeAdjustment, timeIncrement;
-        BOOL isTimeAdjustmentDisabled, ok;
-        info->implementation = "GetTickCount64()";
-        info->monotonic = 1;
-        ok = GetSystemTimeAdjustment(&timeAdjustment, &timeIncrement,
-                                     &isTimeAdjustmentDisabled);
-        if (!ok) {
-            PyErr_SetFromWindowsErr(0);
-            return -1;
-        }
-        info->resolution = timeIncrement * 1e-7;
-        info->adjustable = 0;
-    }
-
-#elif defined(__APPLE__)
-    static mach_timebase_info_data_t timebase;
-    uint64_t time;
-
-    if (timebase.denom == 0) {
-        /* According to the Technical Q&A QA1398, mach_timebase_info() cannot
-           fail: https://developer.apple.com/library/mac/#qa/qa1398/ */
-        (void)mach_timebase_info(&timebase);
-    }
-
-    time = mach_absolute_time();
-
-    /* nanoseconds => microseconds */
-    time /= US_TO_NS;
-    /* apply timebase factor */
-    time *= timebase.numer;
-    time /= timebase.denom;
-    tp->tv_sec = time / SEC_TO_US;
-    tp->tv_usec = time % SEC_TO_US;
-
-    if (info) {
-        info->implementation = "mach_absolute_time()";
-        info->resolution = (double)timebase.numer / timebase.denom * 1e-9;
-        info->monotonic = 1;
-        info->adjustable = 0;
-    }
-
-#else
-    struct timespec ts;
-#ifdef CLOCK_HIGHRES
-    const clockid_t clk_id = CLOCK_HIGHRES;
-    const char *implementation = "clock_gettime(CLOCK_HIGHRES)";
-#else
-    const clockid_t clk_id = CLOCK_MONOTONIC;
-    const char *implementation = "clock_gettime(CLOCK_MONOTONIC)";
-#endif
-
-    assert(info == NULL || raise);
-
-    if (clock_gettime(clk_id, &ts) != 0) {
-        if (raise) {
-            PyErr_SetFromErrno(PyExc_OSError);
-            return -1;
-        }
-        tp->tv_sec = 0;
-        tp->tv_usec = 0;
-        return -1;
-    }
-
-    if (info) {
-        struct timespec res;
-        info->monotonic = 1;
-        info->implementation = implementation;
-        info->adjustable = 0;
-        if (clock_getres(clk_id, &res) != 0) {
-            PyErr_SetFromErrno(PyExc_OSError);
-            return -1;
-        }
-        info->resolution = res.tv_sec + res.tv_nsec * 1e-9;
-    }
-    tp->tv_sec = ts.tv_sec;
-    tp->tv_usec = ts.tv_nsec / US_TO_NS;
-#endif
-    assert(0 <= tp->tv_usec && tp->tv_usec < SEC_TO_US);
-#ifdef Py_DEBUG
-    /* monotonic clock cannot go backward */
-    assert(last.tv_usec == -1
-           || tp->tv_sec > last.tv_sec
-           || (tp->tv_sec == last.tv_sec && tp->tv_usec >= last.tv_usec));
-    last = *tp;
-#endif
-    return 0;
-}
-
-void
-_PyTime_monotonic(_PyTime_timeval *tp)
-{
-    if (pymonotonic(tp, NULL, 0) < 0) {
-        /* cannot happen, _PyTime_Init() checks that pymonotonic() works */
-        assert(0);
-        tp->tv_sec = 0;
-        tp->tv_usec = 0;
-    }
-}
-
-int
-_PyTime_monotonic_info(_PyTime_timeval *tp, _Py_clock_info_t *info)
-{
-    return pymonotonic(tp, info, 1);
-}
-
 static void
 error_time_t_overflow(void)
 {
@@ -536,6 +414,12 @@ _PyTime_AsMilliseconds(_PyTime_t t, _PyTime_round_t round)
     return _PyTime_Multiply(t, 1000, round);
 }
 
+_PyTime_t
+_PyTime_AsMicroseconds(_PyTime_t t, _PyTime_round_t round)
+{
+    return _PyTime_Multiply(t, 1000 * 1000, round);
+}
+
 int
 _PyTime_AsTimeval(_PyTime_t t, struct timeval *tv, _PyTime_round_t round)
 {
@@ -842,10 +726,6 @@ _PyTime_Init(void)
     if (_PyTime_GetSystemClockWithInfo(&t, NULL) < 0)
         return -1;
 
-    /* ensure that the operating system provides a monotonic clock */
-    if (_PyTime_monotonic_info(&tv, NULL) < 0)
-        return -1;
-
     /* ensure that the operating system provides a monotonic clock */
     if (_PyTime_GetMonotonicClockWithInfo(&t, NULL) < 0)
         return -1;