[Python-checkins] cpython: Issue #22043: time.monotonic() is now always available

victor.stinner python-checkins at python.org
Tue Sep 2 23:26:17 CEST 2014


http://hg.python.org/cpython/rev/b12857782041
changeset:   92306:b12857782041
user:        Victor Stinner <victor.stinner at gmail.com>
date:        Tue Sep 02 23:18:25 2014 +0200
summary:
  Issue #22043: time.monotonic() is now always available

threading.Lock.acquire(), threading.RLock.acquire() and socket operations now
use a monotonic clock, instead of the system clock, when a timeout is used.

files:
  Doc/library/time.rst       |    4 +-
  Doc/whatsnew/3.5.rst       |    5 +
  Include/pytime.h           |   18 ++
  Lib/queue.py               |    5 +-
  Lib/sched.py               |    5 +-
  Lib/socketserver.py        |    5 +-
  Lib/subprocess.py          |    5 +-
  Lib/telnetlib.py           |    5 +-
  Lib/test/test_selectors.py |    5 +-
  Lib/threading.py           |    5 +-
  Lib/trace.py               |    5 +-
  Misc/NEWS                  |    5 +
  Modules/_threadmodule.c    |    4 +-
  Modules/gcmodule.c         |    6 +-
  Modules/socketmodule.c     |    4 +-
  Modules/timemodule.c       |  141 +-------------------
  Python/pytime.c            |  175 +++++++++++++++++++++++++
  17 files changed, 226 insertions(+), 176 deletions(-)


diff --git a/Doc/library/time.rst b/Doc/library/time.rst
--- a/Doc/library/time.rst
+++ b/Doc/library/time.rst
@@ -315,9 +315,9 @@
    processes running for more than 49 days. On more recent versions of Windows
    and on other operating systems, :func:`monotonic` is system-wide.
 
-   Availability: Windows, Mac OS X, Linux, FreeBSD, OpenBSD, Solaris.
-
    .. versionadded:: 3.3
+   .. versionchanged:: 3.5
+      The function is now always available.
 
 
 .. function:: perf_counter()
diff --git a/Doc/whatsnew/3.5.rst b/Doc/whatsnew/3.5.rst
--- a/Doc/whatsnew/3.5.rst
+++ b/Doc/whatsnew/3.5.rst
@@ -238,6 +238,11 @@
   :meth:`socket.socket.send`.
   (contributed by Giampaolo Rodola' in :issue:`17552`)
 
+time
+----
+
+The :func:`time.monotonic` function is now always available (:issue`22043`).
+
 wsgiref
 -------
 
diff --git a/Include/pytime.h b/Include/pytime.h
--- a/Include/pytime.h
+++ b/Include/pytime.h
@@ -91,6 +91,24 @@
     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);
+
 /* Initialize time.
    Return 0 on success, raise an exception and return -1 on error. */
 PyAPI_FUNC(int) _PyTime_Init(void);
diff --git a/Lib/queue.py b/Lib/queue.py
--- a/Lib/queue.py
+++ b/Lib/queue.py
@@ -6,10 +6,7 @@
     import dummy_threading as threading
 from collections import deque
 from heapq import heappush, heappop
-try:
-    from time import monotonic as time
-except ImportError:
-    from time import time
+from time import monotonic as time
 
 __all__ = ['Empty', 'Full', 'Queue', 'PriorityQueue', 'LifoQueue']
 
diff --git a/Lib/sched.py b/Lib/sched.py
--- a/Lib/sched.py
+++ b/Lib/sched.py
@@ -35,10 +35,7 @@
     import threading
 except ImportError:
     import dummy_threading as threading
-try:
-    from time import monotonic as _time
-except ImportError:
-    from time import time as _time
+from time import monotonic as _time
 
 __all__ = ["scheduler"]
 
diff --git a/Lib/socketserver.py b/Lib/socketserver.py
--- a/Lib/socketserver.py
+++ b/Lib/socketserver.py
@@ -136,10 +136,7 @@
     import threading
 except ImportError:
     import dummy_threading as threading
-try:
-    from time import monotonic as time
-except ImportError:
-    from time import time as time
+from time import monotonic as time
 
 __all__ = ["TCPServer","UDPServer","ForkingUDPServer","ForkingTCPServer",
            "ThreadingUDPServer","ThreadingTCPServer","BaseRequestHandler",
diff --git a/Lib/subprocess.py b/Lib/subprocess.py
--- a/Lib/subprocess.py
+++ b/Lib/subprocess.py
@@ -365,10 +365,7 @@
 import builtins
 import warnings
 import errno
-try:
-    from time import monotonic as _time
-except ImportError:
-    from time import time as _time
+from time import monotonic as _time
 
 # Exception classes used by this module.
 class SubprocessError(Exception): pass
diff --git a/Lib/telnetlib.py b/Lib/telnetlib.py
--- a/Lib/telnetlib.py
+++ b/Lib/telnetlib.py
@@ -36,10 +36,7 @@
 import sys
 import socket
 import selectors
-try:
-    from time import monotonic as _time
-except ImportError:
-    from time import time as _time
+from time import monotonic as _time
 
 __all__ = ["Telnet"]
 
diff --git a/Lib/test/test_selectors.py b/Lib/test/test_selectors.py
--- a/Lib/test/test_selectors.py
+++ b/Lib/test/test_selectors.py
@@ -8,10 +8,7 @@
 from time import sleep
 import unittest
 import unittest.mock
-try:
-    from time import monotonic as time
-except ImportError:
-    from time import time as time
+from time import monotonic as time
 try:
     import resource
 except ImportError:
diff --git a/Lib/threading.py b/Lib/threading.py
--- a/Lib/threading.py
+++ b/Lib/threading.py
@@ -3,10 +3,7 @@
 import sys as _sys
 import _thread
 
-try:
-    from time import monotonic as _time
-except ImportError:
-    from time import time as _time
+from time import monotonic as _time
 from traceback import format_exc as _format_exc
 from _weakrefset import WeakSet
 from itertools import islice as _islice
diff --git a/Lib/trace.py b/Lib/trace.py
--- a/Lib/trace.py
+++ b/Lib/trace.py
@@ -59,10 +59,7 @@
 import dis
 import pickle
 from warnings import warn as _warn
-try:
-    from time import monotonic as _time
-except ImportError:
-    from time import time as _time
+from time import monotonic as _time
 
 try:
     import threading
diff --git a/Misc/NEWS b/Misc/NEWS
--- a/Misc/NEWS
+++ b/Misc/NEWS
@@ -129,6 +129,11 @@
 Library
 -------
 
+- Issue #22043: time.monotonic() is now always available.
+  ``threading.Lock.acquire()``, ``threading.RLock.acquire()`` and socket
+  operations now use a monotonic clock, instead of the system clock, when a
+  timeout is used.
+
 - Issue #21527: Add a default number of workers to ThreadPoolExecutor equal
   to 5 times the number of CPUs.  Patch by Claudiu Popa.
 
diff --git a/Modules/_threadmodule.c b/Modules/_threadmodule.c
--- a/Modules/_threadmodule.c
+++ b/Modules/_threadmodule.c
@@ -57,7 +57,7 @@
 
 
     if (microseconds > 0) {
-        _PyTime_gettimeofday(&endtime);
+        _PyTime_monotonic(&endtime);
         endtime.tv_sec += microseconds / (1000 * 1000);
         endtime.tv_usec += microseconds % (1000 * 1000);
     }
@@ -83,7 +83,7 @@
             /* If we're using a timeout, recompute the timeout after processing
              * signals, since those can take time.  */
             if (microseconds > 0) {
-                _PyTime_gettimeofday(&curtime);
+                _PyTime_monotonic(&curtime);
                 microseconds = ((endtime.tv_sec - curtime.tv_sec) * 1000000 +
                                 (endtime.tv_usec - curtime.tv_usec));
 
diff --git a/Modules/gcmodule.c b/Modules/gcmodule.c
--- a/Modules/gcmodule.c
+++ b/Modules/gcmodule.c
@@ -25,7 +25,7 @@
 
 #include "Python.h"
 #include "frameobject.h"        /* for PyFrame_ClearFreeList */
-#include "pytime.h"           /* for _PyTime_gettimeofday, _PyTime_INTERVAL */
+#include "pytime.h"             /* for _PyTime_monotonic, _PyTime_INTERVAL */
 
 /* Get an object's GC head */
 #define AS_GC(o) ((PyGC_Head *)(o)-1)
@@ -919,7 +919,7 @@
         for (i = 0; i < NUM_GENERATIONS; i++)
             PySys_FormatStderr(" %zd",
                               gc_list_size(GEN_HEAD(i)));
-        _PyTime_gettimeofday(&t1);
+        _PyTime_monotonic(&t1);
 
         PySys_WriteStderr("\n");
     }
@@ -1025,7 +1025,7 @@
     }
     if (debug & DEBUG_STATS) {
         _PyTime_timeval t2;
-        _PyTime_gettimeofday(&t2);
+        _PyTime_monotonic(&t2);
 
         if (m == 0 && n == 0)
             PySys_WriteStderr("gc: done");
diff --git a/Modules/socketmodule.c b/Modules/socketmodule.c
--- a/Modules/socketmodule.c
+++ b/Modules/socketmodule.c
@@ -680,7 +680,7 @@
         double interval = s->sock_timeout; \
         int has_timeout = s->sock_timeout > 0.0; \
         if (has_timeout) { \
-            _PyTime_gettimeofday(&now); \
+            _PyTime_monotonic(&now); \
             deadline = now; \
             _PyTime_ADD_SECONDS(deadline, s->sock_timeout); \
         } \
@@ -691,7 +691,7 @@
             if (!has_timeout || \
                 (!CHECK_ERRNO(EWOULDBLOCK) && !CHECK_ERRNO(EAGAIN))) \
                 break; \
-            _PyTime_gettimeofday(&now); \
+            _PyTime_monotonic(&now); \
             interval = _PyTime_INTERVAL(now, deadline); \
         } \
     } \
diff --git a/Modules/timemodule.c b/Modules/timemodule.c
--- a/Modules/timemodule.c
+++ b/Modules/timemodule.c
@@ -37,10 +37,6 @@
 #endif /* MS_WINDOWS */
 #endif /* !__WATCOMC__ || __QNX__ */
 
-#if defined(__APPLE__)
-#include <mach/mach_time.h>
-#endif
-
 /* Forward declarations */
 static int floatsleep(double);
 static PyObject* floattime(_Py_clock_info_t *info);
@@ -899,122 +895,15 @@
 should not be relied on.");
 #endif /* HAVE_WORKING_TZSET */
 
-#if defined(MS_WINDOWS) || defined(__APPLE__) \
-    || (defined(HAVE_CLOCK_GETTIME) \
-        && (defined(CLOCK_HIGHRES) || defined(CLOCK_MONOTONIC)))
-#define PYMONOTONIC
-#endif
-
-#ifdef PYMONOTONIC
-static PyObject*
+static PyObject *
 pymonotonic(_Py_clock_info_t *info)
 {
-#if defined(MS_WINDOWS)
-    static ULONGLONG (*GetTickCount64) (void) = NULL;
-    static ULONGLONG (CALLBACK *Py_GetTickCount64)(void);
-    static int has_getickcount64 = -1;
-    double result;
-
-    if (has_getickcount64 == -1) {
-        /* GetTickCount64() was added to Windows Vista */
-        if (winver.dwMajorVersion >= 6) {
-            HINSTANCE hKernel32;
-            hKernel32 = GetModuleHandleW(L"KERNEL32");
-            *(FARPROC*)&Py_GetTickCount64 = GetProcAddress(hKernel32,
-                                                           "GetTickCount64");
-            has_getickcount64 = (Py_GetTickCount64 != NULL);
-        }
-        else
-            has_getickcount64 = 0;
-    }
-
-    if (has_getickcount64) {
-        ULONGLONG ticks;
-        ticks = Py_GetTickCount64();
-        result = (double)ticks * 1e-3;
-    }
-    else {
-        static DWORD last_ticks = 0;
-        static DWORD n_overflow = 0;
-        DWORD ticks;
-
-        ticks = GetTickCount();
-        if (ticks < last_ticks)
-            n_overflow++;
-        last_ticks = ticks;
-
-        result = ldexp(n_overflow, 32);
-        result += ticks;
-        result *= 1e-3;
-    }
-
-    if (info) {
-        DWORD timeAdjustment, timeIncrement;
-        BOOL isTimeAdjustmentDisabled, ok;
-        if (has_getickcount64)
-            info->implementation = "GetTickCount64()";
-        else
-            info->implementation = "GetTickCount()";
-        info->monotonic = 1;
-        ok = GetSystemTimeAdjustment(&timeAdjustment, &timeIncrement,
-                                     &isTimeAdjustmentDisabled);
-        if (!ok) {
-            PyErr_SetFromWindowsErr(0);
-            return NULL;
-        }
-        info->resolution = timeIncrement * 1e-7;
-        info->adjustable = 0;
-    }
-    return PyFloat_FromDouble(result);
-
-#elif defined(__APPLE__)
-    static mach_timebase_info_data_t timebase;
-    uint64_t time;
-    double secs;
-
-    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();
-    secs = (double)time * timebase.numer / timebase.denom * 1e-9;
-    if (info) {
-        info->implementation = "mach_absolute_time()";
-        info->resolution = (double)timebase.numer / timebase.denom * 1e-9;
-        info->monotonic = 1;
-        info->adjustable = 0;
-    }
-    return PyFloat_FromDouble(secs);
-
-#elif defined(HAVE_CLOCK_GETTIME) && (defined(CLOCK_HIGHRES) || defined(CLOCK_MONOTONIC))
-    struct timespec tp;
-#ifdef CLOCK_HIGHRES
-    const clockid_t clk_id = CLOCK_HIGHRES;
-    const char *function = "clock_gettime(CLOCK_HIGHRES)";
-#else
-    const clockid_t clk_id = CLOCK_MONOTONIC;
-    const char *function = "clock_gettime(CLOCK_MONOTONIC)";
-#endif
-
-    if (clock_gettime(clk_id, &tp) != 0) {
-        PyErr_SetFromErrno(PyExc_OSError);
+    _PyTime_timeval tv;
+    if (_PyTime_monotonic_info(&tv, info) < 0) {
+        assert(info != NULL);
         return NULL;
     }
-
-    if (info) {
-        struct timespec res;
-        info->monotonic = 1;
-        info->implementation = function;
-        info->adjustable = 0;
-        if (clock_getres(clk_id, &res) == 0)
-            info->resolution = res.tv_sec + res.tv_nsec * 1e-9;
-        else
-            info->resolution = 1e-9;
-    }
-    return PyFloat_FromDouble(tp.tv_sec + tp.tv_nsec * 1e-9);
-#endif
+    return PyFloat_FromDouble((double)tv.tv_sec + tv.tv_usec * 1e-6);
 }
 
 static PyObject *
@@ -1027,7 +916,6 @@
 "monotonic() -> float\n\
 \n\
 Monotonic clock, cannot go backward.");
-#endif   /* PYMONOTONIC */
 
 static PyObject*
 perf_counter(_Py_clock_info_t *info)
@@ -1035,20 +923,7 @@
 #ifdef WIN32_PERF_COUNTER
     return win_perf_counter(info);
 #else
-
-#ifdef PYMONOTONIC
-    static int use_monotonic = 1;
-
-    if (use_monotonic) {
-        PyObject *res = pymonotonic(info);
-        if (res != NULL)
-            return res;
-        use_monotonic = 0;
-        PyErr_Clear();
-    }
-#endif
-    return floattime(info);
-
+    return pymonotonic(info);
 #endif
 }
 
@@ -1216,10 +1091,8 @@
     else if (strcmp(name, "clock") == 0)
         obj = pyclock(&info);
 #endif
-#ifdef PYMONOTONIC
     else if (strcmp(name, "monotonic") == 0)
         obj = pymonotonic(&info);
-#endif
     else if (strcmp(name, "perf_counter") == 0)
         obj = perf_counter(&info);
     else if (strcmp(name, "process_time") == 0)
@@ -1411,9 +1284,7 @@
 #ifdef HAVE_WORKING_TZSET
     {"tzset",           time_tzset, METH_NOARGS, tzset_doc},
 #endif
-#ifdef PYMONOTONIC
     {"monotonic",       time_monotonic, METH_NOARGS, monotonic_doc},
-#endif
     {"process_time",    time_process_time, METH_NOARGS, process_time_doc},
     {"perf_counter",    time_perf_counter, METH_NOARGS, perf_counter_doc},
     {"get_clock_info",  time_get_clock_info, METH_VARARGS, get_clock_info_doc},
diff --git a/Python/pytime.c b/Python/pytime.c
--- a/Python/pytime.c
+++ b/Python/pytime.c
@@ -3,6 +3,14 @@
 #include <windows.h>
 #endif
 
+#if defined(__APPLE__)
+#include <mach/mach_time.h>   /* mach_absolute_time(), mach_timebase_info() */
+#endif
+
+#ifdef MS_WINDOWS
+static OSVERSIONINFOEX winver;
+#endif
+
 static int
 pygettimeofday(_PyTime_timeval *tp, _Py_clock_info_t *info, int raise)
 {
@@ -109,6 +117,160 @@
     return pygettimeofday(tp, info, 1);
 }
 
+static int
+pymonotonic(_PyTime_timeval *tp, _Py_clock_info_t *info, int raise)
+{
+#ifdef Py_DEBUG
+    static _PyTime_timeval last = {-1, -1};
+#endif
+#if defined(MS_WINDOWS)
+    static ULONGLONG (*GetTickCount64) (void) = NULL;
+    static ULONGLONG (CALLBACK *Py_GetTickCount64)(void);
+    static int has_gettickcount64 = -1;
+    ULONGLONG result;
+
+    assert(info == NULL || raise);
+
+    if (has_gettickcount64 == -1) {
+        /* GetTickCount64() was added to Windows Vista */
+        has_gettickcount64 = (winver.dwMajorVersion >= 6);
+        if (has_gettickcount64) {
+            HINSTANCE hKernel32;
+            hKernel32 = GetModuleHandleW(L"KERNEL32");
+            *(FARPROC*)&Py_GetTickCount64 = GetProcAddress(hKernel32,
+                                                           "GetTickCount64");
+            assert(Py_GetTickCount64 != NULL);
+        }
+    }
+
+    if (has_gettickcount64) {
+        result = Py_GetTickCount64();
+    }
+    else {
+        static DWORD last_ticks = 0;
+        static DWORD n_overflow = 0;
+        DWORD ticks;
+
+        ticks = GetTickCount();
+        if (ticks < last_ticks)
+            n_overflow++;
+        last_ticks = ticks;
+
+        result = (ULONGLONG)n_overflow << 32;
+        result += ticks;
+    }
+
+    tp->tv_sec = result / 1000;
+    tp->tv_usec = (result % 1000) * 1000;
+
+    if (info) {
+        DWORD timeAdjustment, timeIncrement;
+        BOOL isTimeAdjustmentDisabled, ok;
+        if (has_gettickcount64)
+            info->implementation = "GetTickCount64()";
+        else
+            info->implementation = "GetTickCount()";
+        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 /= 1000;
+    /* apply timebase factor */
+    time *= timebase.numer;
+    time /= timebase.denom;
+    tp->tv_sec = time / (1000 * 1000);
+    tp->tv_usec = time % (1000 * 1000);
+
+    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 / 1000;
+#endif
+    assert(0 <= tp->tv_usec && tp->tv_usec < 1000 * 1000);
+#ifdef Py_DEBUG
+    /* monotonic clock cannot go backward */
+    assert(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)
 {
@@ -245,8 +407,21 @@
 _PyTime_Init(void)
 {
     _PyTime_timeval tv;
+
+#ifdef MS_WINDOWS
+    winver.dwOSVersionInfoSize = sizeof(winver);
+    if (!GetVersionEx((OSVERSIONINFO*)&winver)) {
+        PyErr_SetFromWindowsErr(0);
+        return -1;
+    }
+#endif
+
     /* ensure that the system clock works */
     if (_PyTime_gettimeofday_info(&tv, NULL) < 0)
         return -1;
+
+    /* ensure that the operating system provides a monotonic clock */
+    if (_PyTime_monotonic_info(&tv, NULL) < 0)
+        return -1;
     return 0;
 }

-- 
Repository URL: http://hg.python.org/cpython


More information about the Python-checkins mailing list