changeset: 92268:0c37403d6a58 tag: tip user: Victor Stinner date: Fri Aug 29 17:04:58 2014 +0200 files: Doc/library/time.rst Include/pytime.h Lib/queue.py Lib/sched.py Lib/socketserver.py Lib/subprocess.py Lib/telnetlib.py Lib/test/test_selectors.py Lib/threading.py Lib/trace.py Modules/_threadmodule.c Modules/gcmodule.c Modules/socketmodule.c Modules/timemodule.c Python/pytime.c description: test diff -r ab81b4cdc33c -r 0c37403d6a58 Doc/library/time.rst --- a/Doc/library/time.rst Fri Aug 29 17:00:17 2014 +0200 +++ b/Doc/library/time.rst Fri Aug 29 17:04:58 2014 +0200 @@ -315,9 +315,9 @@ The module defines the following functio 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 -r ab81b4cdc33c -r 0c37403d6a58 Include/pytime.h --- a/Include/pytime.h Fri Aug 29 17:00:17 2014 +0200 +++ b/Include/pytime.h Fri Aug 29 17:04:58 2014 +0200 @@ -93,6 +93,24 @@ 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); + /* Initialize time. Return 0 on success, raise an exception and return -1 on error. */ PyAPI_FUNC(int) _PyTime_Init(void); diff -r ab81b4cdc33c -r 0c37403d6a58 Lib/queue.py --- a/Lib/queue.py Fri Aug 29 17:00:17 2014 +0200 +++ b/Lib/queue.py Fri Aug 29 17:04:58 2014 +0200 @@ -6,10 +6,7 @@ except ImportError: 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 -r ab81b4cdc33c -r 0c37403d6a58 Lib/sched.py --- a/Lib/sched.py Fri Aug 29 17:00:17 2014 +0200 +++ b/Lib/sched.py Fri Aug 29 17:04:58 2014 +0200 @@ -35,10 +35,7 @@ try: 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 -r ab81b4cdc33c -r 0c37403d6a58 Lib/socketserver.py --- a/Lib/socketserver.py Fri Aug 29 17:00:17 2014 +0200 +++ b/Lib/socketserver.py Fri Aug 29 17:04:58 2014 +0200 @@ -136,10 +136,7 @@ try: 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 -r ab81b4cdc33c -r 0c37403d6a58 Lib/subprocess.py --- a/Lib/subprocess.py Fri Aug 29 17:00:17 2014 +0200 +++ b/Lib/subprocess.py Fri Aug 29 17:04:58 2014 +0200 @@ -365,10 +365,7 @@ import signal 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 -r ab81b4cdc33c -r 0c37403d6a58 Lib/telnetlib.py --- a/Lib/telnetlib.py Fri Aug 29 17:00:17 2014 +0200 +++ b/Lib/telnetlib.py Fri Aug 29 17:04:58 2014 +0200 @@ -36,10 +36,7 @@ To do: 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 -r ab81b4cdc33c -r 0c37403d6a58 Lib/test/test_selectors.py --- a/Lib/test/test_selectors.py Fri Aug 29 17:00:17 2014 +0200 +++ b/Lib/test/test_selectors.py Fri Aug 29 17:04:58 2014 +0200 @@ -8,10 +8,7 @@ from test import support 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 -r ab81b4cdc33c -r 0c37403d6a58 Lib/threading.py --- a/Lib/threading.py Fri Aug 29 17:00:17 2014 +0200 +++ b/Lib/threading.py Fri Aug 29 17:04:58 2014 +0200 @@ -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 -r ab81b4cdc33c -r 0c37403d6a58 Lib/trace.py --- a/Lib/trace.py Fri Aug 29 17:00:17 2014 +0200 +++ b/Lib/trace.py Fri Aug 29 17:04:58 2014 +0200 @@ -59,10 +59,7 @@ import gc 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 -r ab81b4cdc33c -r 0c37403d6a58 Modules/_threadmodule.c --- a/Modules/_threadmodule.c Fri Aug 29 17:00:17 2014 +0200 +++ b/Modules/_threadmodule.c Fri Aug 29 17:04:58 2014 +0200 @@ -57,7 +57,7 @@ acquire_timed(PyThread_type_lock lock, P 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 @@ acquire_timed(PyThread_type_lock lock, P /* 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 -r ab81b4cdc33c -r 0c37403d6a58 Modules/gcmodule.c --- a/Modules/gcmodule.c Fri Aug 29 17:00:17 2014 +0200 +++ b/Modules/gcmodule.c Fri Aug 29 17:04:58 2014 +0200 @@ -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 @@ collect(int generation, Py_ssize_t *n_co 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 @@ collect(int generation, Py_ssize_t *n_co } if (debug & DEBUG_STATS) { _PyTime_timeval t2; - _PyTime_gettimeofday(&t2); + _PyTime_monotonic(&t2); if (m == 0 && n == 0) PySys_WriteStderr("gc: done"); diff -r ab81b4cdc33c -r 0c37403d6a58 Modules/socketmodule.c --- a/Modules/socketmodule.c Fri Aug 29 17:00:17 2014 +0200 +++ b/Modules/socketmodule.c Fri Aug 29 17:04:58 2014 +0200 @@ -680,7 +680,7 @@ internal_select(PySocketSockObject *s, i 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 @@ internal_select(PySocketSockObject *s, i if (!has_timeout || \ (!CHECK_ERRNO(EWOULDBLOCK) && !CHECK_ERRNO(EAGAIN))) \ break; \ - _PyTime_gettimeofday(&now); \ + _PyTime_monotonic(&now); \ interval = _PyTime_INTERVAL(now, deadline); \ } \ } \ diff -r ab81b4cdc33c -r 0c37403d6a58 Modules/timemodule.c --- a/Modules/timemodule.c Fri Aug 29 17:00:17 2014 +0200 +++ b/Modules/timemodule.c Fri Aug 29 17:04:58 2014 +0200 @@ -37,10 +37,6 @@ #endif /* MS_WINDOWS */ #endif /* !__WATCOMC__ || __QNX__ */ -#if defined(__APPLE__) -#include -#endif - /* Forward declarations */ static int floatsleep(double); static PyObject* floattime(_Py_clock_info_t *info); @@ -899,122 +895,15 @@ the local timezone used by methods such 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 @@ PyDoc_STRVAR(monotonic_doc, "monotonic() -> float\n\ \n\ Monotonic clock, cannot go backward."); -#endif /* PYMONOTONIC */ static PyObject* perf_counter(_Py_clock_info_t *info) @@ -1035,20 +923,7 @@ perf_counter(_Py_clock_info_t *info) #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 @@ time_get_clock_info(PyObject *self, PyOb 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 @@ static PyMethodDef time_methods[] = { #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 -r ab81b4cdc33c -r 0c37403d6a58 Python/pytime.c --- a/Python/pytime.c Fri Aug 29 17:00:17 2014 +0200 +++ b/Python/pytime.c Fri Aug 29 17:04:58 2014 +0200 @@ -3,6 +3,14 @@ #include #endif +#if defined(__APPLE__) +#include /* 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) { @@ -112,6 +120,153 @@ int return pygettimeofday(tp, info, 1); } +static int +pymonotonic(_PyTime_timeval *tp, _Py_clock_info_t *info, int raise) +{ +#if defined(MS_WINDOWS) + static ULONGLONG (*GetTickCount64) (void) = NULL; + static ULONGLONG (CALLBACK *Py_GetTickCount64)(void); + static int has_getickcount64 = -1; + ULONGLONG result; + + assert(info == NULL || raise); + + 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) { + 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_getickcount64) + 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; + } + return 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; + } + return 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; + return 0; +#endif +} + +void +_PyTime_monotonic(_PyTime_timeval *tp) +{ + if (pymonotonic(tp, NULL, 0) < 0) { + /* cannot happen, _PyTime_Init() checks that pygettimeofday() 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) { @@ -248,8 +403,21 @@ int _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; }