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 From 547de7c7bdd902a96d8b74f35e3a24c4eeabaf2f Mon Sep 17 00:00:00 2001 From: Steve Dower Date: Mon, 2 Mar 2015 08:01:10 -0800 Subject: Issue #23451: Update pyconfig.h for Windows to require Vista headers and remove unnecessary version checks. --- Python/pytime.c | 49 ++----------------------------------------------- 1 file changed, 2 insertions(+), 47 deletions(-) (limited to 'Python/pytime.c') diff --git a/Python/pytime.c b/Python/pytime.c index a8460c6867..cdaa22ec66 100644 --- a/Python/pytime.c +++ b/Python/pytime.c @@ -7,10 +7,6 @@ #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) { @@ -124,41 +120,11 @@ pymonotonic(_PyTime_timeval *tp, _Py_clock_info_t *info, int raise) static _PyTime_timeval last = {0, -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; - } + result = GetTickCount64(); tp->tv_sec = result / 1000; tp->tv_usec = (result % 1000) * 1000; @@ -166,10 +132,7 @@ pymonotonic(_PyTime_timeval *tp, _Py_clock_info_t *info, int raise) if (info) { DWORD timeAdjustment, timeIncrement; BOOL isTimeAdjustmentDisabled, ok; - if (has_gettickcount64) - info->implementation = "GetTickCount64()"; - else - info->implementation = "GetTickCount()"; + info->implementation = "GetTickCount64()"; info->monotonic = 1; ok = GetSystemTimeAdjustment(&timeAdjustment, &timeIncrement, &isTimeAdjustmentDisabled); @@ -409,14 +372,6 @@ _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; -- cgit v1.2.1 From 698efefbc45dbd581dbf12fb70ab2fccc62a075e Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Fri, 20 Mar 2015 01:42:20 +0100 Subject: Issue #23646: Enhance precision of time.sleep() and socket timeout when interrupted by a signal Add a new _PyTime_AddDouble() function and remove _PyTime_ADD_SECONDS() macro. The _PyTime_ADD_SECONDS only supported an integer number of seconds, the _PyTime_AddDouble() has subsecond resolution. --- Python/pytime.c | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) (limited to 'Python/pytime.c') diff --git a/Python/pytime.c b/Python/pytime.c index cdaa22ec66..894008db8b 100644 --- a/Python/pytime.c +++ b/Python/pytime.c @@ -367,6 +367,23 @@ _PyTime_ObjectToTimeval(PyObject *obj, time_t *sec, long *usec, return _PyTime_ObjectToDenominator(obj, sec, usec, 1e6, round); } +void +_PyTime_AddDouble(_PyTime_timeval *tv, double interval, _PyTime_round_t round) +{ + _PyTime_timeval tv2; + double frac; + + frac = fmod(interval, 1.0); + interval = floor(interval); + tv2.tv_sec = (long)interval; + tv2.tv_usec = (long)(frac*1e6); + + tv->tv_sec += tv2.tv_sec; + tv->tv_usec += tv2.tv_usec; + tv->tv_sec += (time_t)(tv->tv_usec / 1000000); + tv->tv_usec %= 1000000; +} + int _PyTime_Init(void) { -- cgit v1.2.1 From b274c94b273e8e7385c5f46afaba989a124d1619 Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Fri, 20 Mar 2015 01:55:04 +0100 Subject: Cleanup pytime.c: add XXX_TO_YYY constants (ex: SEC_TO_US) --- Python/pytime.c | 32 +++++++++++++++++++------------- 1 file changed, 19 insertions(+), 13 deletions(-) (limited to 'Python/pytime.c') diff --git a/Python/pytime.c b/Python/pytime.c index 894008db8b..578569d4dd 100644 --- a/Python/pytime.c +++ b/Python/pytime.c @@ -7,6 +7,12 @@ #include /* mach_absolute_time(), mach_timebase_info() */ #endif +#define SEC_TO_MS 1000 +#define MS_TO_US 1000 +#define US_TO_NS 1000 + +#define SEC_TO_US (SEC_TO_MS * MS_TO_US) + static int pygettimeofday(_PyTime_timeval *tp, _Py_clock_info_t *info, int raise) { @@ -24,8 +30,8 @@ pygettimeofday(_PyTime_timeval *tp, _Py_clock_info_t *info, int raise) the 1st january 1601 and the 1st january 1970 (369 years + 89 leap days). */ microseconds = large.QuadPart / 10 - 11644473600000000; - tp->tv_sec = microseconds / 1000000; - tp->tv_usec = microseconds % 1000000; + tp->tv_sec = microseconds / SEC_TO_US; + tp->tv_usec = microseconds % SEC_TO_US; if (info) { DWORD timeAdjustment, timeIncrement; BOOL isTimeAdjustmentDisabled, ok; @@ -58,7 +64,7 @@ pygettimeofday(_PyTime_timeval *tp, _Py_clock_info_t *info, int raise) return -1; } tp->tv_sec = ts.tv_sec; - tp->tv_usec = ts.tv_nsec / 1000; + tp->tv_usec = ts.tv_nsec / US_TO_NS; if (info) { struct timespec res; @@ -92,7 +98,7 @@ pygettimeofday(_PyTime_timeval *tp, _Py_clock_info_t *info, int raise) } #endif /* !HAVE_CLOCK_GETTIME */ #endif /* !MS_WINDOWS */ - assert(0 <= tp->tv_usec && tp->tv_usec < 1000 * 1000); + assert(0 <= tp->tv_usec && tp->tv_usec < SEC_TO_US); return 0; } @@ -126,8 +132,8 @@ pymonotonic(_PyTime_timeval *tp, _Py_clock_info_t *info, int raise) result = GetTickCount64(); - tp->tv_sec = result / 1000; - tp->tv_usec = (result % 1000) * 1000; + tp->tv_sec = result / SEC_TO_MS; + tp->tv_usec = (result % SEC_TO_MS) * MS_TO_US; if (info) { DWORD timeAdjustment, timeIncrement; @@ -157,12 +163,12 @@ pymonotonic(_PyTime_timeval *tp, _Py_clock_info_t *info, int raise) time = mach_absolute_time(); /* nanoseconds => microseconds */ - time /= 1000; + time /= US_TO_NS; /* apply timebase factor */ time *= timebase.numer; time /= timebase.denom; - tp->tv_sec = time / (1000 * 1000); - tp->tv_usec = time % (1000 * 1000); + tp->tv_sec = time / SEC_TO_US; + tp->tv_usec = time % SEC_TO_US; if (info) { info->implementation = "mach_absolute_time()"; @@ -205,9 +211,9 @@ pymonotonic(_PyTime_timeval *tp, _Py_clock_info_t *info, int raise) info->resolution = res.tv_sec + res.tv_nsec * 1e-9; } tp->tv_sec = ts.tv_sec; - tp->tv_usec = ts.tv_nsec / 1000; + tp->tv_usec = ts.tv_nsec / US_TO_NS; #endif - assert(0 <= tp->tv_usec && tp->tv_usec < 1000 * 1000); + assert(0 <= tp->tv_usec && tp->tv_usec < SEC_TO_US); #ifdef Py_DEBUG /* monotonic clock cannot go backward */ assert(last.tv_usec == -1 @@ -380,8 +386,8 @@ _PyTime_AddDouble(_PyTime_timeval *tv, double interval, _PyTime_round_t round) tv->tv_sec += tv2.tv_sec; tv->tv_usec += tv2.tv_usec; - tv->tv_sec += (time_t)(tv->tv_usec / 1000000); - tv->tv_usec %= 1000000; + tv->tv_sec += (time_t)(tv->tv_usec / SEC_TO_US); + tv->tv_usec %= SEC_TO_US; } int -- cgit v1.2.1 From 6fd74191973e8e9af1813e9bd49075cc048dff82 Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Fri, 27 Mar 2015 13:31:18 +0100 Subject: Issue #22117: Add a new Python timestamp format _PyTime_t to pytime.h In practice, _PyTime_t is a number of nanoseconds. Its C type is a 64-bit signed number. It's integer value is in the range [-2^63; 2^63-1]. In seconds, the range is around [-292 years; +292 years]. In term of Epoch timestamp (1970-01-01), it can store a date between 1677-09-21 and 2262-04-11. The API has a resolution of 1 nanosecond and use integer number. With a resolution on 1 nanosecond, 64-bit IEEE 754 floating point numbers loose precision after 194 days. It's not the case with this API. The drawback is overflow for values outside [-2^63; 2^63-1], but these values are unlikely for most Python modules, except of the datetime module. New functions: - _PyTime_GetMonotonicClock() - _PyTime_FromObject() - _PyTime_AsMilliseconds() - _PyTime_AsTimeval() This change uses these new functions in time.sleep() to avoid rounding issues. The new API will be extended step by step, and the old API will be removed step by step. Currently, some code is duplicated just to be able to move incrementally, instead of pushing a large change at once. --- Python/pytime.c | 311 +++++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 310 insertions(+), 1 deletion(-) (limited to 'Python/pytime.c') diff --git a/Python/pytime.c b/Python/pytime.c index 578569d4dd..52bd958dbb 100644 --- a/Python/pytime.c +++ b/Python/pytime.c @@ -7,11 +7,21 @@ #include /* mach_absolute_time(), mach_timebase_info() */ #endif +/* To millisecond (10^-3) */ #define SEC_TO_MS 1000 + +/* To microseconds (10^-6) */ #define MS_TO_US 1000 +#define SEC_TO_US (SEC_TO_MS * MS_TO_US) + +/* To nanoseconds (10^-9) */ #define US_TO_NS 1000 +#define MS_TO_NS (MS_TO_US * US_TO_NS) +#define SEC_TO_NS (SEC_TO_MS * MS_TO_NS) -#define SEC_TO_US (SEC_TO_MS * MS_TO_US) +#ifdef MS_WINDOWS +static OSVERSIONINFOEX winver; +#endif static int pygettimeofday(_PyTime_timeval *tp, _Py_clock_info_t *info, int raise) @@ -390,10 +400,305 @@ _PyTime_AddDouble(_PyTime_timeval *tv, double interval, _PyTime_round_t round) tv->tv_usec %= SEC_TO_US; } +/****************** NEW _PyTime_t API **********************/ + +static void +_PyTime_overflow(void) +{ + PyErr_SetString(PyExc_OverflowError, + "timestamp too large to convert to C _PyTime_t"); +} + +#if !defined(MS_WINDOWS) && !defined(__APPLE__) +static int +_PyTime_FromTimespec(_PyTime_t *tp, struct timespec *ts) +{ + _PyTime_t t; + t = (_PyTime_t)ts->tv_sec * SEC_TO_NS; + if (t / SEC_TO_NS != ts->tv_sec) { + _PyTime_overflow(); + return -1; + } + + t += ts->tv_nsec; + + *tp = t; + return 0; +} +#endif + +int +_PyTime_FromObject(_PyTime_t *t, PyObject *obj, _PyTime_round_t round) +{ + if (PyFloat_Check(obj)) { + double d, err; + + /* convert to a number of nanoseconds */ + d = PyFloat_AsDouble(obj); + d *= 1e9; + + /* FIXME: use sign */ + if (round == _PyTime_ROUND_UP) + d = ceil(d); + else + d = floor(d); + + *t = (_PyTime_t)d; + err = d - (double)*t; + if (fabs(err) >= 1.0) { + _PyTime_overflow(); + return -1; + } + return 0; + } + else { +#ifdef HAVE_LONG_LONG + PY_LONG_LONG sec; + sec = PyLong_AsLongLong(obj); + assert(sizeof(PY_LONG_LONG) <= sizeof(_PyTime_t)); +#else + long sec; + sec = PyLong_AsLong(obj); + assert(sizeof(PY_LONG_LONG) <= sizeof(_PyTime_t)); +#endif + if (sec == -1 && PyErr_Occurred()) { + if (PyErr_ExceptionMatches(PyExc_OverflowError)) + _PyTime_overflow(); + return -1; + } + *t = sec * SEC_TO_NS; + if (*t / SEC_TO_NS != sec) { + _PyTime_overflow(); + return -1; + } + return 0; + } +} + +static _PyTime_t +_PyTime_Multiply(_PyTime_t t, unsigned int multiply, _PyTime_round_t round) +{ + _PyTime_t k; + if (multiply < SEC_TO_NS) { + k = SEC_TO_NS / multiply; + if (round == _PyTime_ROUND_UP) + return (t + k - 1) / k; + else + return t / k; + } + else { + k = multiply / SEC_TO_NS; + return t * k; + } +} + +_PyTime_t +_PyTime_AsMilliseconds(_PyTime_t t, _PyTime_round_t round) +{ + return _PyTime_Multiply(t, 1000, round); +} + +int +_PyTime_AsTimeval(_PyTime_t t, struct timeval *tv, _PyTime_round_t round) +{ + _PyTime_t secs, ns; + + secs = t / SEC_TO_NS; + ns = t % SEC_TO_NS; + +#ifdef MS_WINDOWS + /* On Windows, timeval.tv_sec is a long (32 bit), + whereas time_t can be 64-bit. */ + assert(sizeof(tv->tv_sec) == sizeof(long)); +#if SIZEOF_TIME_T > SIZEOF_LONG + if (secs > LONG_MAX) { + _PyTime_overflow(); + return -1; + } +#endif + tv->tv_sec = (long)secs; +#else + /* On OpenBSD 5.4, timeval.tv_sec is a long. + Example: long is 64-bit, whereas time_t is 32-bit. */ + tv->tv_sec = secs; + if ((_PyTime_t)tv->tv_sec != secs) { + _PyTime_overflow(); + return -1; + } +#endif + + if (round == _PyTime_ROUND_UP) + tv->tv_usec = (int)((ns + US_TO_NS - 1) / US_TO_NS); + else + tv->tv_usec = (int)(ns / US_TO_NS); + return 0; +} + +static int +pymonotonic_new(_PyTime_t *tp, _Py_clock_info_t *info, int raise) +{ +#ifdef Py_DEBUG + static int last_set = 0; + static _PyTime_t last = 0; +#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 = result * MS_TO_NS; + if (*tp / MS_TO_NS != result) { + if (raise) { + _PyTime_overflow(); + return -1; + } + /* Hello, time traveler! */ + assert(0); + } + + 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(); + + /* apply timebase factor */ + time *= timebase.numer; + time /= timebase.denom; + + *tp = time; + + 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; + } + 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; + } + if (_PyTime_FromTimespec(tp, &ts) < 0) + return -1; +#endif +#ifdef Py_DEBUG + /* monotonic clock cannot go backward */ + assert(!last_set || last <= *tp); + last = *tp; + last_set = 1; +#endif + return 0; +} + +_PyTime_t +_PyTime_GetMonotonicClock(void) +{ + _PyTime_t t; + if (pymonotonic_new(&t, NULL, 0) < 0) { + /* cannot happen, _PyTime_Init() checks that pymonotonic_new() works */ + assert(0); + t = 0; + } + return t; +} + int _PyTime_Init(void) { _PyTime_timeval tv; + _PyTime_t t; + +#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) @@ -402,5 +707,9 @@ _PyTime_Init(void) /* 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 (pymonotonic_new(&t, NULL, 1) < 0) + return -1; return 0; } -- cgit v1.2.1 From 9cb2d3d6fc6594626c47dc7b2b42674b85bd8081 Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Fri, 27 Mar 2015 14:12:08 +0100 Subject: Issue #23451, #22117: Python 3.5 now requires Windows Vista or newer, so GetTickCount64() is now always available. --- Python/pytime.c | 49 ++----------------------------------------------- 1 file changed, 2 insertions(+), 47 deletions(-) (limited to 'Python/pytime.c') diff --git a/Python/pytime.c b/Python/pytime.c index 52bd958dbb..6bf7030cba 100644 --- a/Python/pytime.c +++ b/Python/pytime.c @@ -19,10 +19,6 @@ #define MS_TO_NS (MS_TO_US * US_TO_NS) #define SEC_TO_NS (SEC_TO_MS * MS_TO_NS) -#ifdef MS_WINDOWS -static OSVERSIONINFOEX winver; -#endif - static int pygettimeofday(_PyTime_timeval *tp, _Py_clock_info_t *info, int raise) { @@ -542,41 +538,11 @@ pymonotonic_new(_PyTime_t *tp, _Py_clock_info_t *info, int raise) static _PyTime_t last = 0; #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; - } + result = GetTickCount64(); *tp = result * MS_TO_NS; if (*tp / MS_TO_NS != result) { @@ -591,10 +557,7 @@ pymonotonic_new(_PyTime_t *tp, _Py_clock_info_t *info, int raise) if (info) { DWORD timeAdjustment, timeIncrement; BOOL isTimeAdjustmentDisabled, ok; - if (has_gettickcount64) - info->implementation = "GetTickCount64()"; - else - info->implementation = "GetTickCount()"; + info->implementation = "GetTickCount64()"; info->monotonic = 1; ok = GetSystemTimeAdjustment(&timeAdjustment, &timeIncrement, &isTimeAdjustmentDisabled); @@ -692,14 +655,6 @@ _PyTime_Init(void) _PyTime_timeval tv; _PyTime_t t; -#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; -- cgit v1.2.1 From 8868ac99631bc516c59dcc9f0d112683ea6cbcc4 Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Fri, 27 Mar 2015 17:12:45 +0100 Subject: Issue #22117: Fix rounding in _PyTime_FromSecondsObject() * Rename _PyTime_FromObject() to _PyTime_FromSecondsObject() * Add _PyTime_AsNanosecondsObject() and _testcapi.pytime_fromsecondsobject() * Add unit tests --- Python/pytime.c | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) (limited to 'Python/pytime.c') diff --git a/Python/pytime.c b/Python/pytime.c index 6bf7030cba..2aeeddc943 100644 --- a/Python/pytime.c +++ b/Python/pytime.c @@ -424,7 +424,7 @@ _PyTime_FromTimespec(_PyTime_t *tp, struct timespec *ts) #endif int -_PyTime_FromObject(_PyTime_t *t, PyObject *obj, _PyTime_round_t round) +_PyTime_FromSecondsObject(_PyTime_t *t, PyObject *obj, _PyTime_round_t round) { if (PyFloat_Check(obj)) { double d, err; @@ -433,8 +433,7 @@ _PyTime_FromObject(_PyTime_t *t, PyObject *obj, _PyTime_round_t round) d = PyFloat_AsDouble(obj); d *= 1e9; - /* FIXME: use sign */ - if (round == _PyTime_ROUND_UP) + if ((round == _PyTime_ROUND_UP) ^ (d < 0)) d = ceil(d); else d = floor(d); @@ -471,6 +470,18 @@ _PyTime_FromObject(_PyTime_t *t, PyObject *obj, _PyTime_round_t round) } } +PyObject * +_PyTime_AsNanosecondsObject(_PyTime_t t) +{ +#ifdef HAVE_LONG_LONG + assert(sizeof(PY_LONG_LONG) >= sizeof(_PyTime_t)); + return PyLong_FromLongLong((PY_LONG_LONG)t); +#else + assert(sizeof(long) >= sizeof(_PyTime_t)); + return PyLong_FromLong((long)t); +#endif +} + static _PyTime_t _PyTime_Multiply(_PyTime_t t, unsigned int multiply, _PyTime_round_t round) { -- cgit v1.2.1 From 6f52573ef10e80de3fec8705dd6b6df16e0e7f53 Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Fri, 27 Mar 2015 22:27:24 +0100 Subject: Issue #22117: time.monotonic() now uses the new _PyTime_t API * Add _PyTime_FromNanoseconds() * Add _PyTime_AsSecondsDouble() * Add unit tests for _PyTime_AsSecondsDouble() --- Python/pytime.c | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) (limited to 'Python/pytime.c') diff --git a/Python/pytime.c b/Python/pytime.c index 2aeeddc943..a4963357df 100644 --- a/Python/pytime.c +++ b/Python/pytime.c @@ -405,6 +405,15 @@ _PyTime_overflow(void) "timestamp too large to convert to C _PyTime_t"); } +_PyTime_t +_PyTime_FromNanoseconds(PY_LONG_LONG ns) +{ + _PyTime_t t; + assert(sizeof(PY_LONG_LONG) <= sizeof(_PyTime_t)); + t = Py_SAFE_DOWNCAST(ns, PY_LONG_LONG, _PyTime_t); + return t; +} + #if !defined(MS_WINDOWS) && !defined(__APPLE__) static int _PyTime_FromTimespec(_PyTime_t *tp, struct timespec *ts) @@ -470,6 +479,17 @@ _PyTime_FromSecondsObject(_PyTime_t *t, PyObject *obj, _PyTime_round_t round) } } +double +_PyTime_AsSecondsDouble(_PyTime_t t) +{ + _PyTime_t sec, ns; + /* Divide using integers to avoid rounding issues on the integer part. + 1e-9 cannot be stored exactly in IEEE 64-bit. */ + sec = t / SEC_TO_NS; + ns = t % SEC_TO_NS; + return (double)sec + (double)ns * 1e-9; +} + PyObject * _PyTime_AsNanosecondsObject(_PyTime_t t) { @@ -660,6 +680,12 @@ _PyTime_GetMonotonicClock(void) return t; } +int +_PyTime_GetMonotonicClockWithInfo(_PyTime_t *tp, _Py_clock_info_t *info) +{ + return pymonotonic_new(tp, info, 1); +} + int _PyTime_Init(void) { -- cgit v1.2.1 From fb93bc7a7bd2901fa147b30d815eacc8e7154d9e Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Fri, 27 Mar 2015 18:16:17 +0100 Subject: Issue #22117: time.time() now uses the new _PyTime_t API * Add _PyTime_GetSystemClockWithInfo() --- Python/pytime.c | 129 ++++++++++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 120 insertions(+), 9 deletions(-) (limited to 'Python/pytime.c') diff --git a/Python/pytime.c b/Python/pytime.c index a4963357df..0f30f428d8 100644 --- a/Python/pytime.c +++ b/Python/pytime.c @@ -119,12 +119,6 @@ _PyTime_gettimeofday(_PyTime_timeval *tp) } } -int -_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) { @@ -414,7 +408,7 @@ _PyTime_FromNanoseconds(PY_LONG_LONG ns) return t; } -#if !defined(MS_WINDOWS) && !defined(__APPLE__) +#ifdef HAVE_CLOCK_GETTIME static int _PyTime_FromTimespec(_PyTime_t *tp, struct timespec *ts) { @@ -430,6 +424,23 @@ _PyTime_FromTimespec(_PyTime_t *tp, struct timespec *ts) *tp = t; return 0; } +#else +static int +_PyTime_FromTimeval(_PyTime_t *tp, struct timeval *tv) +{ + _PyTime_t t; + + t = (_PyTime_t)tv->tv_sec * SEC_TO_NS; + if (t / SEC_TO_NS != tv->tv_sec) { + _PyTime_overflow(); + return -1; + } + + t += (_PyTime_t)tv->tv_usec * US_TO_NS; + + *tp = t; + return 0; +} #endif int @@ -561,6 +572,102 @@ _PyTime_AsTimeval(_PyTime_t t, struct timeval *tv, _PyTime_round_t round) return 0; } +static int +pygettimeofday_new(_PyTime_t *tp, _Py_clock_info_t *info, int raise) +{ +#ifdef MS_WINDOWS + FILETIME system_time; + ULARGE_INTEGER large; + + assert(info == NULL || raise); + + GetSystemTimeAsFileTime(&system_time); + large.u.LowPart = system_time.dwLowDateTime; + large.u.HighPart = system_time.dwHighDateTime; + /* 11,644,473,600,000,000,000: number of nanoseconds between + the 1st january 1601 and the 1st january 1970 (369 years + 89 leap + days). */ + *tp = large.QuadPart * 100 - 11644473600000000000; + if (info) { + DWORD timeAdjustment, timeIncrement; + BOOL isTimeAdjustmentDisabled, ok; + + info->implementation = "GetSystemTimeAsFileTime()"; + info->monotonic = 0; + ok = GetSystemTimeAdjustment(&timeAdjustment, &timeIncrement, + &isTimeAdjustmentDisabled); + if (!ok) { + PyErr_SetFromWindowsErr(0); + return -1; + } + info->resolution = timeIncrement * 1e-7; + info->adjustable = 1; + } + +#else /* MS_WINDOWS */ + int err; +#ifdef HAVE_CLOCK_GETTIME + struct timespec ts; +#else + struct timeval tv; +#endif + + assert(info == NULL || raise); + +#ifdef HAVE_CLOCK_GETTIME + err = clock_gettime(CLOCK_REALTIME, &ts); + if (err) { + if (raise) + PyErr_SetFromErrno(PyExc_OSError); + return -1; + } + if (_PyTime_FromTimespec(tp, &ts) < 0) + return -1; + + 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; + } +#else /* HAVE_CLOCK_GETTIME */ + + /* test gettimeofday() */ +#ifdef GETTIMEOFDAY_NO_TZ + err = gettimeofday(&tv); +#else + err = gettimeofday(&tv, (struct timezone *)NULL); +#endif + if (err) { + if (raise) + PyErr_SetFromErrno(PyExc_OSError); + return -1; + } + if (_PyTime_FromTimeval(tp, &tv) < 0) + return -1; + + if (info) { + info->implementation = "gettimeofday()"; + info->resolution = 1e-6; + info->monotonic = 0; + info->adjustable = 1; + } +#endif /* !HAVE_CLOCK_GETTIME */ +#endif /* !MS_WINDOWS */ + return 0; +} + +int +_PyTime_GetSystemClockWithInfo(_PyTime_t *t, _Py_clock_info_t *info) +{ + return pygettimeofday_new(t, info, 1); +} + + static int pymonotonic_new(_PyTime_t *tp, _Py_clock_info_t *info, int raise) { @@ -693,7 +800,11 @@ _PyTime_Init(void) _PyTime_t t; /* ensure that the system clock works */ - if (_PyTime_gettimeofday_info(&tv, NULL) < 0) + if (pygettimeofday(&tv, NULL, 1) < 0) + return -1; + + /* ensure that the system clock works */ + if (_PyTime_GetSystemClockWithInfo(&t, NULL) < 0) return -1; /* ensure that the operating system provides a monotonic clock */ @@ -701,7 +812,7 @@ _PyTime_Init(void) return -1; /* ensure that the operating system provides a monotonic clock */ - if (pymonotonic_new(&t, NULL, 1) < 0) + if (_PyTime_GetMonotonicClockWithInfo(&t, NULL) < 0) return -1; return 0; } -- cgit v1.2.1 From e920c51efa3cc13cacda6c7ebc34289c02753024 Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Fri, 27 Mar 2015 18:19:03 +0100 Subject: Issue #22117: The signal modules uses the new _PyTime_t API * Add _PyTime_AsTimespec() * Add unit tests for _PyTime_AsTimespec() --- Python/pytime.c | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) (limited to 'Python/pytime.c') diff --git a/Python/pytime.c b/Python/pytime.c index 0f30f428d8..bd94787241 100644 --- a/Python/pytime.c +++ b/Python/pytime.c @@ -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) { -- cgit v1.2.1 From 1392cf4c25fee3824c6d56fee0a37e863e52bba6 Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Sat, 28 Mar 2015 01:26:47 +0100 Subject: Issue #22117: Write unit tests for _PyTime_AsTimeval() * _PyTime_AsTimeval() now ensures that tv_usec is always positive * _PyTime_AsTimespec() now ensures that tv_nsec is always positive * _PyTime_AsTimeval() now returns an integer on overflow instead of raising an exception --- Python/pytime.c | 40 +++++++++++++++++++++++++++------------- 1 file changed, 27 insertions(+), 13 deletions(-) (limited to 'Python/pytime.c') diff --git a/Python/pytime.c b/Python/pytime.c index bd94787241..9893116465 100644 --- a/Python/pytime.c +++ b/Python/pytime.c @@ -540,9 +540,14 @@ int _PyTime_AsTimeval(_PyTime_t t, struct timeval *tv, _PyTime_round_t round) { _PyTime_t secs, ns; + int res = 0; secs = t / SEC_TO_NS; ns = t % SEC_TO_NS; + if (ns < 0) { + ns += SEC_TO_NS; + secs -= 1; + } #ifdef MS_WINDOWS /* On Windows, timeval.tv_sec is a long (32 bit), @@ -550,8 +555,12 @@ _PyTime_AsTimeval(_PyTime_t t, struct timeval *tv, _PyTime_round_t round) assert(sizeof(tv->tv_sec) == sizeof(long)); #if SIZEOF_TIME_T > SIZEOF_LONG if (secs > LONG_MAX) { - _PyTime_overflow(); - return -1; + secs = LONG_MAX; + res = -1; + } + else if (secs < LONG_MIN) { + secs = LONG_MIN; + res = -1; } #endif tv->tv_sec = (long)secs; @@ -559,32 +568,37 @@ _PyTime_AsTimeval(_PyTime_t t, struct timeval *tv, _PyTime_round_t round) /* On OpenBSD 5.4, timeval.tv_sec is a long. Example: long is 64-bit, whereas time_t is 32-bit. */ tv->tv_sec = secs; - if ((_PyTime_t)tv->tv_sec != secs) { - _PyTime_overflow(); - return -1; - } + if ((_PyTime_t)tv->tv_sec != secs) + res = -1; #endif - if (round == _PyTime_ROUND_UP) + if ((round == _PyTime_ROUND_UP) ^ (tv->tv_sec < 0)) tv->tv_usec = (int)((ns + US_TO_NS - 1) / US_TO_NS); else tv->tv_usec = (int)(ns / US_TO_NS); - return 0; + + if (tv->tv_usec >= SEC_TO_US) { + tv->tv_usec -= SEC_TO_US; + tv->tv_sec += 1; + } + + return res; } #ifdef HAVE_CLOCK_GETTIME int _PyTime_AsTimespec(_PyTime_t t, struct timespec *ts) { - _PyTime_t sec, nsec; - sec = t / SEC_TO_NS; + _PyTime_t secs, nsec; + + secs = t / SEC_TO_NS; nsec = t % SEC_TO_NS; if (nsec < 0) { nsec += SEC_TO_NS; - sec -= 1; + secs -= 1; } - ts->tv_sec = (time_t)sec; - if ((_PyTime_t)ts->tv_sec != sec) { + ts->tv_sec = (time_t)secs; + if ((_PyTime_t)ts->tv_sec != secs) { _PyTime_overflow(); return -1; } -- cgit v1.2.1 From a760a78975cb1670cad04e9a4ca5e62584758a03 Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Sat, 28 Mar 2015 03:52:05 +0100 Subject: Issue #22117: The thread module uses the new _PyTime_t timestamp API 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. --- Python/pytime.c | 132 +++----------------------------------------------------- 1 file changed, 6 insertions(+), 126 deletions(-) (limited to 'Python/pytime.c') diff --git a/Python/pytime.c b/Python/pytime.c index 9893116465..aa64977552 100644 --- a/Python/pytime.c +++ b/Python/pytime.c @@ -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; -- cgit v1.2.1 From 92ab17f9200c135ec9d951e734c02ed57a77d803 Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Sat, 28 Mar 2015 05:07:51 +0100 Subject: Issue #22117: Use the new _PyTime_t API in the select module --- Python/pytime.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'Python/pytime.c') diff --git a/Python/pytime.c b/Python/pytime.c index aa64977552..27004f338c 100644 --- a/Python/pytime.c +++ b/Python/pytime.c @@ -469,7 +469,7 @@ _PyTime_AsTimeval(_PyTime_t t, struct timeval *tv, _PyTime_round_t round) return res; } -#ifdef HAVE_CLOCK_GETTIME +#if defined(HAVE_CLOCK_GETTIME) || defined(HAVE_KQUEUE) int _PyTime_AsTimespec(_PyTime_t t, struct timespec *ts) { -- cgit v1.2.1 From 1d90b89bca58066016225d3ed721c167597a0ee5 Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Sat, 28 Mar 2015 04:09:41 +0100 Subject: Issue #22117: Use the _PyTime_t API for time.clock_settime() Remove also the now unused _PyTime_AddDouble() function. --- Python/pytime.c | 17 ----------------- 1 file changed, 17 deletions(-) (limited to 'Python/pytime.c') diff --git a/Python/pytime.c b/Python/pytime.c index 27004f338c..0f05db424d 100644 --- a/Python/pytime.c +++ b/Python/pytime.c @@ -251,23 +251,6 @@ _PyTime_ObjectToTimeval(PyObject *obj, time_t *sec, long *usec, return _PyTime_ObjectToDenominator(obj, sec, usec, 1e6, round); } -void -_PyTime_AddDouble(_PyTime_timeval *tv, double interval, _PyTime_round_t round) -{ - _PyTime_timeval tv2; - double frac; - - frac = fmod(interval, 1.0); - interval = floor(interval); - tv2.tv_sec = (long)interval; - tv2.tv_usec = (long)(frac*1e6); - - tv->tv_sec += tv2.tv_sec; - tv->tv_usec += tv2.tv_usec; - tv->tv_sec += (time_t)(tv->tv_usec / SEC_TO_US); - tv->tv_usec %= SEC_TO_US; -} - /****************** NEW _PyTime_t API **********************/ static void -- cgit v1.2.1 From 3a5ed718f1eb432c73a069291247f6297ed2d324 Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Sat, 28 Mar 2015 05:02:39 +0100 Subject: Issue #22117: Add the new _PyTime_ROUND_FLOOR rounding method for the datetime module. time.clock_settime() now uses this rounding method instead of _PyTime_ROUND_DOWN to handle correctly dates before 1970. --- Python/pytime.c | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) (limited to 'Python/pytime.c') diff --git a/Python/pytime.c b/Python/pytime.c index 0f05db424d..8c6771baf3 100644 --- a/Python/pytime.c +++ b/Python/pytime.c @@ -260,6 +260,14 @@ _PyTime_overflow(void) "timestamp too large to convert to C _PyTime_t"); } +int +_PyTime_RoundTowardsInfinity(int is_neg, _PyTime_round_t round) +{ + if (round == _PyTime_ROUND_FLOOR) + return 0; + return ((round == _PyTime_ROUND_UP) ^ is_neg); +} + _PyTime_t _PyTime_FromNanoseconds(PY_LONG_LONG ns) { @@ -314,7 +322,7 @@ _PyTime_FromSecondsObject(_PyTime_t *t, PyObject *obj, _PyTime_round_t round) d = PyFloat_AsDouble(obj); d *= 1e9; - if ((round == _PyTime_ROUND_UP) ^ (d < 0)) + if (_PyTime_RoundTowardsInfinity(d < 0, round)) d = ceil(d); else d = floor(d); @@ -380,7 +388,7 @@ _PyTime_Multiply(_PyTime_t t, unsigned int multiply, _PyTime_round_t round) _PyTime_t k; if (multiply < SEC_TO_NS) { k = SEC_TO_NS / multiply; - if (round == _PyTime_ROUND_UP) + if (_PyTime_RoundTowardsInfinity(t < 0, round)) return (t + k - 1) / k; else return t / k; @@ -397,6 +405,7 @@ _PyTime_AsMilliseconds(_PyTime_t t, _PyTime_round_t round) return _PyTime_Multiply(t, 1000, round); } +/* FIXME: write unit tests */ _PyTime_t _PyTime_AsMicroseconds(_PyTime_t t, _PyTime_round_t round) { @@ -439,7 +448,7 @@ _PyTime_AsTimeval(_PyTime_t t, struct timeval *tv, _PyTime_round_t round) res = -1; #endif - if ((round == _PyTime_ROUND_UP) ^ (tv->tv_sec < 0)) + if (_PyTime_RoundTowardsInfinity(tv->tv_sec < 0, round)) tv->tv_usec = (int)((ns + US_TO_NS - 1) / US_TO_NS); else tv->tv_usec = (int)(ns / US_TO_NS); -- cgit v1.2.1 From 5ce3bc5e57648b96c380f70424d6cbd889c6c136 Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Sat, 28 Mar 2015 05:24:19 +0100 Subject: Issue #22117: Fix _PyTime_GetMonotonicClock() and _PyTime_GetSystemClockWithInfo() to not raise an exception and return 0 on error (it should never occur) --- Python/pytime.c | 32 ++++++++++++++++++++------------ 1 file changed, 20 insertions(+), 12 deletions(-) (limited to 'Python/pytime.c') diff --git a/Python/pytime.c b/Python/pytime.c index 8c6771baf3..d23d9d36b9 100644 --- a/Python/pytime.c +++ b/Python/pytime.c @@ -279,36 +279,41 @@ _PyTime_FromNanoseconds(PY_LONG_LONG ns) #ifdef HAVE_CLOCK_GETTIME static int -_PyTime_FromTimespec(_PyTime_t *tp, struct timespec *ts) +_PyTime_FromTimespec(_PyTime_t *tp, struct timespec *ts, int raise) { _PyTime_t t; + int res = 0; + t = (_PyTime_t)ts->tv_sec * SEC_TO_NS; if (t / SEC_TO_NS != ts->tv_sec) { - _PyTime_overflow(); - return -1; + if (raise) + _PyTime_overflow(); + res = -1; } t += ts->tv_nsec; *tp = t; - return 0; + return res; } #else static int -_PyTime_FromTimeval(_PyTime_t *tp, struct timeval *tv) +_PyTime_FromTimeval(_PyTime_t *tp, struct timeval *tv, int raise) { _PyTime_t t; + int res = 0; t = (_PyTime_t)tv->tv_sec * SEC_TO_NS; if (t / SEC_TO_NS != tv->tv_sec) { - _PyTime_overflow(); - return -1; + if (raise) + _PyTime_overflow(); + res = -1; } t += (_PyTime_t)tv->tv_usec * US_TO_NS; *tp = t; - return 0; + return res; } #endif @@ -532,7 +537,7 @@ pygettimeofday_new(_PyTime_t *tp, _Py_clock_info_t *info, int raise) PyErr_SetFromErrno(PyExc_OSError); return -1; } - if (_PyTime_FromTimespec(tp, &ts) < 0) + if (_PyTime_FromTimespec(tp, &ts, raise) < 0) return -1; if (info) { @@ -558,7 +563,7 @@ pygettimeofday_new(_PyTime_t *tp, _Py_clock_info_t *info, int raise) PyErr_SetFromErrno(PyExc_OSError); return -1; } - if (_PyTime_FromTimeval(tp, &tv) < 0) + if (_PyTime_FromTimeval(tp, &tv, raise) < 0) return -1; if (info) { @@ -674,7 +679,7 @@ pymonotonic_new(_PyTime_t *tp, _Py_clock_info_t *info, int raise) } info->resolution = res.tv_sec + res.tv_nsec * 1e-9; } - if (_PyTime_FromTimespec(tp, &ts) < 0) + if (_PyTime_FromTimespec(tp, &ts, raise) < 0) return -1; #endif #ifdef Py_DEBUG @@ -691,8 +696,11 @@ _PyTime_GetMonotonicClock(void) { _PyTime_t t; if (pymonotonic_new(&t, NULL, 0) < 0) { - /* cannot happen, _PyTime_Init() checks that pymonotonic_new() works */ + /* should not happen, _PyTime_Init() checked that monotonic clock at + startup */ assert(0); + + /* use a fixed value instead of a random value from the stack */ t = 0; } return t; -- cgit v1.2.1 From ccf5983e6847a096b3a22191e2d2420cf50b4a30 Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Mon, 30 Mar 2015 00:09:18 +0200 Subject: Issue #22117: Use the _PyTime_t API in _datetime.datetime() constructor * Remove _PyTime_gettimeofday() * Add _PyTime_GetSystemClock() --- Python/pytime.c | 119 +++++++------------------------------------------------- 1 file changed, 14 insertions(+), 105 deletions(-) (limited to 'Python/pytime.c') diff --git a/Python/pytime.c b/Python/pytime.c index d23d9d36b9..11e3a627ed 100644 --- a/Python/pytime.c +++ b/Python/pytime.c @@ -19,106 +19,6 @@ #define MS_TO_NS (MS_TO_US * US_TO_NS) #define SEC_TO_NS (SEC_TO_MS * MS_TO_NS) -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; - /* 11,644,473,600,000,000: number of microseconds between - the 1st january 1601 and the 1st january 1970 (369 years + 89 leap - days). */ - microseconds = large.QuadPart / 10 - 11644473600000000; - tp->tv_sec = microseconds / SEC_TO_US; - tp->tv_usec = microseconds % SEC_TO_US; - if (info) { - DWORD timeAdjustment, timeIncrement; - BOOL isTimeAdjustmentDisabled, ok; - - info->implementation = "GetSystemTimeAsFileTime()"; - info->monotonic = 0; - ok = GetSystemTimeAdjustment(&timeAdjustment, &timeIncrement, - &isTimeAdjustmentDisabled); - if (!ok) { - PyErr_SetFromWindowsErr(0); - return -1; - } - info->resolution = timeIncrement * 1e-7; - info->adjustable = 1; - } - -#else /* MS_WINDOWS */ - int err; -#ifdef HAVE_CLOCK_GETTIME - struct timespec ts; -#endif - - assert(info == NULL || raise); - -#ifdef HAVE_CLOCK_GETTIME - err = clock_gettime(CLOCK_REALTIME, &ts); - if (err) { - if (raise) - PyErr_SetFromErrno(PyExc_OSError); - return -1; - } - tp->tv_sec = ts.tv_sec; - tp->tv_usec = ts.tv_nsec / US_TO_NS; - - 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; - } -#else /* HAVE_CLOCK_GETTIME */ - - /* test gettimeofday() */ -#ifdef GETTIMEOFDAY_NO_TZ - err = gettimeofday(tp); -#else - err = gettimeofday(tp, (struct timezone *)NULL); -#endif - if (err) { - if (raise) - PyErr_SetFromErrno(PyExc_OSError); - return -1; - } - - if (info) { - info->implementation = "gettimeofday()"; - info->resolution = 1e-6; - info->monotonic = 0; - info->adjustable = 1; - } -#endif /* !HAVE_CLOCK_GETTIME */ -#endif /* !MS_WINDOWS */ - assert(0 <= tp->tv_usec && tp->tv_usec < SEC_TO_US); - return 0; -} - -void -_PyTime_gettimeofday(_PyTime_timeval *tp) -{ - if (pygettimeofday(tp, NULL, 0) < 0) { - /* cannot happen, _PyTime_Init() checks that pygettimeofday() works */ - assert(0); - tp->tv_sec = 0; - tp->tv_usec = 0; - } -} - static void error_time_t_overflow(void) { @@ -577,6 +477,20 @@ pygettimeofday_new(_PyTime_t *tp, _Py_clock_info_t *info, int raise) return 0; } +_PyTime_t +_PyTime_GetSystemClock(void) +{ + _PyTime_t t; + if (pygettimeofday_new(&t, NULL, 0) < 0) { + /* should not happen, _PyTime_Init() checked the clock at startup */ + assert(0); + + /* use a fixed value instead of a random value from the stack */ + t = 0; + } + return t; +} + int _PyTime_GetSystemClockWithInfo(_PyTime_t *t, _Py_clock_info_t *info) { @@ -715,13 +629,8 @@ _PyTime_GetMonotonicClockWithInfo(_PyTime_t *tp, _Py_clock_info_t *info) int _PyTime_Init(void) { - _PyTime_timeval tv; _PyTime_t t; - /* ensure that the system clock works */ - if (pygettimeofday(&tv, NULL, 1) < 0) - return -1; - /* ensure that the system clock works */ if (_PyTime_GetSystemClockWithInfo(&t, NULL) < 0) return -1; -- cgit v1.2.1 From a11414afa6772939df5c9e91c58f070022ed25be Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Mon, 30 Mar 2015 00:25:38 +0200 Subject: Issue #22117: Cleanup pytime.c/.h --- Python/pytime.c | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) (limited to 'Python/pytime.c') diff --git a/Python/pytime.c b/Python/pytime.c index 11e3a627ed..d9ff3c6310 100644 --- a/Python/pytime.c +++ b/Python/pytime.c @@ -151,8 +151,6 @@ _PyTime_ObjectToTimeval(PyObject *obj, time_t *sec, long *usec, return _PyTime_ObjectToDenominator(obj, sec, usec, 1e6, round); } -/****************** NEW _PyTime_t API **********************/ - static void _PyTime_overflow(void) { @@ -161,7 +159,7 @@ _PyTime_overflow(void) } int -_PyTime_RoundTowardsInfinity(int is_neg, _PyTime_round_t round) +_PyTime_RoundTowardsPosInf(int is_neg, _PyTime_round_t round) { if (round == _PyTime_ROUND_FLOOR) return 0; @@ -196,7 +194,7 @@ _PyTime_FromTimespec(_PyTime_t *tp, struct timespec *ts, int raise) *tp = t; return res; } -#else +#elif !defined(MS_WINDOWS) static int _PyTime_FromTimeval(_PyTime_t *tp, struct timeval *tv, int raise) { @@ -227,7 +225,7 @@ _PyTime_FromSecondsObject(_PyTime_t *t, PyObject *obj, _PyTime_round_t round) d = PyFloat_AsDouble(obj); d *= 1e9; - if (_PyTime_RoundTowardsInfinity(d < 0, round)) + if (_PyTime_RoundTowardsPosInf(d < 0, round)) d = ceil(d); else d = floor(d); @@ -293,7 +291,7 @@ _PyTime_Multiply(_PyTime_t t, unsigned int multiply, _PyTime_round_t round) _PyTime_t k; if (multiply < SEC_TO_NS) { k = SEC_TO_NS / multiply; - if (_PyTime_RoundTowardsInfinity(t < 0, round)) + if (_PyTime_RoundTowardsPosInf(t < 0, round)) return (t + k - 1) / k; else return t / k; @@ -353,7 +351,7 @@ _PyTime_AsTimeval(_PyTime_t t, struct timeval *tv, _PyTime_round_t round) res = -1; #endif - if (_PyTime_RoundTowardsInfinity(tv->tv_sec < 0, round)) + if (_PyTime_RoundTowardsPosInf(tv->tv_sec < 0, round)) tv->tv_usec = (int)((ns + US_TO_NS - 1) / US_TO_NS); else tv->tv_usec = (int)(ns / US_TO_NS); -- cgit v1.2.1 From 6c7b1fc664a585dc13cd8f7a1d32f2c75f1d9d41 Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Mon, 30 Mar 2015 00:44:06 +0200 Subject: Issue #22117: Fix rounding and implement _PyTime_ROUND_FLOOR in: - _PyTime_ObjectToTime_t() - _PyTime_ObjectToTimespec() - _PyTime_ObjectToTimeval() --- Python/pytime.c | 44 ++++++++++++++++++++------------------------ 1 file changed, 20 insertions(+), 24 deletions(-) (limited to 'Python/pytime.c') diff --git a/Python/pytime.c b/Python/pytime.c index d9ff3c6310..2bf6ba5cf2 100644 --- a/Python/pytime.c +++ b/Python/pytime.c @@ -26,6 +26,14 @@ error_time_t_overflow(void) "timestamp out of range for platform time_t"); } +static int +_PyTime_RoundTowardsPosInf(int is_neg, _PyTime_round_t round) +{ + if (round == _PyTime_ROUND_FLOOR) + return 0; + return ((round == _PyTime_ROUND_UP) ^ is_neg); +} + time_t _PyLong_AsTime_t(PyObject *obj) { @@ -74,18 +82,16 @@ _PyTime_ObjectToDenominator(PyObject *obj, time_t *sec, long *numerator, } floatpart *= denominator; - if (round == _PyTime_ROUND_UP) { - if (intpart >= 0) { - floatpart = ceil(floatpart); - if (floatpart >= denominator) { - floatpart = 0.0; - intpart += 1.0; - } - } - else { - floatpart = floor(floatpart); + if (_PyTime_RoundTowardsPosInf(intpart < 0, round)) { + floatpart = ceil(floatpart); + if (floatpart >= denominator) { + floatpart = 0.0; + intpart += 1.0; } } + else { + floatpart = floor(floatpart); + } *sec = (time_t)intpart; err = intpart - (double)*sec; @@ -113,12 +119,10 @@ _PyTime_ObjectToTime_t(PyObject *obj, time_t *sec, _PyTime_round_t round) double d, intpart, err; d = PyFloat_AsDouble(obj); - if (round == _PyTime_ROUND_UP) { - if (d >= 0) - d = ceil(d); - else - d = floor(d); - } + if (_PyTime_RoundTowardsPosInf(d < 0, round)) + d = ceil(d); + else + d = floor(d); (void)modf(d, &intpart); *sec = (time_t)intpart; @@ -158,14 +162,6 @@ _PyTime_overflow(void) "timestamp too large to convert to C _PyTime_t"); } -int -_PyTime_RoundTowardsPosInf(int is_neg, _PyTime_round_t round) -{ - if (round == _PyTime_ROUND_FLOOR) - return 0; - return ((round == _PyTime_ROUND_UP) ^ is_neg); -} - _PyTime_t _PyTime_FromNanoseconds(PY_LONG_LONG ns) { -- cgit v1.2.1 From e86873f59ff36f88f82bc885cf7bc052dee22a15 Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Mon, 30 Mar 2015 02:51:13 +0200 Subject: Issue #22117: Fix usage of _PyTime_AsTimeval() Add _PyTime_AsTimeval_noraise() function. Call it when it's not possible (or not useful) to raise a Python exception on overflow. --- Python/pytime.c | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) (limited to 'Python/pytime.c') diff --git a/Python/pytime.c b/Python/pytime.c index 2bf6ba5cf2..a7eda869a0 100644 --- a/Python/pytime.c +++ b/Python/pytime.c @@ -311,8 +311,9 @@ _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) +static int +_PyTime_AsTimeval_impl(_PyTime_t t, struct timeval *tv, _PyTime_round_t round, + int raise) { _PyTime_t secs, ns; int res = 0; @@ -357,9 +358,23 @@ _PyTime_AsTimeval(_PyTime_t t, struct timeval *tv, _PyTime_round_t round) tv->tv_sec += 1; } + if (res && raise) + _PyTime_overflow(); return res; } +int +_PyTime_AsTimeval(_PyTime_t t, struct timeval *tv, _PyTime_round_t round) +{ + return _PyTime_AsTimeval_impl(t, tv, round, 1); +} + +int +_PyTime_AsTimeval_noraise(_PyTime_t t, struct timeval *tv, _PyTime_round_t round) +{ + return _PyTime_AsTimeval_impl(t, tv, round, 0); +} + #if defined(HAVE_CLOCK_GETTIME) || defined(HAVE_KQUEUE) int _PyTime_AsTimespec(_PyTime_t t, struct timespec *ts) -- cgit v1.2.1 From 86cb6fb8629db8e8d2de9452150dbb50987399ee Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Mon, 30 Mar 2015 02:54:57 +0200 Subject: Issue #22117: Add assertions to _PyTime_AsTimeval() and _PyTime_AsTimespec() to check that microseconds and nanoseconds fits into the specified range. --- Python/pytime.c | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'Python/pytime.c') diff --git a/Python/pytime.c b/Python/pytime.c index a7eda869a0..0d28911c37 100644 --- a/Python/pytime.c +++ b/Python/pytime.c @@ -360,6 +360,8 @@ _PyTime_AsTimeval_impl(_PyTime_t t, struct timeval *tv, _PyTime_round_t round, if (res && raise) _PyTime_overflow(); + + assert(0 <= tv->tv_usec && tv->tv_usec <= 999999); return res; } @@ -393,6 +395,8 @@ _PyTime_AsTimespec(_PyTime_t t, struct timespec *ts) return -1; } ts->tv_nsec = nsec; + + assert(0 <= ts->tv_nsec && ts->tv_nsec <= 999999999); return 0; } #endif -- cgit v1.2.1 From 1e66db2443e7a814d39e2f2f7c252c310fb736cf Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Mon, 30 Mar 2015 03:52:49 +0200 Subject: Issue #22117: Add _PyTime_ROUND_CEILING rounding method for timestamps Add also more tests for ROUNd_FLOOR. --- Python/pytime.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'Python/pytime.c') diff --git a/Python/pytime.c b/Python/pytime.c index 0d28911c37..ca4386aa08 100644 --- a/Python/pytime.c +++ b/Python/pytime.c @@ -31,6 +31,8 @@ _PyTime_RoundTowardsPosInf(int is_neg, _PyTime_round_t round) { if (round == _PyTime_ROUND_FLOOR) return 0; + if (round == _PyTime_ROUND_CEILING) + return 1; return ((round == _PyTime_ROUND_UP) ^ is_neg); } -- cgit v1.2.1 From edb319537899ddaa0d482ec55597784fb0f775a5 Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Mon, 30 Mar 2015 03:57:14 +0200 Subject: Issue #22117: Remove _PyTime_ROUND_DOWN and _PyTime_ROUND_UP rounding methods Use _PyTime_ROUND_FLOOR and _PyTime_ROUND_CEILING instead. --- Python/pytime.c | 20 +++++--------------- 1 file changed, 5 insertions(+), 15 deletions(-) (limited to 'Python/pytime.c') diff --git a/Python/pytime.c b/Python/pytime.c index ca4386aa08..98f29acc38 100644 --- a/Python/pytime.c +++ b/Python/pytime.c @@ -26,16 +26,6 @@ error_time_t_overflow(void) "timestamp out of range for platform time_t"); } -static int -_PyTime_RoundTowardsPosInf(int is_neg, _PyTime_round_t round) -{ - if (round == _PyTime_ROUND_FLOOR) - return 0; - if (round == _PyTime_ROUND_CEILING) - return 1; - return ((round == _PyTime_ROUND_UP) ^ is_neg); -} - time_t _PyLong_AsTime_t(PyObject *obj) { @@ -84,7 +74,7 @@ _PyTime_ObjectToDenominator(PyObject *obj, time_t *sec, long *numerator, } floatpart *= denominator; - if (_PyTime_RoundTowardsPosInf(intpart < 0, round)) { + if (round == _PyTime_ROUND_CEILING) { floatpart = ceil(floatpart); if (floatpart >= denominator) { floatpart = 0.0; @@ -121,7 +111,7 @@ _PyTime_ObjectToTime_t(PyObject *obj, time_t *sec, _PyTime_round_t round) double d, intpart, err; d = PyFloat_AsDouble(obj); - if (_PyTime_RoundTowardsPosInf(d < 0, round)) + if (round == _PyTime_ROUND_CEILING) d = ceil(d); else d = floor(d); @@ -223,7 +213,7 @@ _PyTime_FromSecondsObject(_PyTime_t *t, PyObject *obj, _PyTime_round_t round) d = PyFloat_AsDouble(obj); d *= 1e9; - if (_PyTime_RoundTowardsPosInf(d < 0, round)) + if (round == _PyTime_ROUND_CEILING) d = ceil(d); else d = floor(d); @@ -289,7 +279,7 @@ _PyTime_Multiply(_PyTime_t t, unsigned int multiply, _PyTime_round_t round) _PyTime_t k; if (multiply < SEC_TO_NS) { k = SEC_TO_NS / multiply; - if (_PyTime_RoundTowardsPosInf(t < 0, round)) + if (round == _PyTime_ROUND_CEILING) return (t + k - 1) / k; else return t / k; @@ -350,7 +340,7 @@ _PyTime_AsTimeval_impl(_PyTime_t t, struct timeval *tv, _PyTime_round_t round, res = -1; #endif - if (_PyTime_RoundTowardsPosInf(tv->tv_sec < 0, round)) + if (round == _PyTime_ROUND_CEILING) tv->tv_usec = (int)((ns + US_TO_NS - 1) / US_TO_NS); else tv->tv_usec = (int)(ns / US_TO_NS); -- cgit v1.2.1 From 3a56f42aa63b0d72187825320cc5ad0bd4d22b9d Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Mon, 30 Mar 2015 10:22:16 +0200 Subject: Issue #22117: Try to fix rounding in conversion from Python double to _PyTime_t using the C volatile keyword. --- Python/pytime.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'Python/pytime.c') diff --git a/Python/pytime.c b/Python/pytime.c index 98f29acc38..5bf8c568e7 100644 --- a/Python/pytime.c +++ b/Python/pytime.c @@ -207,7 +207,8 @@ int _PyTime_FromSecondsObject(_PyTime_t *t, PyObject *obj, _PyTime_round_t round) { if (PyFloat_Check(obj)) { - double d, err; + /* volatile avoids unsafe optimization on float enabled by gcc -O3 */ + volatile double d, err; /* convert to a number of nanoseconds */ d = PyFloat_AsDouble(obj); -- cgit v1.2.1 From aae53feac52c27e01df5fdbd7dd4214d8bf96467 Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Mon, 30 Mar 2015 21:36:10 +0200 Subject: Issue #23485: Add _PyTime_FromMillisecondsObject() function --- Python/pytime.c | 23 ++++++++++++++++++----- 1 file changed, 18 insertions(+), 5 deletions(-) (limited to 'Python/pytime.c') diff --git a/Python/pytime.c b/Python/pytime.c index 5bf8c568e7..003003bdca 100644 --- a/Python/pytime.c +++ b/Python/pytime.c @@ -203,8 +203,9 @@ _PyTime_FromTimeval(_PyTime_t *tp, struct timeval *tv, int raise) } #endif -int -_PyTime_FromSecondsObject(_PyTime_t *t, PyObject *obj, _PyTime_round_t round) +static int +_PyTime_FromObject(_PyTime_t *t, PyObject *obj, _PyTime_round_t round, + long to_nanoseconds) { if (PyFloat_Check(obj)) { /* volatile avoids unsafe optimization on float enabled by gcc -O3 */ @@ -212,7 +213,7 @@ _PyTime_FromSecondsObject(_PyTime_t *t, PyObject *obj, _PyTime_round_t round) /* convert to a number of nanoseconds */ d = PyFloat_AsDouble(obj); - d *= 1e9; + d *= to_nanoseconds; if (round == _PyTime_ROUND_CEILING) d = ceil(d); @@ -242,8 +243,8 @@ _PyTime_FromSecondsObject(_PyTime_t *t, PyObject *obj, _PyTime_round_t round) _PyTime_overflow(); return -1; } - *t = sec * SEC_TO_NS; - if (*t / SEC_TO_NS != sec) { + *t = sec * to_nanoseconds; + if (*t / to_nanoseconds != sec) { _PyTime_overflow(); return -1; } @@ -251,6 +252,18 @@ _PyTime_FromSecondsObject(_PyTime_t *t, PyObject *obj, _PyTime_round_t round) } } +int +_PyTime_FromSecondsObject(_PyTime_t *t, PyObject *obj, _PyTime_round_t round) +{ + return _PyTime_FromObject(t, obj, round, SEC_TO_NS); +} + +int +_PyTime_FromMillisecondsObject(_PyTime_t *t, PyObject *obj, _PyTime_round_t round) +{ + return _PyTime_FromObject(t, obj, round, MS_TO_NS); +} + double _PyTime_AsSecondsDouble(_PyTime_t t) { -- cgit v1.2.1 From 61bf4cde8b029fb59ff4bc209c0eb413ed0cc702 Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Wed, 1 Apr 2015 17:47:07 +0200 Subject: Issue #22117, issue #23485: Fix _PyTime_AsMilliseconds() and _PyTime_AsMicroseconds() rounding. Add also unit tests. --- Python/pytime.c | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) (limited to 'Python/pytime.c') diff --git a/Python/pytime.c b/Python/pytime.c index 003003bdca..491bbea611 100644 --- a/Python/pytime.c +++ b/Python/pytime.c @@ -19,6 +19,10 @@ #define MS_TO_NS (MS_TO_US * US_TO_NS) #define SEC_TO_NS (SEC_TO_MS * MS_TO_NS) +/* Conversion from nanoseconds */ +#define NS_TO_MS (1000 * 1000) +#define NS_TO_US (1000) + static void error_time_t_overflow(void) { @@ -288,33 +292,29 @@ _PyTime_AsNanosecondsObject(_PyTime_t t) } static _PyTime_t -_PyTime_Multiply(_PyTime_t t, unsigned int multiply, _PyTime_round_t round) +_PyTime_Divide(_PyTime_t t, _PyTime_t k, _PyTime_round_t round) { - _PyTime_t k; - if (multiply < SEC_TO_NS) { - k = SEC_TO_NS / multiply; - if (round == _PyTime_ROUND_CEILING) + assert(k > 1); + if (round == _PyTime_ROUND_CEILING) { + if (t >= 0) return (t + k - 1) / k; else - return t / k; - } - else { - k = multiply / SEC_TO_NS; - return t * k; + return (t - (k - 1)) / k; } + else + return t / k; } _PyTime_t _PyTime_AsMilliseconds(_PyTime_t t, _PyTime_round_t round) { - return _PyTime_Multiply(t, 1000, round); + return _PyTime_Divide(t, NS_TO_MS, round); } -/* FIXME: write unit tests */ _PyTime_t _PyTime_AsMicroseconds(_PyTime_t t, _PyTime_round_t round) { - return _PyTime_Multiply(t, 1000 * 1000, round); + return _PyTime_Divide(t, NS_TO_US, round); } static int -- cgit v1.2.1 From e5a1ccdc5648669a45fd37781f99b022bdd1dd52 Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Fri, 3 Apr 2015 13:10:54 +0200 Subject: Issue #22117: Add a new _PyTime_FromSeconds() function Fix also _Py_InitializeEx_Private(): initialize time before initializing import, import_init() uses the _PyTime API (for thread locks). --- Python/pytime.c | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) (limited to 'Python/pytime.c') diff --git a/Python/pytime.c b/Python/pytime.c index 491bbea611..02a93749c7 100644 --- a/Python/pytime.c +++ b/Python/pytime.c @@ -158,6 +158,19 @@ _PyTime_overflow(void) "timestamp too large to convert to C _PyTime_t"); } +_PyTime_t +_PyTime_FromSeconds(int seconds) +{ + _PyTime_t t; + /* ensure that integer overflow cannot happen, int type should have 32 + bits, whereas _PyTime_t type has at least 64 bits (SEC_TO_MS takes 30 + bits). */ + assert((seconds >= 0 && seconds <= _PyTime_MAX / SEC_TO_NS) + || (seconds < 0 && seconds >= _PyTime_MIN / SEC_TO_NS)); + t = (_PyTime_t)seconds * SEC_TO_NS; + return t; +} + _PyTime_t _PyTime_FromNanoseconds(PY_LONG_LONG ns) { @@ -657,5 +670,9 @@ _PyTime_Init(void) /* ensure that the operating system provides a monotonic clock */ if (_PyTime_GetMonotonicClockWithInfo(&t, NULL) < 0) return -1; + + /* check that _PyTime_FromSeconds() cannot overflow */ + assert(INT_MAX <= _PyTime_MAX / SEC_TO_NS); + assert(INT_MIN >= _PyTime_MIN / SEC_TO_NS); return 0; } -- cgit v1.2.1 From 722ad3f60d8cb6fb148712c45b504e516694f118 Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Thu, 3 Sep 2015 00:13:46 +0200 Subject: Issue #24707: Remove assertion in monotonic clock Don't check anymore at runtime that the monotonic clock doesn't go backward. Yes, it happens. It occurs sometimes each month on a Debian buildbot slave running in a VM. The problem is that Python cannot do anything useful if a monotonic clock goes backward. It was decided in the PEP 418 to not fix the system, but only expose the clock provided by the OS. --- Python/pytime.c | 10 ---------- 1 file changed, 10 deletions(-) (limited to 'Python/pytime.c') diff --git a/Python/pytime.c b/Python/pytime.c index 02a93749c7..e46bd42f54 100644 --- a/Python/pytime.c +++ b/Python/pytime.c @@ -533,10 +533,6 @@ _PyTime_GetSystemClockWithInfo(_PyTime_t *t, _Py_clock_info_t *info) static int pymonotonic_new(_PyTime_t *tp, _Py_clock_info_t *info, int raise) { -#ifdef Py_DEBUG - static int last_set = 0; - static _PyTime_t last = 0; -#endif #if defined(MS_WINDOWS) ULONGLONG result; @@ -627,12 +623,6 @@ pymonotonic_new(_PyTime_t *tp, _Py_clock_info_t *info, int raise) } if (_PyTime_FromTimespec(tp, &ts, raise) < 0) return -1; -#endif -#ifdef Py_DEBUG - /* monotonic clock cannot go backward */ - assert(!last_set || last <= *tp); - last = *tp; - last_set = 1; #endif return 0; } -- cgit v1.2.1 From fa33a69ca6184c64146a6845a187eb400d5fbbe1 Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Thu, 3 Sep 2015 00:14:58 +0200 Subject: oops, rename pymonotonic_new() to pymonotonic() I was not supposed to commit the function with the name pymonotonic_new(). I forgot to rename it. --- Python/pytime.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'Python/pytime.c') diff --git a/Python/pytime.c b/Python/pytime.c index e46bd42f54..77db204489 100644 --- a/Python/pytime.c +++ b/Python/pytime.c @@ -531,7 +531,7 @@ _PyTime_GetSystemClockWithInfo(_PyTime_t *t, _Py_clock_info_t *info) static int -pymonotonic_new(_PyTime_t *tp, _Py_clock_info_t *info, int raise) +pymonotonic(_PyTime_t *tp, _Py_clock_info_t *info, int raise) { #if defined(MS_WINDOWS) ULONGLONG result; @@ -631,7 +631,7 @@ _PyTime_t _PyTime_GetMonotonicClock(void) { _PyTime_t t; - if (pymonotonic_new(&t, NULL, 0) < 0) { + if (pymonotonic(&t, NULL, 0) < 0) { /* should not happen, _PyTime_Init() checked that monotonic clock at startup */ assert(0); @@ -645,7 +645,7 @@ _PyTime_GetMonotonicClock(void) int _PyTime_GetMonotonicClockWithInfo(_PyTime_t *tp, _Py_clock_info_t *info) { - return pymonotonic_new(tp, info, 1); + return pymonotonic(tp, info, 1); } int -- cgit v1.2.1 From 7443c71f7f90ed4e84b0878c748b9442861f6d2e Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Fri, 18 Sep 2015 13:36:17 +0200 Subject: Issue #25155: Add _PyTime_AsTimevalTime_t() function On Windows, the tv_sec field of the timeval structure has the type C long, whereas it has the type C time_t on all other platforms. A C long has a size of 32 bits (signed inter, 1 bit for the sign, 31 bits for the value) which is not enough to store an Epoch timestamp after the year 2038. Add the _PyTime_AsTimevalTime_t() function written for datetime.datetime.now(): convert a _PyTime_t timestamp to a (secs, us) tuple where secs type is time_t. It allows to support dates after the year 2038 on Windows. Enhance also _PyTime_AsTimeval_impl() to detect overflow on the number of seconds when rounding the number of microseconds. --- Python/pytime.c | 99 +++++++++++++++++++++++++++++++++++---------------------- 1 file changed, 61 insertions(+), 38 deletions(-) (limited to 'Python/pytime.c') diff --git a/Python/pytime.c b/Python/pytime.c index 77db204489..85e1ca8388 100644 --- a/Python/pytime.c +++ b/Python/pytime.c @@ -331,69 +331,92 @@ _PyTime_AsMicroseconds(_PyTime_t t, _PyTime_round_t round) } static int -_PyTime_AsTimeval_impl(_PyTime_t t, struct timeval *tv, _PyTime_round_t round, - int raise) +_PyTime_AsTimeval_impl(_PyTime_t t, _PyTime_t *p_secs, int *p_us, + _PyTime_round_t round) { _PyTime_t secs, ns; + int usec; int res = 0; secs = t / SEC_TO_NS; ns = t % SEC_TO_NS; - if (ns < 0) { - ns += SEC_TO_NS; - secs -= 1; - } -#ifdef MS_WINDOWS - /* On Windows, timeval.tv_sec is a long (32 bit), - whereas time_t can be 64-bit. */ - assert(sizeof(tv->tv_sec) == sizeof(long)); -#if SIZEOF_TIME_T > SIZEOF_LONG - if (secs > LONG_MAX) { - secs = LONG_MAX; - res = -1; + usec = (int)_PyTime_Divide(ns, US_TO_NS, round); + if (usec < 0) { + usec += SEC_TO_US; + if (secs != _PyTime_MIN) + secs -= 1; + else + res = -1; } - else if (secs < LONG_MIN) { - secs = LONG_MIN; - res = -1; + else if (usec >= SEC_TO_US) { + usec -= SEC_TO_US; + if (secs != _PyTime_MAX) + secs += 1; + else + res = -1; } -#endif + assert(0 <= usec && usec < SEC_TO_US); + + *p_secs = secs; + *p_us = usec; + + return res; +} + +static int +_PyTime_AsTimevalStruct_impl(_PyTime_t t, struct timeval *tv, + _PyTime_round_t round, int raise) +{ + _PyTime_t secs; + int us; + int res; + + res = _PyTime_AsTimeval_impl(t, &secs, &us, round); + +#ifdef MS_WINDOWS tv->tv_sec = (long)secs; #else - /* On OpenBSD 5.4, timeval.tv_sec is a long. - Example: long is 64-bit, whereas time_t is 32-bit. */ tv->tv_sec = secs; - if ((_PyTime_t)tv->tv_sec != secs) - res = -1; #endif + tv->tv_usec = us; - if (round == _PyTime_ROUND_CEILING) - tv->tv_usec = (int)((ns + US_TO_NS - 1) / US_TO_NS); - else - tv->tv_usec = (int)(ns / US_TO_NS); - - if (tv->tv_usec >= SEC_TO_US) { - tv->tv_usec -= SEC_TO_US; - tv->tv_sec += 1; + if (res < 0 || (_PyTime_t)tv->tv_sec != secs) { + if (raise) + error_time_t_overflow(); + return -1; } - - if (res && raise) - _PyTime_overflow(); - - assert(0 <= tv->tv_usec && tv->tv_usec <= 999999); - return res; + return 0; } int _PyTime_AsTimeval(_PyTime_t t, struct timeval *tv, _PyTime_round_t round) { - return _PyTime_AsTimeval_impl(t, tv, round, 1); + return _PyTime_AsTimevalStruct_impl(t, tv, round, 1); } int _PyTime_AsTimeval_noraise(_PyTime_t t, struct timeval *tv, _PyTime_round_t round) { - return _PyTime_AsTimeval_impl(t, tv, round, 0); + return _PyTime_AsTimevalStruct_impl(t, tv, round, 0); +} + +int +_PyTime_AsTimevalTime_t(_PyTime_t t, time_t *p_secs, int *us, + _PyTime_round_t round) +{ + _PyTime_t secs; + int res; + + res = _PyTime_AsTimeval_impl(t, &secs, us, round); + + *p_secs = secs; + + if (res < 0 || (_PyTime_t)*p_secs != secs) { + error_time_t_overflow(); + return -1; + } + return 0; } #if defined(HAVE_CLOCK_GETTIME) || defined(HAVE_KQUEUE) -- cgit v1.2.1 From ccbf7d3cf04df239c0d1dd5e15ff69dfbad6ab17 Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Fri, 18 Sep 2015 14:21:14 +0200 Subject: Issue #25155: Fix _PyTime_Divide() rounding _PyTime_Divide() rounding was wrong: copy code from Python default which has now much better unit tests. --- Python/pytime.c | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) (limited to 'Python/pytime.c') diff --git a/Python/pytime.c b/Python/pytime.c index 85e1ca8388..5a5cdd9c7a 100644 --- a/Python/pytime.c +++ b/Python/pytime.c @@ -305,17 +305,22 @@ _PyTime_AsNanosecondsObject(_PyTime_t t) } static _PyTime_t -_PyTime_Divide(_PyTime_t t, _PyTime_t k, _PyTime_round_t round) +_PyTime_Divide(const _PyTime_t t, const _PyTime_t k, + const _PyTime_round_t round) { assert(k > 1); if (round == _PyTime_ROUND_CEILING) { if (t >= 0) return (t + k - 1) / k; + else + return t / k; + } + else { + if (t >= 0) + return t / k; else return (t - (k - 1)) / k; } - else - return t / k; } _PyTime_t -- cgit v1.2.1 From c4dbbae41992919ac51152526f749484e333099b Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Tue, 10 Nov 2015 12:11:39 +0100 Subject: pytime.c: rename pygettimeofday_new() to pygettimeofday() I forgot to rename it in my previous refactoring of pytime.c. --- Python/pytime.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'Python/pytime.c') diff --git a/Python/pytime.c b/Python/pytime.c index 5a5cdd9c7a..7f65824b47 100644 --- a/Python/pytime.c +++ b/Python/pytime.c @@ -449,7 +449,7 @@ _PyTime_AsTimespec(_PyTime_t t, struct timespec *ts) #endif static int -pygettimeofday_new(_PyTime_t *tp, _Py_clock_info_t *info, int raise) +pygettimeofday(_PyTime_t *tp, _Py_clock_info_t *info, int raise) { #ifdef MS_WINDOWS FILETIME system_time; @@ -541,7 +541,7 @@ _PyTime_t _PyTime_GetSystemClock(void) { _PyTime_t t; - if (pygettimeofday_new(&t, NULL, 0) < 0) { + if (pygettimeofday(&t, NULL, 0) < 0) { /* should not happen, _PyTime_Init() checked the clock at startup */ assert(0); @@ -554,7 +554,7 @@ _PyTime_GetSystemClock(void) int _PyTime_GetSystemClockWithInfo(_PyTime_t *t, _Py_clock_info_t *info) { - return pygettimeofday_new(t, info, 1); + return pygettimeofday(t, info, 1); } -- cgit v1.2.1