From 6aaa574410a548108d521372d30a2427394a7793 Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Fri, 29 Aug 2014 15:41:08 +0200 Subject: Issue #22287: On UNIX, _PyTime_gettimeofday() now uses clock_gettime(CLOCK_REALTIME) if available. As a side effect, Python now depends on the librt library on Solaris and on Linux (only with glibc older than 2.17). --- Python/pytime.c | 54 +++++++++++++++++++++++++++++++++++++++++------------- 1 file changed, 41 insertions(+), 13 deletions(-) (limited to 'Python/pytime.c') diff --git a/Python/pytime.c b/Python/pytime.c index de6a41fe00..1f3fafb228 100644 --- a/Python/pytime.c +++ b/Python/pytime.c @@ -56,8 +56,39 @@ pygettimeofday(_PyTime_timeval *tp, _Py_clock_info_t *info) fail, so we fall back on ftime() or time(). Note: clock resolution does not imply clock accuracy! */ -#ifdef HAVE_GETTIMEOFDAY +#if (defined(HAVE_CLOCK_GETTIME) || defined(HAVE_GETTIMEOFDAY) \ + || defined(HAVE_FTIME)) int err; +#endif +#ifdef HAVE_CLOCK_GETTIME + struct timespec ts; +#endif +#ifdef HAVE_FTIME + struct timeb t; +#endif + + /* test clock_gettime(CLOCK_REALTIME) */ +#ifdef HAVE_CLOCK_GETTIME + err = clock_gettime(CLOCK_REALTIME, &ts); + if (err == 0) { + if (info) { + struct timespec res; + info->implementation = "clock_gettime(CLOCK_REALTIME)"; + info->monotonic = 0; + info->adjustable = 1; + if (clock_getres(CLOCK_REALTIME, &res) == 0) + info->resolution = res.tv_sec + res.tv_nsec * 1e-9; + else + info->resolution = 1e-9; + } + tp->tv_sec = ts.tv_sec; + tp->tv_usec = ts.tv_nsec / 1000; + return; + } +#endif + + /* test gettimeofday() */ +#ifdef HAVE_GETTIMEOFDAY #ifdef GETTIMEOFDAY_NO_TZ err = gettimeofday(tp); #else @@ -74,18 +105,15 @@ pygettimeofday(_PyTime_timeval *tp, _Py_clock_info_t *info) } #endif /* HAVE_GETTIMEOFDAY */ -#if defined(HAVE_FTIME) - { - struct timeb t; - ftime(&t); - tp->tv_sec = t.time; - tp->tv_usec = t.millitm * 1000; - if (info) { - info->implementation = "ftime()"; - info->resolution = 1e-3; - info->monotonic = 0; - info->adjustable = 1; - } +#ifdef HAVE_FTIME + ftime(&t); + tp->tv_sec = t.time; + tp->tv_usec = t.millitm * 1000; + if (info) { + info->implementation = "ftime()"; + info->resolution = 1e-3; + info->monotonic = 0; + info->adjustable = 1; } #else /* !HAVE_FTIME */ tp->tv_sec = time(NULL); -- cgit v1.2.1 From affccb4da7a3eea42bb7941044961aa2acb8a0f9 Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Fri, 29 Aug 2014 16:31:59 +0200 Subject: Issue #22043: _PyTime_Init() now checks if the system clock works. Other changes: * The whole _PyTime API is private (not defined if Py_LIMITED_API is set) * _PyTime_gettimeofday_info() also returns -1 on error * Simplify PyTime_gettimeofday(): only use clock_gettime(CLOCK_REALTIME) or gettimeofday() on UNIX. Don't fallback to ftime() or time() anymore. --- Python/pytime.c | 145 +++++++++++++++++++++++--------------------------------- 1 file changed, 60 insertions(+), 85 deletions(-) (limited to 'Python/pytime.c') diff --git a/Python/pytime.c b/Python/pytime.c index 1f3fafb228..31c8f66d11 100644 --- a/Python/pytime.c +++ b/Python/pytime.c @@ -3,29 +3,16 @@ #include #endif -#if defined(__APPLE__) && defined(HAVE_GETTIMEOFDAY) && defined(HAVE_FTIME) - /* - * _PyTime_gettimeofday falls back to ftime when getttimeofday fails because the latter - * might fail on some platforms. This fallback is unwanted on MacOSX because - * that makes it impossible to use a binary build on OSX 10.4 on earlier - * releases of the OS. Therefore claim we don't support ftime. - */ -# undef HAVE_FTIME -#endif - -#if defined(HAVE_FTIME) && !defined(MS_WINDOWS) -#include -extern int ftime(struct timeb *); -#endif - -static void -pygettimeofday(_PyTime_timeval *tp, _Py_clock_info_t *info) +static int +pygettimeofday(_PyTime_timeval *tp, _Py_clock_info_t *info, int raise) { #ifdef MS_WINDOWS FILETIME system_time; ULARGE_INTEGER large; ULONGLONG microseconds; + assert(info == NULL || raise); + GetSystemTimeAsFileTime(&system_time); large.u.LowPart = system_time.dwLowDateTime; large.u.HighPart = system_time.dwHighDateTime; @@ -37,55 +24,51 @@ pygettimeofday(_PyTime_timeval *tp, _Py_clock_info_t *info) tp->tv_usec = microseconds % 1000000; if (info) { DWORD timeAdjustment, timeIncrement; - BOOL isTimeAdjustmentDisabled; + BOOL isTimeAdjustmentDisabled, ok; info->implementation = "GetSystemTimeAsFileTime()"; info->monotonic = 0; - (void) GetSystemTimeAdjustment(&timeAdjustment, &timeIncrement, - &isTimeAdjustmentDisabled); + ok = GetSystemTimeAdjustment(&timeAdjustment, &timeIncrement, + &isTimeAdjustmentDisabled); + if (!ok) { + PyErr_SetFromWindowsErr(0); + return -1; + } info->resolution = timeIncrement * 1e-7; info->adjustable = 1; } -#else - /* There are three ways to get the time: - (1) gettimeofday() -- resolution in microseconds - (2) ftime() -- resolution in milliseconds - (3) time() -- resolution in seconds - In all cases the return value in a timeval struct. - Since on some systems (e.g. SCO ODT 3.0) gettimeofday() may - fail, so we fall back on ftime() or time(). - Note: clock resolution does not imply clock accuracy! */ + return 0; -#if (defined(HAVE_CLOCK_GETTIME) || defined(HAVE_GETTIMEOFDAY) \ - || defined(HAVE_FTIME)) +#else /* MS_WINDOWS */ int err; -#endif #ifdef HAVE_CLOCK_GETTIME struct timespec ts; #endif -#ifdef HAVE_FTIME - struct timeb t; -#endif - /* test clock_gettime(CLOCK_REALTIME) */ + assert(info == NULL || raise); + #ifdef HAVE_CLOCK_GETTIME - err = clock_gettime(CLOCK_REALTIME, &ts); - if (err == 0) { - if (info) { - struct timespec res; - info->implementation = "clock_gettime(CLOCK_REALTIME)"; - info->monotonic = 0; - info->adjustable = 1; - if (clock_getres(CLOCK_REALTIME, &res) == 0) - info->resolution = res.tv_sec + res.tv_nsec * 1e-9; - else - info->resolution = 1e-9; - } - tp->tv_sec = ts.tv_sec; - tp->tv_usec = ts.tv_nsec / 1000; - return; + err = clock_gettime(CLOCK_REALTIME, &ts); + if (err) { + if (raise) + PyErr_SetFromErrno(PyExc_OSError); + return -1; } -#endif + tp->tv_sec = ts.tv_sec; + tp->tv_usec = ts.tv_nsec / 1000; + + if (info) { + struct timespec res; + info->implementation = "clock_gettime(CLOCK_REALTIME)"; + info->monotonic = 0; + info->adjustable = 1; + if (clock_getres(CLOCK_REALTIME, &res) == 0) + info->resolution = res.tv_sec + res.tv_nsec * 1e-9; + else + info->resolution = 1e-9; + } + return 0; +#else /* HAVE_CLOCK_GETTIME */ /* test gettimeofday() */ #ifdef HAVE_GETTIMEOFDAY @@ -94,51 +77,39 @@ pygettimeofday(_PyTime_timeval *tp, _Py_clock_info_t *info) #else err = gettimeofday(tp, (struct timezone *)NULL); #endif - if (err == 0) { - if (info) { - info->implementation = "gettimeofday()"; - info->resolution = 1e-6; - info->monotonic = 0; - info->adjustable = 1; - } - return; + if (err) { + if (raise) + PyErr_SetFromErrno(PyExc_OSError); + return -1; } -#endif /* HAVE_GETTIMEOFDAY */ -#ifdef HAVE_FTIME - ftime(&t); - tp->tv_sec = t.time; - tp->tv_usec = t.millitm * 1000; - if (info) { - info->implementation = "ftime()"; - info->resolution = 1e-3; - info->monotonic = 0; - info->adjustable = 1; - } -#else /* !HAVE_FTIME */ - tp->tv_sec = time(NULL); - tp->tv_usec = 0; if (info) { - info->implementation = "time()"; - info->resolution = 1.0; + info->implementation = "gettimeofday()"; + info->resolution = 1e-6; info->monotonic = 0; info->adjustable = 1; } -#endif /* !HAVE_FTIME */ - -#endif /* MS_WINDOWS */ + return 0; +#endif /* HAVE_GETTIMEOFDAY */ +#endif /* !HAVE_CLOCK_GETTIME */ +#endif /* !MS_WINDOWS */ } void _PyTime_gettimeofday(_PyTime_timeval *tp) { - pygettimeofday(tp, NULL); + if (pygettimeofday(tp, NULL, 0) < 0) { + /* cannot happen, _PyTime_Init() checks that pygettimeofday() works */ + assert(0); + tp->tv_sec = 0; + tp->tv_usec = 0; + } } -void +int _PyTime_gettimeofday_info(_PyTime_timeval *tp, _Py_clock_info_t *info) { - pygettimeofday(tp, info); + return pygettimeofday(tp, info, 1); } static void @@ -273,8 +244,12 @@ _PyTime_ObjectToTimeval(PyObject *obj, time_t *sec, long *usec, return _PyTime_ObjectToDenominator(obj, sec, usec, 1e6, round); } -void -_PyTime_Init() +int +_PyTime_Init(void) { - /* Do nothing. Needed to force linking. */ + _PyTime_timeval tv; + /* ensure that the system clock works */ + if (_PyTime_gettimeofday_info(&tv, NULL) < 0) + return -1; + return 0; } -- cgit v1.2.1 From 9bc89143ea19a6827ddb5cf8eab7ee1a8effd17e Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Tue, 2 Sep 2014 23:01:40 +0200 Subject: Issue #22043: Fix _PyTime_gettimeofday() if HAVE_GETTIMEOFDAY Ensure also that the tv_usec field is consistent: in range [0; 999999]. --- Python/pytime.c | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) (limited to 'Python/pytime.c') diff --git a/Python/pytime.c b/Python/pytime.c index 31c8f66d11..395a432da5 100644 --- a/Python/pytime.c +++ b/Python/pytime.c @@ -37,7 +37,6 @@ pygettimeofday(_PyTime_timeval *tp, _Py_clock_info_t *info, int raise) info->resolution = timeIncrement * 1e-7; info->adjustable = 1; } - return 0; #else /* MS_WINDOWS */ int err; @@ -67,11 +66,9 @@ pygettimeofday(_PyTime_timeval *tp, _Py_clock_info_t *info, int raise) else info->resolution = 1e-9; } - return 0; #else /* HAVE_CLOCK_GETTIME */ /* test gettimeofday() */ -#ifdef HAVE_GETTIMEOFDAY #ifdef GETTIMEOFDAY_NO_TZ err = gettimeofday(tp); #else @@ -89,10 +86,10 @@ pygettimeofday(_PyTime_timeval *tp, _Py_clock_info_t *info, int raise) info->monotonic = 0; info->adjustable = 1; } - return 0; -#endif /* HAVE_GETTIMEOFDAY */ #endif /* !HAVE_CLOCK_GETTIME */ #endif /* !MS_WINDOWS */ + assert(0 <= tp->tv_usec && tp->tv_usec < 1000 * 1000); + return 0; } void -- cgit v1.2.1 From f3579a8d0eed6151a1e2f34f6cad0677bfe45246 Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Tue, 2 Sep 2014 23:18:25 +0200 Subject: 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. --- Python/pytime.c | 175 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 175 insertions(+) (limited to 'Python/pytime.c') diff --git a/Python/pytime.c b/Python/pytime.c index 395a432da5..9964195378 100644 --- a/Python/pytime.c +++ b/Python/pytime.c @@ -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) { @@ -109,6 +117,160 @@ _PyTime_gettimeofday_info(_PyTime_timeval *tp, _Py_clock_info_t *info) 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 @@ 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; } -- cgit v1.2.1 From 085e41135e902845e6c6bdc924a5eeffcb475860 Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Wed, 3 Sep 2014 09:43:48 +0200 Subject: Issue #22043: Fix pymonotonic(), use tv_usec=-1 as a marker to skip the monotonic test --- Python/pytime.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'Python/pytime.c') diff --git a/Python/pytime.c b/Python/pytime.c index 9964195378..a8460c6867 100644 --- a/Python/pytime.c +++ b/Python/pytime.c @@ -121,7 +121,7 @@ static int pymonotonic(_PyTime_timeval *tp, _Py_clock_info_t *info, int raise) { #ifdef Py_DEBUG - static _PyTime_timeval last = {-1, -1}; + static _PyTime_timeval last = {0, -1}; #endif #if defined(MS_WINDOWS) static ULONGLONG (*GetTickCount64) (void) = NULL; @@ -247,7 +247,8 @@ pymonotonic(_PyTime_timeval *tp, _Py_clock_info_t *info, int raise) 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 + 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 -- cgit v1.2.1