changeset: 91949:b0aa2448b3ad user: Victor Stinner date: Thu Jul 31 13:03:12 2014 +0200 files: Include/pytime.h Modules/_datetimemodule.c Modules/_threadmodule.c Modules/gcmodule.c Modules/socketmodule.c Modules/timemodule.c Python/pythonrun.c Python/pytime.c description: Change pytime.h to use _PyTimeSpec structure (nanosecond resolution) instead of _PyTime_timeval structure (microsecond resolution) diff -r e49efa892efb -r b0aa2448b3ad Include/pytime.h --- a/Include/pytime.h Thu Jul 31 13:07:17 2014 +0200 +++ b/Include/pytime.h Thu Jul 31 13:03:12 2014 +0200 @@ -13,14 +13,13 @@ functions and constants extern "C" { #endif -#ifdef HAVE_GETTIMEOFDAY -typedef struct timeval _PyTime_timeval; -#else +#ifndef Py_LIMITED_API + +/* Timestamp with nanosecond resolution */ typedef struct { - time_t tv_sec; /* seconds since Jan. 1, 1970 */ - long tv_usec; /* and microseconds */ -} _PyTime_timeval; -#endif + time_t tv_sec; /* seconds */ + long tv_nsec; /* nanoseconds */ +} _PyTimeSpec; /* Structure used by time.get_clock_info() */ typedef struct { @@ -30,29 +29,46 @@ typedef struct { double resolution; } _Py_clock_info_t; -/* Similar to POSIX gettimeofday but cannot fail. If system gettimeofday - * fails or is not available, fall back to lower resolution clocks. - */ -PyAPI_FUNC(void) _PyTime_gettimeofday(_PyTime_timeval *tp); +/* Get the system clock. The function never fails. + _PyTimeSpec_Init() ensures that the system clock works. */ +PyAPI_FUNC(void) _PyTimeSpec_get_time(_PyTimeSpec *tp); -/* Similar to _PyTime_gettimeofday() but retrieve also information on the - * clock used to get the current time. */ -PyAPI_FUNC(void) _PyTime_gettimeofday_info( - _PyTime_timeval *tp, +/* Get the system clock: fill into (if set) with information of the + function used to get the system block. + Return 0 on success, raise an exceptio and return -1 on error. */ +PyAPI_FUNC(int) _PyTimeSpec_get_time_info( + _PyTimeSpec *tp, _Py_clock_info_t *info); -#define _PyTime_ADD_SECONDS(tv, interval) \ -do { \ - tv.tv_usec += (long) (((long) interval - interval) * 1000000); \ - tv.tv_sec += (time_t) interval + (time_t) (tv.tv_usec / 1000000); \ - tv.tv_usec %= 1000000; \ -} while (0) +/* Add seconds to a _PyTimeSpec timestamp. + Return 0 on success, return -1 on overflow. */ +PyAPI_FUNC(int) _PyTimeSpec_add_sec(_PyTimeSpec *tv, double secs); -#define _PyTime_INTERVAL(tv_start, tv_end) \ - ((tv_end.tv_sec - tv_start.tv_sec) + \ - (tv_end.tv_usec - tv_start.tv_usec) * 0.000001) +#if defined(HAVE_LONG_LONG) +typedef unsigned PY_LONG_LONG _PyTime_unit_t; +#define _PyTime_UNIT_MAX PY_ULLONG_MAX +#else +typedef unsigned long _PyTime_unit_t; +#define _PyTime_UNIT_MAX ULONG_MAX +#endif -#ifndef Py_LIMITED_API +/* Add microseconds to a _PyTimeSpec timestamp. + Ensure that ts->ts_nsec is in range [0; 999999999]. + Return 0 on success, -1 on overflow. */ +PyAPI_FUNC(int) _PyTimeSpec_add_us( + _PyTimeSpec *ts, + _PyTime_unit_t microseconds); + +/* Compute the different in seconds between two _PyTimeSpec timestamps */ +PyAPI_FUNC(double) _PyTimeSpec_interval_sec( + _PyTimeSpec *start, + _PyTimeSpec *end); + +/* Compute the different in microseconds between two _PyTimeSpec timestamps. + Return (_PyTime_unit_t)-1 on overflow. */ +PyAPI_FUNC(_PyTime_unit_t) _PyTimeSpec_interval_us( + _PyTimeSpec *start, + _PyTimeSpec *end); typedef enum { /* Round towards zero. */ @@ -92,10 +108,11 @@ PyAPI_FUNC(int) _PyTime_ObjectToTimespec time_t *sec, long *nsec, _PyTime_round_t); + #endif /* Dummy to force linking. */ -PyAPI_FUNC(void) _PyTime_Init(void); +PyAPI_FUNC(int) _PyTimeSpec_Init(void); #ifdef __cplusplus } diff -r e49efa892efb -r b0aa2448b3ad Modules/_datetimemodule.c --- a/Modules/_datetimemodule.c Thu Jul 31 13:07:17 2014 +0200 +++ b/Modules/_datetimemodule.c Thu Jul 31 13:03:12 2014 +0200 @@ -4103,9 +4103,10 @@ datetime_from_timestamp(PyObject *cls, T static PyObject * datetime_best_possible(PyObject *cls, TM_FUNC f, PyObject *tzinfo) { - _PyTime_timeval t; - _PyTime_gettimeofday(&t); - return datetime_from_timet_and_us(cls, f, t.tv_sec, (int)t.tv_usec, + _PyTimeSpec ts; + _PyTimeSpec_get_time(&ts); + return datetime_from_timet_and_us(cls, f, + ts.tv_sec, (int)(ts.tv_nsec / 1000), tzinfo); } diff -r e49efa892efb -r b0aa2448b3ad Modules/_threadmodule.c --- a/Modules/_threadmodule.c Thu Jul 31 13:07:17 2014 +0200 +++ b/Modules/_threadmodule.c Thu Jul 31 13:03:12 2014 +0200 @@ -52,14 +52,13 @@ static PyLockStatus acquire_timed(PyThread_type_lock lock, PY_TIMEOUT_T microseconds) { PyLockStatus r; - _PyTime_timeval curtime; - _PyTime_timeval endtime; + _PyTimeSpec curtime; + _PyTimeSpec endtime; if (microseconds > 0) { - _PyTime_gettimeofday(&endtime); - endtime.tv_sec += microseconds / (1000 * 1000); - endtime.tv_usec += microseconds % (1000 * 1000); + _PyTimeSpec_get_time(&endtime); + _PyTimeSpec_add_us(&endtime, microseconds); } @@ -83,9 +82,8 @@ acquire_timed(PyThread_type_lock lock, P /* If we're using a timeout, recompute the timeout after processing * signals, since those can take time. */ if (microseconds > 0) { - _PyTime_gettimeofday(&curtime); - microseconds = ((endtime.tv_sec - curtime.tv_sec) * 1000000 + - (endtime.tv_usec - curtime.tv_usec)); + _PyTimeSpec_get_time(&curtime); + microseconds = _PyTimeSpec_interval_us(&curtime, &endtime); /* Check for negative values, since those mean block forever. */ diff -r e49efa892efb -r b0aa2448b3ad Modules/gcmodule.c --- a/Modules/gcmodule.c Thu Jul 31 13:07:17 2014 +0200 +++ b/Modules/gcmodule.c Thu Jul 31 13:03:12 2014 +0200 @@ -25,7 +25,7 @@ #include "Python.h" #include "frameobject.h" /* for PyFrame_ClearFreeList */ -#include "pytime.h" /* for _PyTime_gettimeofday, _PyTime_INTERVAL */ +#include "pytime.h" /* for _PyTime_monotonic */ /* Get an object's GC head */ #define AS_GC(o) ((PyGC_Head *)(o)-1) @@ -908,7 +908,7 @@ collect(int generation, Py_ssize_t *n_co PyGC_Head unreachable; /* non-problematic unreachable trash */ PyGC_Head finalizers; /* objects with, & reachable from, __del__ */ PyGC_Head *gc; - _PyTime_timeval t1; + _PyTimeSpec t1; struct gc_generation_stats *stats = &generation_stats[generation]; @@ -919,7 +919,7 @@ collect(int generation, Py_ssize_t *n_co for (i = 0; i < NUM_GENERATIONS; i++) PySys_FormatStderr(" %zd", gc_list_size(GEN_HEAD(i))); - _PyTime_gettimeofday(&t1); + _PyTimeSpec_get_time(&t1); PySys_WriteStderr("\n"); } @@ -1024,8 +1024,8 @@ collect(int generation, Py_ssize_t *n_co debug_cycle("uncollectable", FROM_GC(gc)); } if (debug & DEBUG_STATS) { - _PyTime_timeval t2; - _PyTime_gettimeofday(&t2); + _PyTimeSpec t2; + _PyTimeSpec_get_time(&t2); if (m == 0 && n == 0) PySys_WriteStderr("gc: done"); @@ -1033,7 +1033,8 @@ collect(int generation, Py_ssize_t *n_co PySys_FormatStderr( "gc: done, %zd unreachable, %zd uncollectable", n+m, n); - PySys_WriteStderr(", %.4fs elapsed\n", _PyTime_INTERVAL(t1, t2)); + PySys_WriteStderr(", %.4fs elapsed\n", + _PyTimeSpec_interval_sec(&t1, &t2)); } /* Append instances in the uncollectable set to a Python diff -r e49efa892efb -r b0aa2448b3ad Modules/socketmodule.c --- a/Modules/socketmodule.c Thu Jul 31 13:07:17 2014 +0200 +++ b/Modules/socketmodule.c Thu Jul 31 13:03:12 2014 +0200 @@ -676,13 +676,13 @@ internal_select(PySocketSockObject *s, i #define BEGIN_SELECT_LOOP(s) \ { \ - _PyTime_timeval now, deadline = {0, 0}; \ + _PyTimeSpec now, deadline = {0, 0}; \ double interval = s->sock_timeout; \ int has_timeout = s->sock_timeout > 0.0; \ if (has_timeout) { \ - _PyTime_gettimeofday(&now); \ + _PyTimeSpec_get_time(&now); \ deadline = now; \ - _PyTime_ADD_SECONDS(deadline, s->sock_timeout); \ + _PyTimeSpec_add_sec(&deadline, s->sock_timeout); \ } \ while (1) { \ errno = 0; \ @@ -691,8 +691,8 @@ internal_select(PySocketSockObject *s, i if (!has_timeout || \ (!CHECK_ERRNO(EWOULDBLOCK) && !CHECK_ERRNO(EAGAIN))) \ break; \ - _PyTime_gettimeofday(&now); \ - interval = _PyTime_INTERVAL(now, deadline); \ + _PyTimeSpec_get_time(&now); \ + interval = _PyTimeSpec_interval_sec(&now, &deadline); \ } \ } \ diff -r e49efa892efb -r b0aa2448b3ad Modules/timemodule.c --- a/Modules/timemodule.c Thu Jul 31 13:07:17 2014 +0200 +++ b/Modules/timemodule.c Thu Jul 31 13:03:12 2014 +0200 @@ -1534,7 +1534,7 @@ PyInit_time(void) static PyObject* floattime(_Py_clock_info_t *info) { - _PyTime_timeval t; + _PyTimeSpec ts; #ifdef HAVE_CLOCK_GETTIME struct timespec tp; int ret; @@ -1557,8 +1557,11 @@ floattime(_Py_clock_info_t *info) return PyFloat_FromDouble(tp.tv_sec + tp.tv_nsec * 1e-9); } #endif - _PyTime_gettimeofday_info(&t, info); - return PyFloat_FromDouble((double)t.tv_sec + t.tv_usec * 1e-6); + if (_PyTimeSpec_get_time_info(&ts, info) < 0) { + assert(0 && "_PyTimeSpec_get_time_info should never fail"); + return NULL; + } + return PyFloat_FromDouble((double)ts.tv_sec + ts.tv_nsec * 1e-9); } diff -r e49efa892efb -r b0aa2448b3ad Python/pythonrun.c --- a/Python/pythonrun.c Thu Jul 31 13:07:17 2014 +0200 +++ b/Python/pythonrun.c Thu Jul 31 13:03:12 2014 +0200 @@ -452,7 +452,8 @@ void if (_PyFaulthandler_Init()) Py_FatalError("Py_Initialize: can't initialize faulthandler"); - _PyTime_Init(); + if (_PyTimeSpec_Init() < 0) + Py_FatalError("Py_Initialize: can't initialize time"); if (initfsencoding(interp) < 0) Py_FatalError("Py_Initialize: unable to load the file system codec"); diff -r e49efa892efb -r b0aa2448b3ad Python/pytime.c --- a/Python/pytime.c Thu Jul 31 13:07:17 2014 +0200 +++ b/Python/pytime.c Thu Jul 31 13:03:12 2014 +0200 @@ -5,10 +5,10 @@ #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. + * pygettimeofday() 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 @@ -18,23 +18,182 @@ extern int ftime(struct timeb *); #endif +#if SIZEOF_TIME_T == 8 && defined(HAVE_LONG_LONG) +# define TIME_T_MAX ((PY_LONG_LONG)0x7ffffffffffff) +#elif SIZEOF_TIME_T == 8 && SIZEOF_LONG == 8 +# define TIME_T_MAX 0x7ffffffffffffL +#elif SIZEOF_TIME_T == 4 +# define TIME_T_MAX 0x7fffffff +#else +# error "unsupported size of time_t" +#endif + +/* one second in milliseconds (ms, 10^-3) */ +#define SEC_IN_MILLISECONDS (1000UL) + +/* one second in microseconds (us, 10^-6) */ +#define SEC_IN_MICROSECONDS (1000000UL) + +/* one second in nanoseconds (ns, 10^-9) */ +#define SEC_IN_NANOSECONDS (1000000000UL) + +/* one millisecond (ms, 10^-3) in nanoseconds (ns, 10^-9) */ +#define MS_IN_NANOSECONDS (1000000UL) + +/* one microsecond (us, 10^-6) in nanoseconds (ns, 10^-9) */ +#define US_IN_NANOSECONDS (1000UL) + static void -pygettimeofday(_PyTime_timeval *tp, _Py_clock_info_t *info) +error_time_t_overflow(void) +{ + PyErr_SetString(PyExc_OverflowError, + "timestamp out of range for platform time_t"); +} + +static int +_PyTime_DoubleToDenominator(double d, time_t *sec, long *numerator, + double denominator, _PyTime_round_t round, + int raise) +{ + double intpart, err; + /* volatile avoids unsafe optimization on float enabled by gcc -O3 */ + volatile double floatpart; + + assert(0 < denominator && denominator < 1.0); + + floatpart = modf(d, &intpart); + if (floatpart < 0) { + floatpart = 1.0 + floatpart; + intpart -= 1.0; + } + + floatpart *= denominator; + if (round == _PyTime_ROUND_UP) { + if (intpart >= 0) { + floatpart = ceil(floatpart); + if (floatpart >= denominator) { + floatpart -= denominator; + intpart += 1.0; + } + } + else { + floatpart = floor(floatpart); + } + } + + *sec = (time_t)intpart; + err = intpart - (double)*sec; + if (err <= -1.0 || err >= 1.0) { + if (raise) + error_time_t_overflow(); + return -1; + } + + *numerator = (long)floatpart; + return 0; +} + +static int +_PyTimeSpec_set_overflow(_PyTimeSpec *ts) +{ + ts->tv_sec = TIME_T_MAX; + ts->tv_nsec = 999999999; + return -1; +} + +static int +_PyTimeSpec_add(_PyTimeSpec *ts, time_t secs, long ns) +{ + assert(ns < SEC_IN_NANOSECONDS); + + if (ts->tv_sec > TIME_T_MAX - secs) + goto overflow; + ts->tv_sec += secs; + + ts->tv_nsec += ns; + while (ts->tv_nsec >= SEC_IN_NANOSECONDS) { + ts->tv_nsec -= SEC_IN_NANOSECONDS; + if (ts->tv_sec == TIME_T_MAX) + goto overflow; + ts->tv_sec++; + } + return 0; + +overflow: + return _PyTimeSpec_set_overflow(ts); +} + + +int +_PyTimeSpec_add_sec(_PyTimeSpec *ts, double seconds) +{ + time_t secs; + long ns; + + if (_PyTime_DoubleToDenominator(seconds, &secs, + &ns, 1e-9, _PyTime_ROUND_UP, 0) < 0) + return _PyTimeSpec_set_overflow(ts); + + return _PyTimeSpec_add(ts, secs, ns); +} + +int +_PyTimeSpec_add_us(_PyTimeSpec *ts, + _PyTime_unit_t microseconds) +{ + _PyTime_unit_t secs, us; + + us = microseconds % SEC_IN_MICROSECONDS; + secs = microseconds / SEC_IN_MICROSECONDS; + if (secs > TIME_T_MAX) + return _PyTimeSpec_set_overflow(ts); + + return _PyTimeSpec_add(ts, (time_t)secs, us * US_IN_NANOSECONDS); +} + +double +_PyTimeSpec_interval_sec(_PyTimeSpec *start, _PyTimeSpec *end) +{ + double dt; + dt = end->tv_sec - start->tv_sec; + dt += (end->tv_nsec - start->tv_nsec) * 1e-9; + return dt; +} + +_PyTime_unit_t +_PyTimeSpec_interval_us(_PyTimeSpec *start, _PyTimeSpec *end) +{ + _PyTime_unit_t us; + time_t secs; + + us = (end->tv_nsec - start->tv_nsec) / US_IN_NANOSECONDS; + secs = (end->tv_sec - start->tv_sec); + if (secs > (_PyTime_UNIT_MAX - us) * SEC_IN_MICROSECONDS) + goto overflow; + us += secs * SEC_IN_MICROSECONDS; + return us; + +overflow: + return _PyTime_UNIT_MAX; +} + +static int +pygettimeofday(_PyTimeSpec *ts, _Py_clock_info_t *info, int raise) { #ifdef MS_WINDOWS FILETIME system_time; ULARGE_INTEGER large; - ULONGLONG microseconds; + ULONGLONG nanoseconds; 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 + /* 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). */ - microseconds = large.QuadPart / 10 - 11644473600000000; - tp->tv_sec = microseconds / 1000000; - tp->tv_usec = microseconds % 1000000; + nanoseconds = large.QuadPart * 100 - 11644473600000000000; + ts->tv_sec = nanoseconds / SEC_IN_NANOSECONDS; + ts->tv_nsec = nanoseconds % SEC_IN_NANOSECONDS; if (info) { DWORD timeAdjustment, timeIncrement; BOOL isTimeAdjustmentDisabled; @@ -46,8 +205,9 @@ pygettimeofday(_PyTime_timeval *tp, _Py_ info->resolution = timeIncrement * 1e-7; info->adjustable = 1; } + return 0; #else - /* There are three ways to get the time: + /* There are four ways to get the time: (1) gettimeofday() -- resolution in microseconds (2) ftime() -- resolution in milliseconds (3) time() -- resolution in seconds @@ -56,68 +216,96 @@ pygettimeofday(_PyTime_timeval *tp, _Py_ fail, so we fall back on ftime() or time(). Note: clock resolution does not imply clock accuracy! */ +#if defined(HAVE_GETTIMEOFDAY) || defined(HAVE_FTIME) + int err; +#endif #ifdef HAVE_GETTIMEOFDAY - int err; + struct timeval tv; +#endif +#ifdef HAVE_FTIME + struct timeb tb; +#endif + + /* test gettimeofday() */ +#ifdef HAVE_GETTIMEOFDAY #ifdef GETTIMEOFDAY_NO_TZ - err = gettimeofday(tp); + err = gettimeofday(&tv); #else - err = gettimeofday(tp, (struct timezone *)NULL); + err = gettimeofday(&tv, (struct timezone *)NULL); #endif - if (err == 0) { + if (err && raise) { + PyErr_SetFromErrno(PyExc_OSError); + goto error; + } + if (!err) { + ts->tv_sec = tv.tv_sec; + ts->tv_nsec = tv.tv_usec * US_IN_NANOSECONDS; if (info) { info->implementation = "gettimeofday()"; info->resolution = 1e-6; info->monotonic = 0; info->adjustable = 1; } - return; + return 0; } #endif /* HAVE_GETTIMEOFDAY */ -#if defined(HAVE_FTIME) - { - struct timeb t; - ftime(&t); - tp->tv_sec = t.time; - tp->tv_usec = t.millitm * 1000; + /* test ftime() */ +#ifdef HAVE_FTIME + err = ftime(&tb); + if (err && raise) { + PyErr_SetFromErrno(PyExc_OSError); + goto error; + } + if (!err) { + ts->tv_sec = tb.time; + ts->tv_nsec = tb.millitm * MS_IN_NANOSECONDS; if (info) { info->implementation = "ftime()"; info->resolution = 1e-3; info->monotonic = 0; info->adjustable = 1; } + return 0; } -#else /* !HAVE_FTIME */ - tp->tv_sec = time(NULL); - tp->tv_usec = 0; +#endif /* !HAVE_FTIME */ + + errno = 0; + ts->tv_sec = time(NULL); + if (ts->tv_sec == (time_t)-1 && errno != 0) { + if (raise) + PyErr_SetFromErrno(PyExc_OSError); + goto error; + } + ts->tv_nsec = 0; if (info) { info->implementation = "time()"; info->resolution = 1.0; info->monotonic = 0; info->adjustable = 1; } -#endif /* !HAVE_FTIME */ + return 0; + +error: + ts->tv_sec = 0; + ts->tv_nsec = 0; + return -1; #endif /* MS_WINDOWS */ } void -_PyTime_gettimeofday(_PyTime_timeval *tp) +_PyTimeSpec_get_time(_PyTimeSpec *tp) { - pygettimeofday(tp, NULL); + /* _PyTimeSpec_Init() checked that the system clock works. */ + (void)pygettimeofday(tp, NULL, 0); } -void -_PyTime_gettimeofday_info(_PyTime_timeval *tp, _Py_clock_info_t *info) +int +_PyTimeSpec_get_time_info(_PyTimeSpec *tp, _Py_clock_info_t *info) { - pygettimeofday(tp, info); -} - -static void -error_time_t_overflow(void) -{ - PyErr_SetString(PyExc_OverflowError, - "timestamp out of range for platform time_t"); + /* _PyTimeSpec_Init() checked that the system clock works. */ + return pygettimeofday(tp, info, 1); } time_t @@ -156,40 +344,9 @@ static int { 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; - - d = PyFloat_AsDouble(obj); - floatpart = modf(d, &intpart); - if (floatpart < 0) { - floatpart = 1.0 + floatpart; - intpart -= 1.0; - } - - 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); - } - } - - *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; + double d = PyFloat_AsDouble(obj); + return _PyTime_DoubleToDenominator(d, sec, numerator, + denominator, round, 1); } else { *sec = _PyLong_AsTime_t(obj); @@ -245,8 +402,12 @@ int return _PyTime_ObjectToDenominator(obj, sec, usec, 1e6, round); } -void -_PyTime_Init() +int +_PyTimeSpec_Init(void) { - /* Do nothing. Needed to force linking. */ + _PyTimeSpec ts; + /* ensure that the system clock works */ + if (_PyTimeSpec_get_time_info(&ts, NULL) < 0) + return -1; + return 0; }