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 90e8e06d349074dc58e81a15355b4756285c8182 Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Wed, 2 Sep 2015 00:49:16 +0200 Subject: Refactor pytime.c Move code to convert double timestamp to subfunctions. --- Python/pytime.c | 113 ++++++++++++++++++++++++++++++++------------------------ 1 file changed, 65 insertions(+), 48 deletions(-) (limited to 'Python/pytime.c') diff --git a/Python/pytime.c b/Python/pytime.c index 02a93749c7..17ef197225 100644 --- a/Python/pytime.c +++ b/Python/pytime.c @@ -61,43 +61,51 @@ _PyLong_FromTime_t(time_t t) } static int -_PyTime_ObjectToDenominator(PyObject *obj, time_t *sec, long *numerator, +_PyTime_DoubleToDenominator(double d, time_t *sec, long *numerator, double denominator, _PyTime_round_t round) { - assert(denominator <= LONG_MAX); - if (PyFloat_Check(obj)) { - double d, intpart, err; - /* volatile avoids unsafe optimization on float enabled by gcc -O3 */ - volatile double floatpart; + double intpart, err; + /* volatile avoids unsafe optimization on float enabled by gcc -O3 */ + volatile double floatpart; + + floatpart = modf(d, &intpart); + if (floatpart < 0) { + floatpart = 1.0 + floatpart; + intpart -= 1.0; + } - d = PyFloat_AsDouble(obj); - floatpart = modf(d, &intpart); - if (floatpart < 0) { - floatpart = 1.0 + floatpart; - intpart -= 1.0; + floatpart *= denominator; + if (round == _PyTime_ROUND_CEILING) { + floatpart = ceil(floatpart); + if (floatpart >= denominator) { + floatpart = 0.0; + intpart += 1.0; } + } + else { + floatpart = floor(floatpart); + } - floatpart *= denominator; - if (round == _PyTime_ROUND_CEILING) { - floatpart = ceil(floatpart); - if (floatpart >= denominator) { - floatpart = 0.0; - intpart += 1.0; - } - } - else { - floatpart = floor(floatpart); - } + *sec = (time_t)intpart; + err = intpart - (double)*sec; + if (err <= -1.0 || err >= 1.0) { + error_time_t_overflow(); + return -1; + } - *sec = (time_t)intpart; - err = intpart - (double)*sec; - if (err <= -1.0 || err >= 1.0) { - error_time_t_overflow(); - return -1; - } + *numerator = (long)floatpart; + return 0; +} - *numerator = (long)floatpart; - return 0; +static int +_PyTime_ObjectToDenominator(PyObject *obj, time_t *sec, long *numerator, + double denominator, _PyTime_round_t round) +{ + assert(denominator <= LONG_MAX); + if (PyFloat_Check(obj)) { + double d = PyFloat_AsDouble(obj); + return _PyTime_DoubleToDenominator(d, sec, numerator, + denominator, round); } else { *sec = _PyLong_AsTime_t(obj); @@ -220,30 +228,39 @@ _PyTime_FromTimeval(_PyTime_t *tp, struct timeval *tv, int raise) } #endif +static int +_PyTime_FromFloatObject(_PyTime_t *t, double value, _PyTime_round_t round, + long to_nanoseconds) +{ + /* volatile avoids unsafe optimization on float enabled by gcc -O3 */ + volatile double d, err; + + /* convert to a number of nanoseconds */ + d = value; + d *= to_nanoseconds; + + if (round == _PyTime_ROUND_CEILING) + 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; +} + 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 */ - volatile double d, err; - - /* convert to a number of nanoseconds */ + double d; d = PyFloat_AsDouble(obj); - d *= to_nanoseconds; - - if (round == _PyTime_ROUND_CEILING) - 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; + return _PyTime_FromFloatObject(t, d, round, to_nanoseconds); } else { #ifdef HAVE_LONG_LONG -- cgit v1.2.1 From ea4bd213402a67d964b8edd7221d98eecc806be7 Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Wed, 2 Sep 2015 00:50:43 +0200 Subject: Move assertion inside _PyTime_ObjectToTimeval() Change also _PyTime_FromSeconds() assertion to ensure that the _PyTime_t type is used. --- Python/pytime.c | 20 ++++++++++++++------ 1 file changed, 14 insertions(+), 6 deletions(-) (limited to 'Python/pytime.c') diff --git a/Python/pytime.c b/Python/pytime.c index 17ef197225..fcac68a389 100644 --- a/Python/pytime.c +++ b/Python/pytime.c @@ -101,7 +101,8 @@ static int _PyTime_ObjectToDenominator(PyObject *obj, time_t *sec, long *numerator, double denominator, _PyTime_round_t round) { - assert(denominator <= LONG_MAX); + assert(denominator <= (double)LONG_MAX); + if (PyFloat_Check(obj)) { double d = PyFloat_AsDouble(obj); return _PyTime_DoubleToDenominator(d, sec, numerator, @@ -149,14 +150,20 @@ int _PyTime_ObjectToTimespec(PyObject *obj, time_t *sec, long *nsec, _PyTime_round_t round) { - return _PyTime_ObjectToDenominator(obj, sec, nsec, 1e9, round); + int res; + res = _PyTime_ObjectToDenominator(obj, sec, nsec, 1e9, round); + assert(0 <= *nsec && *nsec < SEC_TO_NS ); + return res; } int _PyTime_ObjectToTimeval(PyObject *obj, time_t *sec, long *usec, _PyTime_round_t round) { - return _PyTime_ObjectToDenominator(obj, sec, usec, 1e6, round); + int res; + res = _PyTime_ObjectToDenominator(obj, sec, usec, 1e6, round); + assert(0 <= *usec && *usec < SEC_TO_US ); + return res; } static void @@ -170,12 +177,13 @@ _PyTime_t _PyTime_FromSeconds(int seconds) { _PyTime_t t; + t = (_PyTime_t)seconds; /* 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; + assert((t >= 0 && t <= _PyTime_MAX / SEC_TO_NS) + || (t < 0 && t >= _PyTime_MIN / SEC_TO_NS)); + t *= SEC_TO_NS; return t; } -- cgit v1.2.1 From d0ee151e3b640eae2a63b1c13860837d3ece2cda Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Wed, 2 Sep 2015 01:43:56 +0200 Subject: Issue #23517: Add "half up" rounding mode to the _PyTime API --- Python/pytime.c | 64 ++++++++++++++++++++++++++++++++++++++++++++++++--------- 1 file changed, 54 insertions(+), 10 deletions(-) (limited to 'Python/pytime.c') diff --git a/Python/pytime.c b/Python/pytime.c index fcac68a389..3294e0f49b 100644 --- a/Python/pytime.c +++ b/Python/pytime.c @@ -60,6 +60,17 @@ _PyLong_FromTime_t(time_t t) #endif } +static double +_PyTime_RoundHalfUp(double x) +{ + if (x >= 0.0) + x = floor(x + 0.5); + else + x = ceil(x - 0.5); + return x; +} + + static int _PyTime_DoubleToDenominator(double d, time_t *sec, long *numerator, double denominator, _PyTime_round_t round) @@ -75,7 +86,9 @@ _PyTime_DoubleToDenominator(double d, time_t *sec, long *numerator, } floatpart *= denominator; - if (round == _PyTime_ROUND_CEILING) { + if (round == _PyTime_ROUND_HALF_UP) + floatpart = _PyTime_RoundHalfUp(floatpart); + else if (round == _PyTime_ROUND_CEILING) { floatpart = ceil(floatpart); if (floatpart >= denominator) { floatpart = 0.0; @@ -124,7 +137,9 @@ _PyTime_ObjectToTime_t(PyObject *obj, time_t *sec, _PyTime_round_t round) double d, intpart, err; d = PyFloat_AsDouble(obj); - if (round == _PyTime_ROUND_CEILING) + if (round == _PyTime_ROUND_HALF_UP) + d = _PyTime_RoundHalfUp(d); + else if (round == _PyTime_ROUND_CEILING) d = ceil(d); else d = floor(d); @@ -247,7 +262,9 @@ _PyTime_FromFloatObject(_PyTime_t *t, double value, _PyTime_round_t round, d = value; d *= to_nanoseconds; - if (round == _PyTime_ROUND_CEILING) + if (round == _PyTime_ROUND_HALF_UP) + d = _PyTime_RoundHalfUp(d); + else if (round == _PyTime_ROUND_CEILING) d = ceil(d); else d = floor(d); @@ -333,7 +350,19 @@ static _PyTime_t _PyTime_Divide(_PyTime_t t, _PyTime_t k, _PyTime_round_t round) { assert(k > 1); - if (round == _PyTime_ROUND_CEILING) { + if (round == _PyTime_ROUND_HALF_UP) { + _PyTime_t x, r; + x = t / k; + r = t % k; + if (Py_ABS(r) >= k / 2) { + if (t >= 0) + x++; + else + x--; + } + return x; + } + else if (round == _PyTime_ROUND_CEILING) { if (t >= 0) return (t + k - 1) / k; else @@ -359,8 +388,10 @@ static int _PyTime_AsTimeval_impl(_PyTime_t t, struct timeval *tv, _PyTime_round_t round, int raise) { + const long k = US_TO_NS; _PyTime_t secs, ns; int res = 0; + int usec; secs = t / SEC_TO_NS; ns = t % SEC_TO_NS; @@ -392,20 +423,33 @@ _PyTime_AsTimeval_impl(_PyTime_t t, struct timeval *tv, _PyTime_round_t round, res = -1; #endif - if (round == _PyTime_ROUND_CEILING) - tv->tv_usec = (int)((ns + US_TO_NS - 1) / US_TO_NS); + if (round == _PyTime_ROUND_HALF_UP) { + _PyTime_t r; + usec = (int)(ns / k); + r = ns % k; + if (Py_ABS(r) >= k / 2) { + if (ns >= 0) + usec++; + else + usec--; + } + } + else if (round == _PyTime_ROUND_CEILING) + usec = (int)((ns + k - 1) / k); else - tv->tv_usec = (int)(ns / US_TO_NS); + usec = (int)(ns / k); - if (tv->tv_usec >= SEC_TO_US) { - tv->tv_usec -= SEC_TO_US; + if (usec >= SEC_TO_US) { + usec -= SEC_TO_US; tv->tv_sec += 1; } if (res && raise) _PyTime_overflow(); - assert(0 <= tv->tv_usec && tv->tv_usec <= 999999); + assert(0 <= usec && usec <= 999999); + + tv->tv_usec = usec; return res; } -- cgit v1.2.1 From 03030b95ebb8b1583fa8d9b6859f10bf59cb83ad Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Wed, 2 Sep 2015 10:37:46 +0200 Subject: Issue #23517: Fix _PyTime_ObjectToDenominator() * initialize numerator on overflow error ensure that numerator is smaller than * denominator. --- Python/pytime.c | 35 +++++++++++++++++------------------ 1 file changed, 17 insertions(+), 18 deletions(-) (limited to 'Python/pytime.c') diff --git a/Python/pytime.c b/Python/pytime.c index 3294e0f49b..8a2579b792 100644 --- a/Python/pytime.c +++ b/Python/pytime.c @@ -60,6 +60,7 @@ _PyLong_FromTime_t(time_t t) #endif } +/* Round to nearest with ties going away from zero (_PyTime_ROUND_HALF_UP). */ static double _PyTime_RoundHalfUp(double x) { @@ -81,32 +82,31 @@ _PyTime_DoubleToDenominator(double d, time_t *sec, long *numerator, floatpart = modf(d, &intpart); if (floatpart < 0) { - floatpart = 1.0 + floatpart; + floatpart += 1.0; intpart -= 1.0; } floatpart *= denominator; if (round == _PyTime_ROUND_HALF_UP) floatpart = _PyTime_RoundHalfUp(floatpart); - else if (round == _PyTime_ROUND_CEILING) { + else if (round == _PyTime_ROUND_CEILING) floatpart = ceil(floatpart); - if (floatpart >= denominator) { - floatpart = 0.0; - intpart += 1.0; - } - } - else { + else floatpart = floor(floatpart); + if (floatpart >= denominator) { + floatpart -= denominator; + intpart += 1.0; } + assert(0.0 <= floatpart && floatpart < denominator); *sec = (time_t)intpart; + *numerator = (long)floatpart; + err = intpart - (double)*sec; if (err <= -1.0 || err >= 1.0) { error_time_t_overflow(); return -1; } - - *numerator = (long)floatpart; return 0; } @@ -123,9 +123,9 @@ _PyTime_ObjectToDenominator(PyObject *obj, time_t *sec, long *numerator, } else { *sec = _PyLong_AsTime_t(obj); + *numerator = 0; if (*sec == (time_t)-1 && PyErr_Occurred()) return -1; - *numerator = 0; return 0; } } @@ -167,7 +167,7 @@ _PyTime_ObjectToTimespec(PyObject *obj, time_t *sec, long *nsec, { int res; res = _PyTime_ObjectToDenominator(obj, sec, nsec, 1e9, round); - assert(0 <= *nsec && *nsec < SEC_TO_NS ); + assert(0 <= *nsec && *nsec < SEC_TO_NS); return res; } @@ -177,7 +177,7 @@ _PyTime_ObjectToTimeval(PyObject *obj, time_t *sec, long *usec, { int res; res = _PyTime_ObjectToDenominator(obj, sec, usec, 1e6, round); - assert(0 <= *usec && *usec < SEC_TO_US ); + assert(0 <= *usec && *usec < SEC_TO_US); return res; } @@ -444,12 +444,11 @@ _PyTime_AsTimeval_impl(_PyTime_t t, struct timeval *tv, _PyTime_round_t round, tv->tv_sec += 1; } + assert(0 <= usec && usec < SEC_TO_US); + tv->tv_usec = usec; + if (res && raise) _PyTime_overflow(); - - assert(0 <= usec && usec <= 999999); - - tv->tv_usec = usec; return res; } @@ -484,7 +483,7 @@ _PyTime_AsTimespec(_PyTime_t t, struct timespec *ts) } ts->tv_nsec = nsec; - assert(0 <= ts->tv_nsec && ts->tv_nsec <= 999999999); + assert(0 <= ts->tv_nsec && ts->tv_nsec < SEC_TO_NS); return 0; } #endif -- cgit v1.2.1 From af05039c1f6f6ec4f969f0fff5b9d7bdcf3af0ee Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Wed, 2 Sep 2015 11:58:56 +0200 Subject: Issue #23517: Try to fix test_time on "x86 Ubuntu Shared 3.x" buildbot --- Python/pytime.c | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) (limited to 'Python/pytime.c') diff --git a/Python/pytime.c b/Python/pytime.c index 8a2579b792..ffb390afe5 100644 --- a/Python/pytime.c +++ b/Python/pytime.c @@ -64,11 +64,13 @@ _PyLong_FromTime_t(time_t t) static double _PyTime_RoundHalfUp(double x) { - if (x >= 0.0) - x = floor(x + 0.5); + /* volatile avoids optimization changing how numbers are rounded */ + volatile double d = x; + if (d >= 0.0) + d = floor(d + 0.5); else - x = ceil(x - 0.5); - return x; + d = ceil(d - 0.5); + return d; } @@ -77,7 +79,7 @@ _PyTime_DoubleToDenominator(double d, time_t *sec, long *numerator, double denominator, _PyTime_round_t round) { double intpart, err; - /* volatile avoids unsafe optimization on float enabled by gcc -O3 */ + /* volatile avoids optimization changing how numbers are rounded */ volatile double floatpart; floatpart = modf(d, &intpart); @@ -134,7 +136,8 @@ int _PyTime_ObjectToTime_t(PyObject *obj, time_t *sec, _PyTime_round_t round) { if (PyFloat_Check(obj)) { - double d, intpart, err; + /* volatile avoids optimization changing how numbers are rounded */ + volatile double d, intpart, err; d = PyFloat_AsDouble(obj); if (round == _PyTime_ROUND_HALF_UP) @@ -255,7 +258,7 @@ static int _PyTime_FromFloatObject(_PyTime_t *t, double value, _PyTime_round_t round, long to_nanoseconds) { - /* volatile avoids unsafe optimization on float enabled by gcc -O3 */ + /* volatile avoids optimization changing how numbers are rounded */ volatile double d, err; /* convert to a number of nanoseconds */ -- cgit v1.2.1 From ea58c6adb1a11497266bc69f0629feb2fb541fa9 Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Wed, 2 Sep 2015 19:16:07 +0200 Subject: Issue #23517: datetime.timedelta constructor now rounds microseconds to nearest with ties going away from zero (ROUND_HALF_UP), as Python 2 and Python older than 3.3, instead of rounding to nearest with ties going to nearest even integer (ROUND_HALF_EVEN). --- Python/pytime.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'Python/pytime.c') diff --git a/Python/pytime.c b/Python/pytime.c index ffb390afe5..02a1edfea0 100644 --- a/Python/pytime.c +++ b/Python/pytime.c @@ -60,8 +60,7 @@ _PyLong_FromTime_t(time_t t) #endif } -/* Round to nearest with ties going away from zero (_PyTime_ROUND_HALF_UP). */ -static double +double _PyTime_RoundHalfUp(double x) { /* volatile avoids optimization changing how numbers are rounded */ -- 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 70192d08abe7e46c418bbbc309d32f8c936bcb4e Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Thu, 3 Sep 2015 16:25:45 +0200 Subject: Enhance _PyTime_AsTimespec() Ensure that the tv_nsec field is set, even if the function fails with an overflow. --- 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 8166ceccc5..cea3cf5d43 100644 --- a/Python/pytime.c +++ b/Python/pytime.c @@ -479,13 +479,13 @@ _PyTime_AsTimespec(_PyTime_t t, struct timespec *ts) secs -= 1; } ts->tv_sec = (time_t)secs; + assert(0 <= nsec && nsec < SEC_TO_NS); + ts->tv_nsec = nsec; + if ((_PyTime_t)ts->tv_sec != secs) { _PyTime_overflow(); return -1; } - ts->tv_nsec = nsec; - - assert(0 <= ts->tv_nsec && ts->tv_nsec < SEC_TO_NS); return 0; } #endif -- cgit v1.2.1 From c894ad91051a6eb8e23be06dc1cc421a54d69907 Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Thu, 3 Sep 2015 16:33:16 +0200 Subject: Don't abuse volatile keyword in pytime.c Only use it on the most important number. This change fixes also a compiler warning on modf(). --- Python/pytime.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) (limited to 'Python/pytime.c') diff --git a/Python/pytime.c b/Python/pytime.c index cea3cf5d43..d226389a7c 100644 --- a/Python/pytime.c +++ b/Python/pytime.c @@ -135,8 +135,9 @@ int _PyTime_ObjectToTime_t(PyObject *obj, time_t *sec, _PyTime_round_t round) { if (PyFloat_Check(obj)) { + double intpart, err; /* volatile avoids optimization changing how numbers are rounded */ - volatile double d, intpart, err; + volatile double d; d = PyFloat_AsDouble(obj); if (round == _PyTime_ROUND_HALF_UP) @@ -257,8 +258,9 @@ static int _PyTime_FromFloatObject(_PyTime_t *t, double value, _PyTime_round_t round, long to_nanoseconds) { + double err; /* volatile avoids optimization changing how numbers are rounded */ - volatile double d, err; + volatile double d; /* convert to a number of nanoseconds */ d = value; -- cgit v1.2.1 From 85ed934b48ca506799d4d531db87e5e3aea2fb96 Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Fri, 4 Sep 2015 23:57:25 +0200 Subject: Issue #23517: Fix implementation of the ROUND_HALF_UP rounding mode in datetime.datetime.fromtimestamp() and datetime.datetime.utcfromtimestamp(). microseconds sign should be kept before rounding. --- Python/pytime.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'Python/pytime.c') diff --git a/Python/pytime.c b/Python/pytime.c index d226389a7c..036447365c 100644 --- a/Python/pytime.c +++ b/Python/pytime.c @@ -82,10 +82,6 @@ _PyTime_DoubleToDenominator(double d, time_t *sec, long *numerator, volatile double floatpart; floatpart = modf(d, &intpart); - if (floatpart < 0) { - floatpart += 1.0; - intpart -= 1.0; - } floatpart *= denominator; if (round == _PyTime_ROUND_HALF_UP) @@ -98,6 +94,10 @@ _PyTime_DoubleToDenominator(double d, time_t *sec, long *numerator, floatpart -= denominator; intpart += 1.0; } + else if (floatpart < 0) { + floatpart += denominator; + intpart -= 1.0; + } assert(0.0 <= floatpart && floatpart < denominator); *sec = (time_t)intpart; -- cgit v1.2.1 From fb8809cbfa9e3302900ffb10b732626ce864157c Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Wed, 9 Sep 2015 01:02:23 +0200 Subject: Issue #23517: fromtimestamp() and utcfromtimestamp() methods of datetime.datetime now round microseconds to nearest with ties going to nearest even integer (ROUND_HALF_EVEN), as round(float), instead of rounding towards -Infinity (ROUND_FLOOR). pytime API: replace _PyTime_ROUND_HALF_UP with _PyTime_ROUND_HALF_EVEN. Fix also _PyTime_Divide() for negative numbers. _PyTime_AsTimeval_impl() now reuses _PyTime_Divide() instead of reimplementing rounding modes. --- Python/pytime.c | 71 +++++++++++++++++++++++---------------------------------- 1 file changed, 29 insertions(+), 42 deletions(-) (limited to 'Python/pytime.c') diff --git a/Python/pytime.c b/Python/pytime.c index 036447365c..9f7ee2d0ed 100644 --- a/Python/pytime.c +++ b/Python/pytime.c @@ -61,18 +61,15 @@ _PyLong_FromTime_t(time_t t) } double -_PyTime_RoundHalfUp(double x) +_PyTime_RoundHalfEven(double x) { - /* volatile avoids optimization changing how numbers are rounded */ - volatile double d = x; - if (d >= 0.0) - d = floor(d + 0.5); - else - d = ceil(d - 0.5); - return d; + double rounded = round(x); + if (fabs(x-rounded) == 0.5) + /* halfway case: round to even */ + rounded = 2.0*round(x/2.0); + return rounded; } - static int _PyTime_DoubleToDenominator(double d, time_t *sec, long *numerator, double denominator, _PyTime_round_t round) @@ -84,8 +81,8 @@ _PyTime_DoubleToDenominator(double d, time_t *sec, long *numerator, floatpart = modf(d, &intpart); floatpart *= denominator; - if (round == _PyTime_ROUND_HALF_UP) - floatpart = _PyTime_RoundHalfUp(floatpart); + if (round == _PyTime_ROUND_HALF_EVEN) + floatpart = _PyTime_RoundHalfEven(floatpart); else if (round == _PyTime_ROUND_CEILING) floatpart = ceil(floatpart); else @@ -140,8 +137,8 @@ _PyTime_ObjectToTime_t(PyObject *obj, time_t *sec, _PyTime_round_t round) volatile double d; d = PyFloat_AsDouble(obj); - if (round == _PyTime_ROUND_HALF_UP) - d = _PyTime_RoundHalfUp(d); + if (round == _PyTime_ROUND_HALF_EVEN) + d = _PyTime_RoundHalfEven(d); else if (round == _PyTime_ROUND_CEILING) d = ceil(d); else @@ -266,8 +263,8 @@ _PyTime_FromFloatObject(_PyTime_t *t, double value, _PyTime_round_t round, d = value; d *= to_nanoseconds; - if (round == _PyTime_ROUND_HALF_UP) - d = _PyTime_RoundHalfUp(d); + if (round == _PyTime_ROUND_HALF_EVEN) + d = _PyTime_RoundHalfEven(d); else if (round == _PyTime_ROUND_CEILING) d = ceil(d); else @@ -351,14 +348,16 @@ _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_HALF_UP) { - _PyTime_t x, r; + if (round == _PyTime_ROUND_HALF_EVEN) { + _PyTime_t x, r, abs_r; x = t / k; r = t % k; - if (Py_ABS(r) >= k / 2) { + abs_r = Py_ABS(r); + if (abs_r > k / 2 || (abs_r == k / 2 && (Py_ABS(x) & 1))) { if (t >= 0) x++; else @@ -369,11 +368,15 @@ _PyTime_Divide(_PyTime_t t, _PyTime_t k, _PyTime_round_t round) else 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 @@ -392,17 +395,12 @@ static int _PyTime_AsTimeval_impl(_PyTime_t t, struct timeval *tv, _PyTime_round_t round, int raise) { - const long k = US_TO_NS; _PyTime_t secs, ns; int res = 0; int usec; 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), @@ -427,23 +425,12 @@ _PyTime_AsTimeval_impl(_PyTime_t t, struct timeval *tv, _PyTime_round_t round, res = -1; #endif - if (round == _PyTime_ROUND_HALF_UP) { - _PyTime_t r; - usec = (int)(ns / k); - r = ns % k; - if (Py_ABS(r) >= k / 2) { - if (ns >= 0) - usec++; - else - usec--; - } + usec = (int)_PyTime_Divide(ns, US_TO_NS, round); + if (usec < 0) { + usec += SEC_TO_US; + tv->tv_sec -= 1; } - else if (round == _PyTime_ROUND_CEILING) - usec = (int)((ns + k - 1) / k); - else - usec = (int)(ns / k); - - if (usec >= SEC_TO_US) { + else if (usec >= SEC_TO_US) { usec -= SEC_TO_US; tv->tv_sec += 1; } -- cgit v1.2.1 From 44e262519e28bd9149a791b4a0f1d5087595abc5 Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Wed, 9 Sep 2015 22:28:09 +0200 Subject: Make _PyTime_RoundHalfEven() private again --- Python/pytime.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'Python/pytime.c') diff --git a/Python/pytime.c b/Python/pytime.c index 9f7ee2d0ed..37dfabbb0a 100644 --- a/Python/pytime.c +++ b/Python/pytime.c @@ -60,7 +60,9 @@ _PyLong_FromTime_t(time_t t) #endif } -double +/* Round to nearest with ties going to nearest even integer + (_PyTime_ROUND_HALF_EVEN) */ +static double _PyTime_RoundHalfEven(double x) { double rounded = round(x); -- cgit v1.2.1 From 3876e1b59e0f11407d2db9029b7741da8b3788d0 Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Wed, 9 Sep 2015 22:28:58 +0200 Subject: pytime: add _PyTime_Round() helper to factorize code --- Python/pytime.c | 45 ++++++++++++++++++++------------------------- 1 file changed, 20 insertions(+), 25 deletions(-) (limited to 'Python/pytime.c') diff --git a/Python/pytime.c b/Python/pytime.c index 37dfabbb0a..973550600f 100644 --- a/Python/pytime.c +++ b/Python/pytime.c @@ -72,6 +72,17 @@ _PyTime_RoundHalfEven(double x) return rounded; } +static double +_PyTime_Round(double x, _PyTime_round_t round) +{ + if (round == _PyTime_ROUND_HALF_EVEN) + return _PyTime_RoundHalfEven(x); + else if (round == _PyTime_ROUND_CEILING) + return ceil(x); + else + return floor(x); +} + static int _PyTime_DoubleToDenominator(double d, time_t *sec, long *numerator, double denominator, _PyTime_round_t round) @@ -83,12 +94,7 @@ _PyTime_DoubleToDenominator(double d, time_t *sec, long *numerator, floatpart = modf(d, &intpart); floatpart *= denominator; - if (round == _PyTime_ROUND_HALF_EVEN) - floatpart = _PyTime_RoundHalfEven(floatpart); - else if (round == _PyTime_ROUND_CEILING) - floatpart = ceil(floatpart); - else - floatpart = floor(floatpart); + floatpart = _PyTime_Round(floatpart, round); if (floatpart >= denominator) { floatpart -= denominator; intpart += 1.0; @@ -139,12 +145,7 @@ _PyTime_ObjectToTime_t(PyObject *obj, time_t *sec, _PyTime_round_t round) volatile double d; d = PyFloat_AsDouble(obj); - if (round == _PyTime_ROUND_HALF_EVEN) - d = _PyTime_RoundHalfEven(d); - else if (round == _PyTime_ROUND_CEILING) - d = ceil(d); - else - d = floor(d); + d = _PyTime_Round(d, round); (void)modf(d, &intpart); *sec = (time_t)intpart; @@ -255,7 +256,7 @@ _PyTime_FromTimeval(_PyTime_t *tp, struct timeval *tv, int raise) static int _PyTime_FromFloatObject(_PyTime_t *t, double value, _PyTime_round_t round, - long to_nanoseconds) + long unit_to_ns) { double err; /* volatile avoids optimization changing how numbers are rounded */ @@ -263,14 +264,8 @@ _PyTime_FromFloatObject(_PyTime_t *t, double value, _PyTime_round_t round, /* convert to a number of nanoseconds */ d = value; - d *= to_nanoseconds; - - if (round == _PyTime_ROUND_HALF_EVEN) - d = _PyTime_RoundHalfEven(d); - else if (round == _PyTime_ROUND_CEILING) - d = ceil(d); - else - d = floor(d); + d *= (double)unit_to_ns; + d = _PyTime_Round(d, round); *t = (_PyTime_t)d; err = d - (double)*t; @@ -283,12 +278,12 @@ _PyTime_FromFloatObject(_PyTime_t *t, double value, _PyTime_round_t round, static int _PyTime_FromObject(_PyTime_t *t, PyObject *obj, _PyTime_round_t round, - long to_nanoseconds) + long unit_to_ns) { if (PyFloat_Check(obj)) { double d; d = PyFloat_AsDouble(obj); - return _PyTime_FromFloatObject(t, d, round, to_nanoseconds); + return _PyTime_FromFloatObject(t, d, round, unit_to_ns); } else { #ifdef HAVE_LONG_LONG @@ -305,8 +300,8 @@ _PyTime_FromObject(_PyTime_t *t, PyObject *obj, _PyTime_round_t round, _PyTime_overflow(); return -1; } - *t = sec * to_nanoseconds; - if (*t / to_nanoseconds != sec) { + *t = sec * unit_to_ns; + if (*t / unit_to_ns != sec) { _PyTime_overflow(); return -1; } -- cgit v1.2.1 From 00d73959796abf8ab71adefa9bbc6f46d2ab9cba Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Wed, 9 Sep 2015 22:32:48 +0200 Subject: test_time: rewrite PyTime API rounding tests Drop all hardcoded tests. Instead, reimplement each function in Python, usually using decimal.Decimal for the rounding mode. Add much more values to the dataset. Test various timestamp units from picroseconds to seconds, in integer and float. Enhance also _PyTime_AsSecondsDouble(). --- Python/pytime.c | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) (limited to 'Python/pytime.c') diff --git a/Python/pytime.c b/Python/pytime.c index 973550600f..4c940c98f1 100644 --- a/Python/pytime.c +++ b/Python/pytime.c @@ -324,12 +324,16 @@ _PyTime_FromMillisecondsObject(_PyTime_t *t, PyObject *obj, _PyTime_round_t roun 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; + if (t % SEC_TO_NS == 0) { + _PyTime_t secs; + /* Divide using integers to avoid rounding issues on the integer part. + 1e-9 cannot be stored exactly in IEEE 64-bit. */ + secs = t / SEC_TO_NS; + return (double)secs; + } + else { + return (double)t / 1e9; + } } PyObject * -- cgit v1.2.1 From 18124b247e83d18d2e2332877c0a481a90a97b0c Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Thu, 10 Sep 2015 09:10:14 +0200 Subject: Fix test_time on Windows * Filter values which would overflow on conversion to the C long type (for timeval.tv_sec). * Adjust also the message of OverflowError on PyTime conversions * test_time: add debug information if a timestamp conversion fails --- Python/pytime.c | 28 ++++++++-------------------- 1 file changed, 8 insertions(+), 20 deletions(-) (limited to 'Python/pytime.c') diff --git a/Python/pytime.c b/Python/pytime.c index 4c940c98f1..243f756f3d 100644 --- a/Python/pytime.c +++ b/Python/pytime.c @@ -288,18 +288,21 @@ _PyTime_FromObject(_PyTime_t *t, PyObject *obj, _PyTime_round_t round, else { #ifdef HAVE_LONG_LONG PY_LONG_LONG sec; - sec = PyLong_AsLongLong(obj); assert(sizeof(PY_LONG_LONG) <= sizeof(_PyTime_t)); + + sec = PyLong_AsLongLong(obj); #else long sec; - sec = PyLong_AsLong(obj); assert(sizeof(PY_LONG_LONG) <= sizeof(_PyTime_t)); + + sec = PyLong_AsLong(obj); #endif if (sec == -1 && PyErr_Occurred()) { if (PyErr_ExceptionMatches(PyExc_OverflowError)) _PyTime_overflow(); return -1; } + *t = sec * unit_to_ns; if (*t / unit_to_ns != sec) { _PyTime_overflow(); @@ -404,27 +407,12 @@ _PyTime_AsTimeval_impl(_PyTime_t t, struct timeval *tv, _PyTime_round_t round, 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) { - secs = LONG_MAX; - res = -1; - } - else if (secs < LONG_MIN) { - secs = LONG_MIN; - res = -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; +#endif if ((_PyTime_t)tv->tv_sec != secs) res = -1; -#endif usec = (int)_PyTime_Divide(ns, US_TO_NS, round); if (usec < 0) { @@ -440,7 +428,7 @@ _PyTime_AsTimeval_impl(_PyTime_t t, struct timeval *tv, _PyTime_round_t round, tv->tv_usec = usec; if (res && raise) - _PyTime_overflow(); + error_time_t_overflow(); return res; } @@ -473,7 +461,7 @@ _PyTime_AsTimespec(_PyTime_t t, struct timespec *ts) ts->tv_nsec = nsec; if ((_PyTime_t)ts->tv_sec != secs) { - _PyTime_overflow(); + error_time_t_overflow(); return -1; } return 0; -- cgit v1.2.1 From 038b36f15b87ad0cb61493ec79b83228990f870a Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Thu, 10 Sep 2015 11:48:00 +0200 Subject: Try to fix test_time.test_AsSecondsDouble() on "x86 Gentoo Non-Debug with X 3.x" buildbot Use volatile keyword in _PyTime_Round() --- 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 243f756f3d..9470636fdc 100644 --- a/Python/pytime.c +++ b/Python/pytime.c @@ -75,12 +75,17 @@ _PyTime_RoundHalfEven(double x) static double _PyTime_Round(double x, _PyTime_round_t round) { + /* volatile avoids optimization changing how numbers are rounded */ + volatile double d; + + d = x; if (round == _PyTime_ROUND_HALF_EVEN) - return _PyTime_RoundHalfEven(x); + d = _PyTime_RoundHalfEven(d); else if (round == _PyTime_ROUND_CEILING) - return ceil(x); + d = ceil(d); else - return floor(x); + d = floor(d); + return d; } static int -- cgit v1.2.1 From f5b952d0b1de0c175629903172a74ebed21b0020 Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Thu, 10 Sep 2015 13:25:17 +0200 Subject: New try to fix test_time.test_AsSecondsDouble() on x86 buildbots. Use volatile keyword in _PyTime_AsSecondsDouble() --- Python/pytime.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) (limited to 'Python/pytime.c') diff --git a/Python/pytime.c b/Python/pytime.c index 9470636fdc..c82d5985b4 100644 --- a/Python/pytime.c +++ b/Python/pytime.c @@ -332,16 +332,21 @@ _PyTime_FromMillisecondsObject(_PyTime_t *t, PyObject *obj, _PyTime_round_t roun double _PyTime_AsSecondsDouble(_PyTime_t t) { + /* volatile avoids optimization changing how numbers are rounded */ + volatile double d; + if (t % SEC_TO_NS == 0) { _PyTime_t secs; /* Divide using integers to avoid rounding issues on the integer part. 1e-9 cannot be stored exactly in IEEE 64-bit. */ secs = t / SEC_TO_NS; - return (double)secs; + d = (double)secs; } else { - return (double)t / 1e9; + d = (double)t; + d /= 1e9; } + return d; } PyObject * -- cgit v1.2.1 From 57057ccfd38eaa647ad2983e024e34833e0205e1 Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Thu, 10 Sep 2015 15:55:07 +0200 Subject: pytime: add _PyTime_check_mul_overflow() macro to avoid undefined behaviour Overflow test in test_FromSecondsObject() fails on FreeBSD 10.0 buildbot which uses clang. clang implements more aggressive optimization which gives different result than GCC on undefined behaviours. Check if a multiplication will overflow, instead of checking if a multiplicatin had overflowed, to avoid undefined behaviour. Add also debug information if the test on overflow fails. --- Python/pytime.c | 35 ++++++++++++++++++++++++----------- 1 file changed, 24 insertions(+), 11 deletions(-) (limited to 'Python/pytime.c') diff --git a/Python/pytime.c b/Python/pytime.c index c82d5985b4..1b20dc797e 100644 --- a/Python/pytime.c +++ b/Python/pytime.c @@ -7,6 +7,11 @@ #include /* mach_absolute_time(), mach_timebase_info() */ #endif +#define _PyTime_check_mul_overflow(a, b) \ + (assert(b > 0), \ + (_PyTime_t)(a) < _PyTime_MIN / (_PyTime_t)(b) \ + || _PyTime_MAX / (_PyTime_t)(b) < (_PyTime_t)(a)) + /* To millisecond (10^-3) */ #define SEC_TO_MS 1000 @@ -226,12 +231,15 @@ _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) { + assert(sizeof(ts->tv_sec) <= sizeof(_PyTime_t)); + t = (_PyTime_t)ts->tv_sec; + + if (_PyTime_check_mul_overflow(t, SEC_TO_NS)) { if (raise) _PyTime_overflow(); res = -1; } + t = t * SEC_TO_NS; t += ts->tv_nsec; @@ -245,12 +253,15 @@ _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) { + assert(sizeof(ts->tv_sec) <= sizeof(_PyTime_t)); + t = (_PyTime_t)tv->tv_sec; + + if (_PyTime_check_mul_overflow(t, SEC_TO_NS)) { if (raise) _PyTime_overflow(); res = -1; } + t = t * SEC_TO_NS; t += (_PyTime_t)tv->tv_usec * US_TO_NS; @@ -308,11 +319,11 @@ _PyTime_FromObject(_PyTime_t *t, PyObject *obj, _PyTime_round_t round, return -1; } - *t = sec * unit_to_ns; - if (*t / unit_to_ns != sec) { + if (_PyTime_check_mul_overflow(sec, unit_to_ns)) { _PyTime_overflow(); return -1; } + *t = sec * unit_to_ns; return 0; } } @@ -587,19 +598,20 @@ _PyTime_GetSystemClockWithInfo(_PyTime_t *t, _Py_clock_info_t *info) return pygettimeofday_new(t, info, 1); } - static int pymonotonic(_PyTime_t *tp, _Py_clock_info_t *info, int raise) { #if defined(MS_WINDOWS) - ULONGLONG result; + ULONGLONG ticks; + _PyTime_t t; assert(info == NULL || raise); - result = GetTickCount64(); + ticks = GetTickCount64(); + assert(sizeof(result) <= sizeof(_PyTime_t)); + t = (_PyTime_t)ticks; - *tp = result * MS_TO_NS; - if (*tp / MS_TO_NS != result) { + if (_PyTime_check_mul_overflow(t, MS_TO_NS)) { if (raise) { _PyTime_overflow(); return -1; @@ -607,6 +619,7 @@ pymonotonic(_PyTime_t *tp, _Py_clock_info_t *info, int raise) /* Hello, time traveler! */ assert(0); } + *tp = t * MS_TO_NS; if (info) { DWORD timeAdjustment, timeIncrement; -- cgit v1.2.1 From 2409685d76c1dd6f121cd3417cf1c31009b7a77c Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Thu, 10 Sep 2015 16:00:06 +0200 Subject: pytime: oops, fix typos on Windows --- Python/pytime.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'Python/pytime.c') diff --git a/Python/pytime.c b/Python/pytime.c index 1b20dc797e..9d0b0fafe1 100644 --- a/Python/pytime.c +++ b/Python/pytime.c @@ -253,7 +253,7 @@ _PyTime_FromTimeval(_PyTime_t *tp, struct timeval *tv, int raise) _PyTime_t t; int res = 0; - assert(sizeof(ts->tv_sec) <= sizeof(_PyTime_t)); + assert(sizeof(tv->tv_sec) <= sizeof(_PyTime_t)); t = (_PyTime_t)tv->tv_sec; if (_PyTime_check_mul_overflow(t, SEC_TO_NS)) { @@ -608,7 +608,7 @@ pymonotonic(_PyTime_t *tp, _Py_clock_info_t *info, int raise) assert(info == NULL || raise); ticks = GetTickCount64(); - assert(sizeof(result) <= sizeof(_PyTime_t)); + assert(sizeof(ticks) <= sizeof(_PyTime_t)); t = (_PyTime_t)ticks; if (_PyTime_check_mul_overflow(t, MS_TO_NS)) { -- cgit v1.2.1 From 00448c2fb0056c69988252dfb086fd1aa26fba7b Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Fri, 18 Sep 2015 13:23:02 +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 | 79 +++++++++++++++++++++++++++++++++++++++++++-------------- 1 file changed, 60 insertions(+), 19 deletions(-) (limited to 'Python/pytime.c') diff --git a/Python/pytime.c b/Python/pytime.c index 9d0b0fafe1..9889a3b53b 100644 --- a/Python/pytime.c +++ b/Python/pytime.c @@ -417,54 +417,95 @@ _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 res = 0; int usec; + int res = 0; secs = t / SEC_TO_NS; ns = t % SEC_TO_NS; -#ifdef MS_WINDOWS - tv->tv_sec = (long)secs; -#else - tv->tv_sec = secs; -#endif - if ((_PyTime_t)tv->tv_sec != secs) - res = -1; - usec = (int)_PyTime_Divide(ns, US_TO_NS, round); if (usec < 0) { usec += SEC_TO_US; - tv->tv_sec -= 1; + if (secs != _PyTime_MIN) + secs -= 1; + else + res = -1; } else if (usec >= SEC_TO_US) { usec -= SEC_TO_US; - tv->tv_sec += 1; + if (secs != _PyTime_MAX) + secs += 1; + else + res = -1; } - assert(0 <= usec && usec < SEC_TO_US); - tv->tv_usec = usec; - if (res && raise) - error_time_t_overflow(); + *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 + tv->tv_sec = secs; +#endif + tv->tv_usec = us; + + if (res < 0 || (_PyTime_t)tv->tv_sec != secs) { + if (raise) + error_time_t_overflow(); + return -1; + } + 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) int _PyTime_AsTimespec(_PyTime_t t, struct timespec *ts) -- 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 ab51974e3b9722fd98eb2e54774875169c7ae41a Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Tue, 29 Sep 2015 13:41:46 +0200 Subject: Try to fix _PyTime_AsTimevalStruct_impl() on OpenBSD It looks like the check for integer overflow doesn't work on x86 OpenBSD 5.8. --- 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 9889a3b53b..53611b1ec1 100644 --- a/Python/pytime.c +++ b/Python/pytime.c @@ -454,7 +454,7 @@ static int _PyTime_AsTimevalStruct_impl(_PyTime_t t, struct timeval *tv, _PyTime_round_t round, int raise) { - _PyTime_t secs; + _PyTime_t secs, secs2; int us; int res; @@ -467,7 +467,8 @@ _PyTime_AsTimevalStruct_impl(_PyTime_t t, struct timeval *tv, #endif tv->tv_usec = us; - if (res < 0 || (_PyTime_t)tv->tv_sec != secs) { + secs2 = (_PyTime_t)tv->tv_sec; + if (res < 0 || secs2 != secs) { if (raise) error_time_t_overflow(); return -1; -- cgit v1.2.1 From 5a6aa0da055b939140518cdcf2f187de533e3456 Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Wed, 30 Sep 2015 22:50:12 +0200 Subject: Backout change 28d3bcb1bad6: "Try to fix _PyTime_AsTimevalStruct_impl() on OpenBSD", I'm not sure that the change was really needed. I read the test result of an old build because the OpenBSD was 100 builds late. --- Python/pytime.c | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) (limited to 'Python/pytime.c') diff --git a/Python/pytime.c b/Python/pytime.c index 53611b1ec1..9889a3b53b 100644 --- a/Python/pytime.c +++ b/Python/pytime.c @@ -454,7 +454,7 @@ static int _PyTime_AsTimevalStruct_impl(_PyTime_t t, struct timeval *tv, _PyTime_round_t round, int raise) { - _PyTime_t secs, secs2; + _PyTime_t secs; int us; int res; @@ -467,8 +467,7 @@ _PyTime_AsTimevalStruct_impl(_PyTime_t t, struct timeval *tv, #endif tv->tv_usec = us; - secs2 = (_PyTime_t)tv->tv_sec; - if (res < 0 || secs2 != secs) { + if (res < 0 || (_PyTime_t)tv->tv_sec != secs) { if (raise) error_time_t_overflow(); return -1; -- cgit v1.2.1 From c879d2c9d9e1633e31e70e0e4e4f5e79dcc87709 Mon Sep 17 00:00:00 2001 From: Victor Stinner Date: Thu, 1 Oct 2015 08:44:03 +0200 Subject: Fix _PyTime_AsTimevalStruct_impl() on OpenBSD On the x86 OpenBSD 5.8 buildbot, the integer overflow check is ignored. Copy the tv_sec variable into a Py_time_t variable instead of "simply" casting it to Py_time_t, to fix the integer overflow check. --- 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 9889a3b53b..53611b1ec1 100644 --- a/Python/pytime.c +++ b/Python/pytime.c @@ -454,7 +454,7 @@ static int _PyTime_AsTimevalStruct_impl(_PyTime_t t, struct timeval *tv, _PyTime_round_t round, int raise) { - _PyTime_t secs; + _PyTime_t secs, secs2; int us; int res; @@ -467,7 +467,8 @@ _PyTime_AsTimevalStruct_impl(_PyTime_t t, struct timeval *tv, #endif tv->tv_usec = us; - if (res < 0 || (_PyTime_t)tv->tv_sec != secs) { + secs2 = (_PyTime_t)tv->tv_sec; + if (res < 0 || secs2 != secs) { if (raise) error_time_t_overflow(); return -1; -- cgit v1.2.1 From ab9342460bf6d26c19d73c506195737a35529433 Mon Sep 17 00:00:00 2001 From: Serhiy Storchaka Date: Sat, 7 Nov 2015 15:42:38 +0200 Subject: Issue #25558: Use compile-time asserts. --- Python/pytime.c | 25 ++++++++++++------------- 1 file changed, 12 insertions(+), 13 deletions(-) (limited to 'Python/pytime.c') diff --git a/Python/pytime.c b/Python/pytime.c index 53611b1ec1..8a6cb551f0 100644 --- a/Python/pytime.c +++ b/Python/pytime.c @@ -43,7 +43,7 @@ _PyLong_AsTime_t(PyObject *obj) val = PyLong_AsLongLong(obj); #else long val; - assert(sizeof(time_t) <= sizeof(long)); + Py_BUILD_ASSERT(sizeof(time_t) <= sizeof(long)); val = PyLong_AsLong(obj); #endif if (val == -1 && PyErr_Occurred()) { @@ -60,7 +60,7 @@ _PyLong_FromTime_t(time_t t) #if defined(HAVE_LONG_LONG) && SIZEOF_TIME_T == SIZEOF_LONG_LONG return PyLong_FromLongLong((PY_LONG_LONG)t); #else - assert(sizeof(time_t) <= sizeof(long)); + Py_BUILD_ASSERT(sizeof(time_t) <= sizeof(long)); return PyLong_FromLong((long)t); #endif } @@ -209,6 +209,8 @@ _PyTime_FromSeconds(int seconds) /* 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). */ + Py_BUILD_ASSERT(INT_MAX <= _PyTime_MAX / SEC_TO_NS); + Py_BUILD_ASSERT(INT_MIN >= _PyTime_MIN / SEC_TO_NS); assert((t >= 0 && t <= _PyTime_MAX / SEC_TO_NS) || (t < 0 && t >= _PyTime_MIN / SEC_TO_NS)); t *= SEC_TO_NS; @@ -219,7 +221,7 @@ _PyTime_t _PyTime_FromNanoseconds(PY_LONG_LONG ns) { _PyTime_t t; - assert(sizeof(PY_LONG_LONG) <= sizeof(_PyTime_t)); + Py_BUILD_ASSERT(sizeof(PY_LONG_LONG) <= sizeof(_PyTime_t)); t = Py_SAFE_DOWNCAST(ns, PY_LONG_LONG, _PyTime_t); return t; } @@ -231,7 +233,7 @@ _PyTime_FromTimespec(_PyTime_t *tp, struct timespec *ts, int raise) _PyTime_t t; int res = 0; - assert(sizeof(ts->tv_sec) <= sizeof(_PyTime_t)); + Py_BUILD_ASSERT(sizeof(ts->tv_sec) <= sizeof(_PyTime_t)); t = (_PyTime_t)ts->tv_sec; if (_PyTime_check_mul_overflow(t, SEC_TO_NS)) { @@ -253,7 +255,7 @@ _PyTime_FromTimeval(_PyTime_t *tp, struct timeval *tv, int raise) _PyTime_t t; int res = 0; - assert(sizeof(tv->tv_sec) <= sizeof(_PyTime_t)); + Py_BUILD_ASSERT(sizeof(tv->tv_sec) <= sizeof(_PyTime_t)); t = (_PyTime_t)tv->tv_sec; if (_PyTime_check_mul_overflow(t, SEC_TO_NS)) { @@ -304,12 +306,12 @@ _PyTime_FromObject(_PyTime_t *t, PyObject *obj, _PyTime_round_t round, else { #ifdef HAVE_LONG_LONG PY_LONG_LONG sec; - assert(sizeof(PY_LONG_LONG) <= sizeof(_PyTime_t)); + Py_BUILD_ASSERT(sizeof(PY_LONG_LONG) <= sizeof(_PyTime_t)); sec = PyLong_AsLongLong(obj); #else long sec; - assert(sizeof(PY_LONG_LONG) <= sizeof(_PyTime_t)); + Py_BUILD_ASSERT(sizeof(PY_LONG_LONG) <= sizeof(_PyTime_t)); sec = PyLong_AsLong(obj); #endif @@ -364,10 +366,10 @@ PyObject * _PyTime_AsNanosecondsObject(_PyTime_t t) { #ifdef HAVE_LONG_LONG - assert(sizeof(PY_LONG_LONG) >= sizeof(_PyTime_t)); + Py_BUILD_ASSERT(sizeof(PY_LONG_LONG) >= sizeof(_PyTime_t)); return PyLong_FromLongLong((PY_LONG_LONG)t); #else - assert(sizeof(long) >= sizeof(_PyTime_t)); + Py_BUILD_ASSERT(sizeof(long) >= sizeof(_PyTime_t)); return PyLong_FromLong((long)t); #endif } @@ -650,7 +652,7 @@ pymonotonic(_PyTime_t *tp, _Py_clock_info_t *info, int raise) assert(info == NULL || raise); ticks = GetTickCount64(); - assert(sizeof(ticks) <= sizeof(_PyTime_t)); + Py_BUILD_ASSERT(sizeof(ticks) <= sizeof(_PyTime_t)); t = (_PyTime_t)ticks; if (_PyTime_check_mul_overflow(t, MS_TO_NS)) { @@ -774,8 +776,5 @@ _PyTime_Init(void) 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 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