diff -r 94d0e842b9ea -r 918faa56634f Include/pytime.h --- a/Include/pytime.h Fri Aug 01 12:28:49 2014 +0200 +++ b/Include/pytime.h Fri Aug 01 22:52:18 2014 +0200 @@ -13,14 +13,22 @@ functions and constants extern "C" { #endif -#ifdef HAVE_GETTIMEOFDAY -typedef struct timeval _PyTime_timeval; -#else +#ifndef Py_LIMITED_API + +typedef enum { + /* Round towards zero */ + _PyTime_ROUND_DOWN=0, + /* Round away from zero */ + _PyTime_ROUND_UP=1, + /* Round towards minus infinity */ + _PyTime_ROUND_FLOOR=2 +} _PyTime_round_t; + +/* 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, must be in the range [0; 999999999] */ +} _PyTimeSpec; /* Structure used by time.get_clock_info() */ typedef struct { @@ -30,43 +38,6 @@ 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); - -/* 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, - _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) - -#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) - -#ifndef Py_LIMITED_API - -typedef enum { - /* Round towards zero. */ - _PyTime_ROUND_DOWN=0, - /* Round away from zero. */ - _PyTime_ROUND_UP -} _PyTime_round_t; - -/* Convert a number of seconds, int or float, to time_t. */ -PyAPI_FUNC(int) _PyTime_ObjectToTime_t( - PyObject *obj, - time_t *sec, - _PyTime_round_t); - /* Convert a time_t to a PyLong. */ PyAPI_FUNC(PyObject *) _PyLong_FromTime_t( time_t sec); @@ -75,27 +46,93 @@ PyAPI_FUNC(PyObject *) _PyLong_FromTime_ PyAPI_FUNC(time_t) _PyLong_AsTime_t( PyObject *obj); -/* Convert a number of seconds, int or float, to a timeval structure. - usec is in the range [0; 999999] and rounded towards zero. - For example, -1.2 is converted to (-2, 800000). */ -PyAPI_FUNC(int) _PyTime_ObjectToTimeval( + +/* 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 _PyTimeSpec_get_time(), fill also info (if it is not NULL) with + information of the function used to get the system block. */ +PyAPI_FUNC(void) _PyTimeSpec_get_time_info( + _PyTimeSpec *tp, + _Py_clock_info_t *info); + + +/* Convert a number of seconds to a timestamp. + Return 0 on success, -1 on overflow. */ +PyAPI_FUNC(int) _PyTimeSpec_from_double(_PyTimeSpec *ts, + double seconds, + _PyTime_round_t round); + +/* Convert a number of seconds, Python int or float, to a timestamp. + Return 0 on success, raise an exception and return -1 on error. */ +PyAPI_FUNC(int) _PyTimeSpec_from_object(_PyTimeSpec *ts, PyObject *obj, - time_t *sec, - long *usec, - _PyTime_round_t); + _PyTime_round_t round); -/* Convert a number of seconds, int or float, to a timespec structure. - nsec is in the range [0; 999999999] and rounded towards zero. - For example, -1.2 is converted to (-2, 800000000). */ -PyAPI_FUNC(int) _PyTime_ObjectToTimespec( - PyObject *obj, - time_t *sec, - long *nsec, - _PyTime_round_t); +/* Get the sign of a timestamp: + - return -1 if the timestamp is negative + - return 0 if the timestamp is equal zero + - return 1 if the timestamp is strictly positive */ +PyAPI_FUNC(int) _PyTimeSpec_sign(const _PyTimeSpec *ts); + +/* Add two timestamps: res = ts1 + ts2. + Return 0 on success, -1 on overflow. */ +PyAPI_FUNC(int) _PyTimeSpec_add( + _PyTimeSpec *res, + const _PyTimeSpec *ts1, + const _PyTimeSpec *ts2); + +/* Substract two timestamps: res = end - start. + Return 0 on success, -1 on overflow. */ +PyAPI_FUNC(int) _PyTimeSpec_sub( + _PyTimeSpec *res, + const _PyTimeSpec *end, + const _PyTimeSpec *start); + +/* Convert a timestamp to a number of seconds. + Return 0 on success, $aise an exception and return -1 on overflow. */ +PyAPI_FUNC(int)_PyTimeSpec_to_time_t(const _PyTimeSpec *ts, + time_t *seconds, + _PyTime_round_t round); + +/* Convert a timestamp to a number of seconds. + Return 0 on success, return -1 if the conversion loss precision. */ +PyAPI_FUNC(int) _PyTimeSpec_as_double(const _PyTimeSpec *ts, + double *seconds, + _PyTime_round_t round); + +/* Multiply a timestamp by factor and convert it to an integer. + For example, it returns the number of milliseconds using a factor of 1000. + Return 0 on success. Set value to LONG_MAX and return -1 on overflow. + Set value to LONG_MIN and return -1 on underflow. */ +PyAPI_FUNC(int) _PyTimeSpec_multiply(const _PyTimeSpec *ts, + unsigned long factor, + long *value, + _PyTime_round_t round); + +/* Divide a timestamp by 1/denominator into (seconds, numerator) where seconds + is the integer part and numerator is the fractional part. numerator is in + the range [0; denominator - 1], denominator must not be zero. + + Return 0 on success, raise an exception and return -1 on overflow. */ +PyAPI_FUNC(int) _PyTimeSpec_divide(const _PyTimeSpec *ts, + unsigned int denominator, + time_t *seconds, + unsigned int *numerator, + _PyTime_round_t round); + +/* Convert a timestamp to a timeval structure. + + Return 0 on success, raise an exception and return -1 on overflow. */ +PyAPI_FUNC(int) _PyTimeSpec_to_timeval(const _PyTimeSpec *ts, + struct timeval *tv, + _PyTime_round_t round); #endif -/* Dummy to force linking. */ -PyAPI_FUNC(void) _PyTime_Init(void); +/* Initialize time functions. + Return 0 on success, -1 on error. */ +PyAPI_FUNC(int) _PyTimeSpec_Init(void); #ifdef __cplusplus } diff -r 94d0e842b9ea -r 918faa56634f Lib/test/test_time.py --- a/Lib/test/test_time.py Fri Aug 01 12:28:49 2014 +0200 +++ b/Lib/test/test_time.py Fri Aug 01 22:52:18 2014 +0200 @@ -1,4 +1,5 @@ from test import support +import enum import time import unittest import locale @@ -14,8 +15,15 @@ except ImportError: SIZEOF_INT = sysconfig.get_config_var('SIZEOF_INT') or 4 TIME_MAXYEAR = (1 << 8 * SIZEOF_INT - 1) - 1 TIME_MINYEAR = -TIME_MAXYEAR - 1 -_PyTime_ROUND_DOWN = 0 -_PyTime_ROUND_UP = 1 + +class _PyTime(enum.IntEnum): + ROUND_DOWN = 0 + ROUND_UP = 1 + ROUND_FLOOR = 2 + +_PyTime_ROUND_DOWN = _PyTime.ROUND_DOWN +_PyTime_ROUND_UP = _PyTime.ROUND_UP +_PyTime_ROUND_FLOOR = _PyTime.ROUND_FLOOR class TimeTestCase(unittest.TestCase): @@ -584,90 +592,75 @@ class TestStrftime4dyear(_TestStrftimeYe pass -class TestPytime(unittest.TestCase): +def get_time_t_limits(): + from _testcapi import pytimespec_from_object + + for time_t_min in (-2**63, -2**31): + try: + pytimespec_from_object(time_t_min - 1, _PyTime_ROUND_UP) + except OverflowError: + pytimespec_from_object(time_t_min, _PyTime_ROUND_UP) + break + else: + raise Exception("unable to compute time_t_min") + + for time_t_max in (2**63 - 1, 2**31 - 1): + try: + pytimespec_from_object(time_t_max + 1, _PyTime_ROUND_UP) + except OverflowError: + pytimespec_from_object(time_t_max, _PyTime_ROUND_UP) + break + else: + raise Exception("unable to compute time_t_max") + + return (time_t_min, time_t_max) + + +@support.cpython_only +class TestPyTimeSpec(unittest.TestCase): def setUp(self): self.invalid_values = ( -(2 ** 100), 2 ** 100, -(2.0 ** 100.0), 2.0 ** 100.0, ) - @support.cpython_only def test_time_t(self): - from _testcapi import pytime_object_to_time_t - for obj, time_t, rnd in ( - # Round towards zero - (0, 0, _PyTime_ROUND_DOWN), - (-1, -1, _PyTime_ROUND_DOWN), - (-1.0, -1, _PyTime_ROUND_DOWN), - (-1.9, -1, _PyTime_ROUND_DOWN), - (1.0, 1, _PyTime_ROUND_DOWN), - (1.9, 1, _PyTime_ROUND_DOWN), - # Round away from zero - (0, 0, _PyTime_ROUND_UP), - (-1, -1, _PyTime_ROUND_UP), - (-1.0, -1, _PyTime_ROUND_UP), - (-1.9, -2, _PyTime_ROUND_UP), - (1.0, 1, _PyTime_ROUND_UP), - (1.9, 2, _PyTime_ROUND_UP), + from _testcapi import pytimespec_to_time_t + for timespec, time_t, rnd in ( + # positive + ((0, 0), 0, _PyTime_ROUND_DOWN), + ((0, 0), 0, _PyTime_ROUND_UP), + ((0, 1), 0, _PyTime_ROUND_DOWN), + ((0, 1), 1, _PyTime_ROUND_UP), + ((2, 1), 2, _PyTime_ROUND_DOWN), + ((2, 1), 3, _PyTime_ROUND_UP), + ((3, 999999999), 3, _PyTime_ROUND_DOWN), + ((3, 999999999), 4, _PyTime_ROUND_UP), + # negative + ((-1, 999999999), 0, _PyTime_ROUND_DOWN), + ((-1, 999999999), -1, _PyTime_ROUND_UP), + ((-2, 999999999), -1, _PyTime_ROUND_DOWN), + ((-2, 999999999), -2, _PyTime_ROUND_UP), + ((-3, 1), -2, _PyTime_ROUND_DOWN), + ((-3, 1), -3, _PyTime_ROUND_UP), + # test rounding floor + ((0, 1), 0, _PyTime_ROUND_FLOOR), + ((-1, 999999999), -1, _PyTime_ROUND_FLOOR), ): - self.assertEqual(pytime_object_to_time_t(obj, rnd), time_t) + with self.subTest(timespec=timespec, round=rnd, time_t=time_t): + result = pytimespec_to_time_t(timespec, rnd) + val = timespec[0] + timespec[1] * 1e-9 + self.assertEqual(result, time_t, + "%s -> %s != %s" % (val, result, time_t)) - rnd = _PyTime_ROUND_DOWN - for invalid in self.invalid_values: - self.assertRaises(OverflowError, - pytime_object_to_time_t, invalid, rnd) + time_t_min, time_t_max = get_time_t_limits() - @support.cpython_only - def test_timeval(self): - from _testcapi import pytime_object_to_timeval - for obj, timeval, rnd in ( - # Round towards zero - (0, (0, 0), _PyTime_ROUND_DOWN), - (-1, (-1, 0), _PyTime_ROUND_DOWN), - (-1.0, (-1, 0), _PyTime_ROUND_DOWN), - (1e-6, (0, 1), _PyTime_ROUND_DOWN), - (1e-7, (0, 0), _PyTime_ROUND_DOWN), - (-1e-6, (-1, 999999), _PyTime_ROUND_DOWN), - (-1e-7, (-1, 999999), _PyTime_ROUND_DOWN), - (-1.2, (-2, 800000), _PyTime_ROUND_DOWN), - (0.9999999, (0, 999999), _PyTime_ROUND_DOWN), - (0.0000041, (0, 4), _PyTime_ROUND_DOWN), - (1.1234560, (1, 123456), _PyTime_ROUND_DOWN), - (1.1234569, (1, 123456), _PyTime_ROUND_DOWN), - (-0.0000040, (-1, 999996), _PyTime_ROUND_DOWN), - (-0.0000041, (-1, 999995), _PyTime_ROUND_DOWN), - (-1.1234560, (-2, 876544), _PyTime_ROUND_DOWN), - (-1.1234561, (-2, 876543), _PyTime_ROUND_DOWN), - # Round away from zero - (0, (0, 0), _PyTime_ROUND_UP), - (-1, (-1, 0), _PyTime_ROUND_UP), - (-1.0, (-1, 0), _PyTime_ROUND_UP), - (1e-6, (0, 1), _PyTime_ROUND_UP), - (1e-7, (0, 1), _PyTime_ROUND_UP), - (-1e-6, (-1, 999999), _PyTime_ROUND_UP), - (-1e-7, (-1, 999999), _PyTime_ROUND_UP), - (-1.2, (-2, 800000), _PyTime_ROUND_UP), - (0.9999999, (1, 0), _PyTime_ROUND_UP), - (0.0000041, (0, 5), _PyTime_ROUND_UP), - (1.1234560, (1, 123457), _PyTime_ROUND_UP), - (1.1234569, (1, 123457), _PyTime_ROUND_UP), - (-0.0000040, (-1, 999996), _PyTime_ROUND_UP), - (-0.0000041, (-1, 999995), _PyTime_ROUND_UP), - (-1.1234560, (-2, 876544), _PyTime_ROUND_UP), - (-1.1234561, (-2, 876543), _PyTime_ROUND_UP), - ): - with self.subTest(obj=obj, round=rnd, timeval=timeval): - self.assertEqual(pytime_object_to_timeval(obj, rnd), timeval) + with self.assertRaises(OverflowError): + pytimespec_to_time_t((time_t_max, 1), _PyTime_ROUND_UP) - rnd = _PyTime_ROUND_DOWN - for invalid in self.invalid_values: - self.assertRaises(OverflowError, - pytime_object_to_timeval, invalid, rnd) - - @support.cpython_only - def test_timespec(self): - from _testcapi import pytime_object_to_timespec - for obj, timespec, rnd in ( + def test_from_object(self): + from _testcapi import pytimespec_from_object, pytimespec_from_double + for secs, timespec, rnd in ( # Round towards zero (0, (0, 0), _PyTime_ROUND_DOWN), (-1, (-1, 0), _PyTime_ROUND_DOWN), @@ -675,13 +668,13 @@ class TestPytime(unittest.TestCase): (1e-9, (0, 1), _PyTime_ROUND_DOWN), (1e-10, (0, 0), _PyTime_ROUND_DOWN), (-1e-9, (-1, 999999999), _PyTime_ROUND_DOWN), - (-1e-10, (-1, 999999999), _PyTime_ROUND_DOWN), + (-1e-10, (0, 0), _PyTime_ROUND_DOWN), (-1.2, (-2, 800000000), _PyTime_ROUND_DOWN), (0.9999999999, (0, 999999999), _PyTime_ROUND_DOWN), (1.1234567890, (1, 123456789), _PyTime_ROUND_DOWN), (1.1234567899, (1, 123456789), _PyTime_ROUND_DOWN), (-1.1234567890, (-2, 876543211), _PyTime_ROUND_DOWN), - (-1.1234567891, (-2, 876543210), _PyTime_ROUND_DOWN), + (-1.1234567891, (-2, 876543211), _PyTime_ROUND_DOWN), # Round away from zero (0, (0, 0), _PyTime_ROUND_UP), (-1, (-1, 0), _PyTime_ROUND_UP), @@ -696,15 +689,219 @@ class TestPytime(unittest.TestCase): (1.1234567899, (1, 123456790), _PyTime_ROUND_UP), (-1.1234567890, (-2, 876543211), _PyTime_ROUND_UP), (-1.1234567891, (-2, 876543210), _PyTime_ROUND_UP), + # test rounding floor + (1e-10, (0, 0), _PyTime_ROUND_FLOOR), + (-1e-10, (-1, 999999999), _PyTime_ROUND_FLOOR), + (0.9999999999, (0, 999999999), _PyTime_ROUND_FLOOR), + (-0.9999999999, (-1, 0), _PyTime_ROUND_FLOOR), + (-0.9999999999, (-1, 1), _PyTime_ROUND_DOWN), + (-0.9999999999, (-1, 0), _PyTime_ROUND_UP), ): - with self.subTest(obj=obj, round=rnd, timespec=timespec): - self.assertEqual(pytime_object_to_timespec(obj, rnd), timespec) + with self.subTest(secs=secs, round=rnd, timespec=timespec): + self.assertEqual(pytimespec_from_object(secs, rnd), + timespec) + self.assertEqual(pytimespec_from_double(secs, rnd), + timespec) - rnd = _PyTime_ROUND_DOWN + # type error + self.assertRaises(TypeError, + pytimespec_from_object, "spam", _PyTime_ROUND_DOWN) + + # overflow for invalid in self.invalid_values: self.assertRaises(OverflowError, - pytime_object_to_timespec, invalid, rnd) + pytimespec_from_object, invalid, _PyTime_ROUND_DOWN) + self.assertRaises(OverflowError, + pytimespec_from_double, invalid, _PyTime_ROUND_DOWN) + def test_divide(self): + # test with microsecond (10^6) + from _testcapi import pytimespec_divide + + denominator = 10 ** 6 + for timespec, timeval, rnd in ( + # positive + ((0, 0), (0, 0), _PyTime_ROUND_DOWN), + ((0, 0), (0, 0), _PyTime_ROUND_UP), + ((1, 1), (1, 0), _PyTime_ROUND_DOWN), + ((1, 1), (1, 1), _PyTime_ROUND_UP), + ((1, 999999999), (1, 999999), _PyTime_ROUND_DOWN), + ((1, 999999999), (2, 0), _PyTime_ROUND_UP), + # negative + ((-1, 0), (-1, 0), _PyTime_ROUND_DOWN), + ((-1, 0), (-1, 0), _PyTime_ROUND_UP), + ((-1, 1), (-1, 1), _PyTime_ROUND_DOWN), + ((-1, 1), (-1, 0), _PyTime_ROUND_UP), + ((-1, 999999999), (0, 0), _PyTime_ROUND_DOWN), + ((-1, 999999999), (-1, 999999), _PyTime_ROUND_UP), + # test rounding floor + ((0, 1), (0, 0), _PyTime_ROUND_FLOOR), + ((-1, 999999999), (-1, 999999), _PyTime_ROUND_FLOOR), + ): + with self.subTest(timespec=timespec, round=rnd, timeval=timeval): + self.assertEqual(pytimespec_divide(timespec, denominator, rnd), + timeval) + + with self.assertRaises(ZeroDivisionError): + pytimespec_divide((1, 0), 0, _PyTime_ROUND_UP) + + time_t_min, time_t_max = get_time_t_limits() + with self.assertRaises(OverflowError): + pytimespec_divide((time_t_max, 999999999), denominator, _PyTime_ROUND_UP) + + def test_multiply(self): + # test pytimespec_multiply with milliseconds (10^-3) + import _testcapi + from _testcapi import pytimespec_multiply + + for timespec, value, rnd in ( + # positive + ((0, 0), 0, _PyTime_ROUND_DOWN), + ((0, 0), 0, _PyTime_ROUND_UP), + ((1, 1), 1000, _PyTime_ROUND_DOWN), + ((1, 1), 1001, _PyTime_ROUND_UP), + ((2, 1001), 2000, _PyTime_ROUND_DOWN), + ((2, 1001), 2001, _PyTime_ROUND_UP), + ((3, 999), 3000, _PyTime_ROUND_DOWN), + ((3, 999), 3001, _PyTime_ROUND_UP), + ((4, 999999), 4000, _PyTime_ROUND_DOWN), + ((4, 999999), 4001, _PyTime_ROUND_UP), + ((5, 999999999), 5999, _PyTime_ROUND_DOWN), + ((5, 999999999), 6000, _PyTime_ROUND_UP), + # negative + ((-1, 0), -1000, _PyTime_ROUND_DOWN), + ((-1, 0), -1000, _PyTime_ROUND_UP), + ((-1, 999999999), 0, _PyTime_ROUND_DOWN), + ((-1, 999999999), -1, _PyTime_ROUND_UP), + ((-2, 999999), -1999, _PyTime_ROUND_DOWN), + ((-2, 999999), -2000, _PyTime_ROUND_UP), + ((-3, 999), -2999, _PyTime_ROUND_DOWN), + ((-3, 999), -3000, _PyTime_ROUND_UP), + ((-4, 1), -3999, _PyTime_ROUND_DOWN), + ((-4, 1), -4000, _PyTime_ROUND_UP), + ((-5, 1000), -4999, _PyTime_ROUND_DOWN), + ((-5, 1000), -5000, _PyTime_ROUND_UP), + ((-6, 1000000), -5999, _PyTime_ROUND_DOWN), + ((-6, 1000000), -5999, _PyTime_ROUND_UP), + ((-7, 1000001), -6998, _PyTime_ROUND_DOWN), + ((-7, 1000001), -6999, _PyTime_ROUND_UP), + # test rounding floor + ((0, 1), 0, _PyTime_ROUND_FLOOR), + ((1, 2000003), 1002, _PyTime_ROUND_FLOOR), + ((1, 2000003), 1003, _PyTime_ROUND_UP), + ((-1, 999999999), -1, _PyTime_ROUND_FLOOR), + ): + with self.subTest(timespec=timespec, value=value, round=rnd): + self.assertEqual(pytimespec_multiply(timespec, 1000, rnd), + value) + + # dummy factory 0 + self.assertEqual(pytimespec_multiply((5, 1), 0, _PyTime_ROUND_UP), + 0) + + # test underflow + secs = (_testcapi.LONG_MIN - 1) // 1000 + pytimespec_multiply((secs + 1, 0), 1000, _PyTime_ROUND_UP) + self.assertRaises(OverflowError, + pytimespec_multiply, (secs, 0), 1000, _PyTime_ROUND_UP) + + # test overflow + secs, ms = divmod(_testcapi.LONG_MAX, 1000) + ns = ms * 10**6 + 1 + pytimespec_multiply((secs, ns), 1000, _PyTime_ROUND_DOWN) + self.assertRaises(OverflowError, + pytimespec_multiply, (secs, ns), 1000, _PyTime_ROUND_UP) + + def test_timespec_add_sub(self): + from _testcapi import pytimespec_add, pytimespec_sub + + # a+b + self.assertEqual(pytimespec_add((1, 2), (5, 7)), + (6, 9)) + self.assertEqual(pytimespec_add((5, 3), (-1, 0)), + (4, 3)) + self.assertEqual(pytimespec_add((1, 0), (0, 999999999)), + (1, 999999999)) + self.assertEqual(pytimespec_add((1, 1), (0, 999999999)), + (2, 0)) + self.assertEqual(pytimespec_add((1, 999999999), (1, 999999999)), + (3, 999999998)) + + # a-b + self.assertEqual(pytimespec_sub((5, 3), (1, 1)), + (4, 2)) + self.assertEqual(pytimespec_sub((5, 3), (-1, 0)), + (6, 3)) + self.assertEqual(pytimespec_sub((0, 0), (0, 1)), + (-1, 999999999)) + + # compute time_t min/max + + time_t_min, time_t_max = get_time_t_limits() + + # FIXME: test overflow + # # test a+b overflow + # self.assertEqual(pytimespec_add((time_t_max, 0), (-1, 0)), + # (time_t_max-1, 0)) + # self.assertEqual(pytimespec_add((-1, 0), (time_t_max, 0)), + # (time_t_max-1, 0)) + # self.assertRaises(OverflowError, + # pytimespec_add, (time_t_max, 0), (1, 0)) + self.assertRaises(OverflowError, + pytimespec_add, (time_t_max, 999999999), (0, 1)) + + # FIXME: test overflow + # # test a-b overflow + # self.assertEqual(pytimespec_sub((time_t_min, 0), (-1, 0)), + # (time_t_min+1, 0)) + # self.assertRaises(OverflowError, + # pytimespec_sub, (time_t_min, 0), (-1, 0)) + # self.assertRaises(OverflowError, + # pytimespec_sub, (time_t_min, 0), (0, 1)) + self.assertRaises(OverflowError, + pytimespec_sub, (time_t_min, 1), (0, 2)) + + def test_as_double(self): + from _testcapi import pytimespec_as_double + for timespec, seconds, loss_precision in ( + # simple timestamps + ((0, 0), 0.0, False), + ((1, 0), 1.0, False), + ((-2, 0), -2.0, False), + ((3, 500000000), 3.5, False), + ((-4, 500000000), -3.5, False), + + # test loss of precision + ((2**30, 0), 2.0 ** 30, False), + ((2**30, 1), 2.0 ** 30, True), + ((-2**30, 0), -(2.0 ** 30), False), + ((-2**30, 1), -(2.0 ** 30), True), + ): + with self.subTest(timespec=timespec, + seconds=seconds, loss_precision=loss_precision): + result = pytimespec_as_double(timespec, _PyTime_ROUND_UP) + self.assertEqual(result[0], seconds) + self.assertEqual(result[1], loss_precision) + + def test_sign(self): + from _testcapi import pytimespec_sign + for timespec, sign in ( + # negative + ((-3, 4), -1), + ((-2, 0), -1), + ((-1, 999999999), -1), + # zero + ((0, 0), 0), + # strictly positive + ((0, 1), 1), + ((2, 0), 1), + ((3, 4), 1), + ): + with self.subTest(timespec=timespec, sign=sign): + self.assertEqual(pytimespec_sign(timespec), sign) + + +class TestPytime(unittest.TestCase): @unittest.skipUnless(time._STRUCT_TM_ITEMS == 11, "needs tm_zone support") def test_localtime_timezone(self): diff -r 94d0e842b9ea -r 918faa56634f Modules/_datetimemodule.c --- a/Modules/_datetimemodule.c Fri Aug 01 12:28:49 2014 +0200 +++ b/Modules/_datetimemodule.c Fri Aug 01 22:52:18 2014 +0200 @@ -2456,13 +2456,16 @@ date_new(PyTypeObject *type, PyObject *a static PyObject * date_local_from_object(PyObject *cls, PyObject *obj) { + _PyTimeSpec ts; + time_t seconds; struct tm *tm; - time_t t; - - if (_PyTime_ObjectToTime_t(obj, &t, _PyTime_ROUND_DOWN) == -1) + + if (_PyTimeSpec_from_object(&ts, obj, _PyTime_ROUND_FLOOR) < 0) return NULL; - - tm = localtime(&t); + if (_PyTimeSpec_to_time_t(&ts, &seconds, _PyTime_ROUND_FLOOR) < 0) + return NULL; + + tm = localtime(&seconds); if (tm == NULL) { /* unconvertible time */ #ifdef EINVAL @@ -4088,12 +4091,17 @@ static PyObject * datetime_from_timestamp(PyObject *cls, TM_FUNC f, PyObject *timestamp, PyObject *tzinfo) { - time_t timet; - long us; - - if (_PyTime_ObjectToTimeval(timestamp, &timet, &us, _PyTime_ROUND_DOWN) == -1) + _PyTimeSpec ts; + time_t sec; + unsigned int us; + + if (_PyTimeSpec_from_object(&ts, timestamp, _PyTime_ROUND_FLOOR) < 0) return NULL; - return datetime_from_timet_and_us(cls, f, timet, (int)us, tzinfo); + if (_PyTimeSpec_divide(&ts, 1000 * 1000, + &sec, &us, _PyTime_ROUND_FLOOR) < 0) + return NULL; + + return datetime_from_timet_and_us(cls, f, sec, (int)us, tzinfo); } /* Internal helper. @@ -4103,9 +4111,15 @@ 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; + time_t sec; + unsigned int us; + _PyTimeSpec_get_time(&ts); + if (_PyTimeSpec_divide(&ts, 1000 * 1000, + &sec, &us, _PyTime_ROUND_FLOOR) < 0) + return NULL; + return datetime_from_timet_and_us(cls, f, + sec, (int)us, tzinfo); } diff -r 94d0e842b9ea -r 918faa56634f Modules/_ssl.c --- a/Modules/_ssl.c Fri Aug 01 12:28:49 2014 +0200 +++ b/Modules/_ssl.c Fri Aug 01 22:52:18 2014 +0200 @@ -518,7 +518,7 @@ newPySSLSocket(PySSLContext *sslctx, PyS /* If the socket is in non-blocking mode or timeout mode, set the BIO * to non-blocking mode (blocking is the default) */ - if (sock->sock_timeout >= 0.0) { + if (sock->sock_timeout.tv_sec >= 0) { BIO_set_nbio(SSL_get_rbio(self->ssl), 1); BIO_set_nbio(SSL_get_wbio(self->ssl), 1); } @@ -557,7 +557,7 @@ static PyObject *PySSL_SSLdo_handshake(P Py_INCREF(sock); /* just in case the blocking state of the socket has been changed */ - nonblocking = (sock->sock_timeout >= 0.0); + nonblocking = (_PyTimeSpec_sign(&sock->sock_timeout) >= 0); BIO_set_nbio(SSL_get_rbio(self->ssl), nonblocking); BIO_set_nbio(SSL_get_wbio(self->ssl), nonblocking); @@ -1496,9 +1496,9 @@ check_socket_and_wait_for_timeout(PySock int rc; /* Nothing to do unless we're in timeout mode (not non-blocking) */ - if (s->sock_timeout < 0.0) + if (s->sock_timeout.tv_sec < 0) return SOCKET_IS_BLOCKING; - else if (s->sock_timeout == 0.0) + else if (_PyTimeSpec_sign(&s->sock_timeout) == 0) return SOCKET_IS_NONBLOCKING; /* Guard against closed socket */ @@ -1510,15 +1510,20 @@ check_socket_and_wait_for_timeout(PySock #ifdef HAVE_POLL { struct pollfd pollfd; - int timeout; + long ms; + int err; pollfd.fd = s->sock_fd; pollfd.events = writing ? POLLOUT : POLLIN; /* s->sock_timeout is in seconds, timeout in ms */ - timeout = (int)(s->sock_timeout * 1000 + 0.5); + rc = _PyTimeSpec_multiply(&s->sock_timeout, 1000, + &ms, _PyTime_ROUND_UP); + /* sock_parse_timeout() already checked for overflow */ + assert(!rc && 0 <= ms && ms <= INT_MAX); + PySSL_BEGIN_ALLOW_THREADS - rc = poll(&pollfd, 1, timeout); + rc = poll(&pollfd, 1, (int)ms); PySSL_END_ALLOW_THREADS goto normal_return; @@ -1530,8 +1535,10 @@ check_socket_and_wait_for_timeout(PySock return SOCKET_TOO_LARGE_FOR_SELECT; /* Construct the arguments to select */ - tv.tv_sec = (int)s->sock_timeout; - tv.tv_usec = (int)((s->sock_timeout - tv.tv_sec) * 1e6); + rc = _PyTimeSpec_to_timeval(&s->sock_timeout, &tv, _PyTime_ROUND_UP); + /* sock_parse_timeout() already checked for overflow */ + assert(!rc); + FD_ZERO(&fds); FD_SET(s->sock_fd, &fds); @@ -1582,7 +1589,7 @@ static PyObject *PySSL_SSLwrite(PySSLSoc } /* just in case the blocking state of the socket has been changed */ - nonblocking = (sock->sock_timeout >= 0.0); + nonblocking = (_PyTimeSpec_sign(&sock->sock_timeout) >= 0); BIO_set_nbio(SSL_get_rbio(self->ssl), nonblocking); BIO_set_nbio(SSL_get_wbio(self->ssl), nonblocking); @@ -1711,7 +1718,7 @@ static PyObject *PySSL_SSLread(PySSLSock } /* just in case the blocking state of the socket has been changed */ - nonblocking = (sock->sock_timeout >= 0.0); + nonblocking = (_PyTimeSpec_sign(&sock->sock_timeout) >= 0); BIO_set_nbio(SSL_get_rbio(self->ssl), nonblocking); BIO_set_nbio(SSL_get_wbio(self->ssl), nonblocking); @@ -1809,7 +1816,7 @@ static PyObject *PySSL_SSLshutdown(PySSL Py_INCREF(sock); /* Just in case the blocking state of the socket has been changed */ - nonblocking = (sock->sock_timeout >= 0.0); + nonblocking = (_PyTimeSpec_sign(&sock->sock_timeout) >= 0); BIO_set_nbio(SSL_get_rbio(self->ssl), nonblocking); BIO_set_nbio(SSL_get_wbio(self->ssl), nonblocking); diff -r 94d0e842b9ea -r 918faa56634f Modules/_testcapimodule.c --- a/Modules/_testcapimodule.c Fri Aug 01 12:28:49 2014 +0200 +++ b/Modules/_testcapimodule.c Fri Aug 01 22:52:18 2014 +0200 @@ -2520,58 +2520,242 @@ run_in_subinterp(PyObject *self, PyObjec static int check_time_rounding(int round) { - if (round != _PyTime_ROUND_DOWN && round != _PyTime_ROUND_UP) { + switch(round) + { + case _PyTime_ROUND_DOWN: + case _PyTime_ROUND_UP: + case _PyTime_ROUND_FLOOR: + return 0; + default: PyErr_SetString(PyExc_ValueError, "invalid rounding"); return -1; } +} + +static int +pytimespec_parse(_PyTimeSpec *ts, PyObject *obj) +{ + ts->tv_sec = _PyLong_AsTime_t(obj); + if (ts->tv_sec == (time_t)-1 && PyErr_Occurred()) + return -1; + if (ts->tv_nsec < 0 || 999999999 < ts->tv_nsec) { + PyErr_SetString(PyExc_ValueError, "ns must be in range [0, 999999999]"); + return -1; + } return 0; } static PyObject * -test_pytime_object_to_time_t(PyObject *self, PyObject *args) +test_pytimespec_to_time_t(PyObject *self, PyObject *args) { - PyObject *obj; + PyObject *secobj; + _PyTimeSpec ts; + int round; time_t sec; - int round; - if (!PyArg_ParseTuple(args, "Oi:pytime_object_to_time_t", &obj, &round)) + + if (!PyArg_ParseTuple(args, "(O!l)i", + &PyLong_Type, &secobj, &ts.tv_nsec, &round)) + return NULL; + if (pytimespec_parse(&ts, secobj) < 0) return NULL; if (check_time_rounding(round) < 0) return NULL; - if (_PyTime_ObjectToTime_t(obj, &sec, round) == -1) + if (_PyTimeSpec_to_time_t(&ts, &sec , round) < 0) return NULL; - return _PyLong_FromTime_t(sec); + return Py_BuildValue("N", _PyLong_FromTime_t(sec)); } static PyObject * -test_pytime_object_to_timeval(PyObject *self, PyObject *args) +test_pytimespec_as_double(PyObject *self, PyObject *args) { - PyObject *obj; - time_t sec; - long usec; + PyObject *secobj; + _PyTimeSpec ts; int round; - if (!PyArg_ParseTuple(args, "Oi:pytime_object_to_timeval", &obj, &round)) + double seconds; + int loss_precision; + + if (!PyArg_ParseTuple(args, "(O!l)i", + &PyLong_Type, &secobj, &ts.tv_nsec, &round)) + return NULL; + if (pytimespec_parse(&ts, secobj) < 0) return NULL; if (check_time_rounding(round) < 0) return NULL; - if (_PyTime_ObjectToTimeval(obj, &sec, &usec, round) == -1) + loss_precision = (_PyTimeSpec_as_double(&ts, &seconds, round) < 0); + return Py_BuildValue("(dN)", seconds, PyBool_FromLong(loss_precision)); +} + +static PyObject * +test_pytimespec_sign(PyObject *self, PyObject *args) +{ + PyObject *secobj; + _PyTimeSpec ts; + + if (!PyArg_ParseTuple(args, "(O!l)", + &PyLong_Type, &secobj, &ts.tv_nsec)) return NULL; - return Py_BuildValue("Nl", _PyLong_FromTime_t(sec), usec); + if (pytimespec_parse(&ts, secobj) < 0) + return NULL; + return Py_BuildValue("i", _PyTimeSpec_sign(&ts)); } static PyObject * -test_pytime_object_to_timespec(PyObject *self, PyObject *args) +test_pytimespec_divide(PyObject *self, PyObject *args) { - PyObject *obj; + PyObject *secobj; + _PyTimeSpec ts; + int denominator; + int round; time_t sec; - long nsec; + unsigned int numerator; + + if (!PyArg_ParseTuple(args, "(O!l)ii", + &PyLong_Type, &secobj, &ts.tv_nsec, + &denominator, &round)) + return NULL; + if (pytimespec_parse(&ts, secobj) < 0) + return NULL; + if (denominator < 0) { + PyErr_SetString(PyExc_ValueError, "denominator must be >= 1"); + return 0; + } + if (check_time_rounding(round) < 0) + return NULL; + if (_PyTimeSpec_divide(&ts, (unsigned int)denominator, + &sec, &numerator, round) < 0) + return NULL; + return Py_BuildValue("NI", _PyLong_FromTime_t(sec), numerator); +} + +static PyObject* +_PyTimeSpec_to_object(_PyTimeSpec *ts) +{ + PyObject *sec; + sec = _PyLong_FromTime_t(ts->tv_sec); + if (sec == NULL) + return NULL; + return Py_BuildValue("(Ni)", sec, ts->tv_nsec); +} + +static PyObject* +test_pytimespec_from_double(PyObject *self, PyObject *args) +{ + double secs; int round; - if (!PyArg_ParseTuple(args, "Oi:pytime_object_to_timespec", &obj, &round)) + _PyTimeSpec ts; + if (!PyArg_ParseTuple(args, "di", &secs, &round)) return NULL; if (check_time_rounding(round) < 0) return NULL; - if (_PyTime_ObjectToTimespec(obj, &sec, &nsec, round) == -1) + if (_PyTimeSpec_from_double(&ts, secs, round) < 0) { + PyErr_SetString(PyExc_OverflowError, "underflow or overflow"); return NULL; - return Py_BuildValue("Nl", _PyLong_FromTime_t(sec), nsec); + } + return _PyTimeSpec_to_object(&ts); +} + +static PyObject* +test_pytimespec_from_object(PyObject *self, PyObject *args) +{ + PyObject *obj; + int round; + _PyTimeSpec ts; + if (!PyArg_ParseTuple(args, "Oi", &obj, &round)) + return NULL; + if (check_time_rounding(round) < 0) + return NULL; + if (_PyTimeSpec_from_object(&ts, obj, round) < 0) + return NULL; + return _PyTimeSpec_to_object(&ts); +} + +static int +pytimespec_parse2(PyObject *args, + _PyTimeSpec *ts1, _PyTimeSpec *ts2) +{ + PyObject *secs1, *secs2; + + if (!PyArg_ParseTuple(args, "(O!l)(O!l)", + &PyLong_Type, &secs1, &ts1->tv_nsec, + &PyLong_Type, &secs2, &ts2->tv_nsec)) + return -1; + + if (pytimespec_parse(ts1, secs1) < 0) + return -1; + if (pytimespec_parse(ts2, secs2) < 0) + return -1; + + return 0; +} + +static PyObject* +pytimespec_overflow(_PyTimeSpec *ts) +{ + if (ts->tv_sec < 0) + PyErr_SetString(PyExc_OverflowError, "underflow"); + else + PyErr_SetString(PyExc_OverflowError, "overflow"); + return NULL; +} + +static PyObject* +test_pytimespec_add(PyObject *self, PyObject *args) +{ + _PyTimeSpec res, ts1, ts2; + + if (pytimespec_parse2(args, &ts1, &ts2) < 0) + return NULL; + + if (_PyTimeSpec_add(&res, &ts1, &ts2) < 0) + return pytimespec_overflow(&res); + + return _PyTimeSpec_to_object(&res); +} + +static PyObject* +test_pytimespec_sub(PyObject *self, PyObject *args) +{ + _PyTimeSpec res, end, start; + + if (pytimespec_parse2(args, &end, &start) < 0) + return NULL; + + if (_PyTimeSpec_sub(&res, &end, &start) < 0) + return pytimespec_overflow(&res); + + return _PyTimeSpec_to_object(&res); +} + +static PyObject* +test_pytimespec_multiply(PyObject *self, PyObject *args) +{ + _PyTimeSpec ts; + PyObject *sec; + long factor; + int round; + long value; + + if (!PyArg_ParseTuple(args, "(O!l)li", + &PyLong_Type, &sec, &ts.tv_nsec, &factor, &round)) + return NULL; + + if (pytimespec_parse(&ts, sec) < 0) + return NULL; + if (factor < 0) { + PyErr_SetString(PyExc_ValueError, "factor must be >= 0"); + return NULL; + } + if (check_time_rounding(round) < 0) + return NULL; + + if (_PyTimeSpec_multiply(&ts, factor, &value, round) < 0) { + if (value < 0) + PyErr_SetString(PyExc_OverflowError, "underflow"); + else + PyErr_SetString(PyExc_OverflowError, "overflow"); + return NULL; + } + return PyLong_FromLong(value); } static void @@ -3182,9 +3366,15 @@ static PyMethodDef TestMethods[] = { METH_NOARGS}, {"crash_no_current_thread", (PyCFunction)crash_no_current_thread, METH_NOARGS}, {"run_in_subinterp", run_in_subinterp, METH_VARARGS}, - {"pytime_object_to_time_t", test_pytime_object_to_time_t, METH_VARARGS}, - {"pytime_object_to_timeval", test_pytime_object_to_timeval, METH_VARARGS}, - {"pytime_object_to_timespec", test_pytime_object_to_timespec, METH_VARARGS}, + {"pytimespec_from_double", test_pytimespec_from_double, METH_VARARGS}, + {"pytimespec_from_object", test_pytimespec_from_object, METH_VARARGS}, + {"pytimespec_add", test_pytimespec_add, METH_VARARGS}, + {"pytimespec_sub", test_pytimespec_sub, METH_VARARGS}, + {"pytimespec_multiply", test_pytimespec_multiply, METH_VARARGS}, + {"pytimespec_divide", test_pytimespec_divide, METH_VARARGS}, + {"pytimespec_to_time_t", test_pytimespec_to_time_t, METH_VARARGS}, + {"pytimespec_as_double", test_pytimespec_as_double, METH_VARARGS}, + {"pytimespec_sign", test_pytimespec_sign, METH_VARARGS}, {"with_tp_del", with_tp_del, METH_VARARGS}, {"test_pymem_alloc0", (PyCFunction)test_pymem_alloc0, METH_NOARGS}, diff -r 94d0e842b9ea -r 918faa56634f Modules/_threadmodule.c --- a/Modules/_threadmodule.c Fri Aug 01 12:28:49 2014 +0200 +++ b/Modules/_threadmodule.c Fri Aug 01 22:52:18 2014 +0200 @@ -49,19 +49,25 @@ lock_dealloc(lockobject *self) * timeout. */ static PyLockStatus -acquire_timed(PyThread_type_lock lock, PY_TIMEOUT_T microseconds) +acquire_timed(PyThread_type_lock lock, const _PyTimeSpec *timeout) { PyLockStatus r; - _PyTime_timeval curtime; - _PyTime_timeval endtime; + _PyTimeSpec endtime; + long microseconds; + if (_PyTimeSpec_sign(timeout) > 0) { + int err = _PyTimeSpec_multiply(timeout, 1000 * 1000, + µseconds, _PyTime_ROUND_UP); + /* the caller already checked for overflow */ + assert(!err); - if (microseconds > 0) { - _PyTime_gettimeofday(&endtime); - endtime.tv_sec += microseconds / (1000 * 1000); - endtime.tv_usec += microseconds % (1000 * 1000); + _PyTimeSpec_get_time(&endtime); + _PyTimeSpec_add(&endtime, &endtime, timeout); } - + else if (_PyTimeSpec_sign(timeout) == 0) + microseconds = 0; + else + microseconds = -1; do { /* first a simple non-blocking try without releasing the GIL */ @@ -72,71 +78,101 @@ acquire_timed(PyThread_type_lock lock, P Py_END_ALLOW_THREADS } - if (r == PY_LOCK_INTR) { - /* Run signal handlers if we were interrupted. Propagate - * exceptions from signal handlers, such as KeyboardInterrupt, by - * passing up PY_LOCK_INTR. */ - if (Py_MakePendingCalls() < 0) { - return PY_LOCK_INTR; + if (r != PY_LOCK_INTR) + break; + + /* Run signal handlers if we were interrupted. Propagate + * exceptions from signal handlers, such as KeyboardInterrupt, by + * passing up PY_LOCK_INTR. */ + if (Py_MakePendingCalls() < 0) { + return PY_LOCK_INTR; + } + + /* If we're using a timeout, recompute the timeout after processing + * signals, since those can take time. */ + if (microseconds > 0) { + _PyTimeSpec now, ts; + + _PyTimeSpec_get_time(&now); + _PyTimeSpec_sub(&ts, &endtime, &now); + _PyTimeSpec_multiply(&ts, 1000 * 1000, + µseconds, _PyTime_ROUND_UP); + + /* Check for negative values, since those mean block forever. + */ + if (microseconds <= 0) { + r = PY_LOCK_FAILURE; + } + } + } while (1); + + return r; +} + +static int +lock_acquire_parse_args(PyObject *args, PyObject *kwds, + int *blocking_p, _PyTimeSpec *timeout) +{ + char *kwlist[] = {"blocking", "timeout", NULL}; + int blocking = 1; + PyObject *timeoutobj = NULL; + + if (!PyArg_ParseTupleAndKeywords(args, kwds, "|iO:acquire", kwlist, + &blocking, &timeoutobj)) + return -1; + + if (timeoutobj != NULL) { + if (_PyTimeSpec_from_object(timeout, timeoutobj, _PyTime_ROUND_UP) < 0) + return -1; + + if (!(timeout->tv_sec == -1 && timeout->tv_nsec == 0)) { + if (!blocking) { + PyErr_SetString(PyExc_ValueError, "can't specify a timeout " + "for a non-blocking call"); + return -1; } - /* 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)); - - /* Check for negative values, since those mean block forever. - */ - if (microseconds <= 0) { - r = PY_LOCK_FAILURE; - } + if (_PyTimeSpec_sign(timeout) < 0) { + PyErr_SetString(PyExc_ValueError, "timeout value must be " + "strictly positive"); + return -1; } } - } while (r == PY_LOCK_INTR); /* Retry if we were interrupted. */ + } + else { + timeout->tv_sec = -1; + timeout->tv_nsec = 0; + } - return r; + if (!blocking) { + timeout->tv_sec = 0; + timeout->tv_nsec = 0; + } + else { + long microseconds; + + if (_PyTimeSpec_multiply(timeout, 1000 * 1000, + µseconds, _PyTime_ROUND_UP) < 0) { + PyErr_SetString(PyExc_OverflowError, + "timeout value is too large"); + return -1; + } + } + *blocking_p = blocking; + return 0; } static PyObject * lock_PyThread_acquire_lock(lockobject *self, PyObject *args, PyObject *kwds) { - char *kwlist[] = {"blocking", "timeout", NULL}; - int blocking = 1; - double timeout = -1; - PY_TIMEOUT_T microseconds; + int blocking; + _PyTimeSpec timeout; PyLockStatus r; - if (!PyArg_ParseTupleAndKeywords(args, kwds, "|id:acquire", kwlist, - &blocking, &timeout)) + if (lock_acquire_parse_args(args, kwds, &blocking, &timeout) < 0) return NULL; - if (!blocking && timeout != -1) { - PyErr_SetString(PyExc_ValueError, "can't specify a timeout " - "for a non-blocking call"); - return NULL; - } - if (timeout < 0 && timeout != -1) { - PyErr_SetString(PyExc_ValueError, "timeout value must be " - "strictly positive"); - return NULL; - } - if (!blocking) - microseconds = 0; - else if (timeout == -1) - microseconds = -1; - else { - timeout *= 1e6; - if (timeout >= (double) PY_TIMEOUT_MAX) { - PyErr_SetString(PyExc_OverflowError, - "timeout value is too large"); - return NULL; - } - microseconds = (PY_TIMEOUT_T) timeout; - } - - r = acquire_timed(self->lock_lock, microseconds); + r = acquire_timed(self->lock_lock, &timeout); if (r == PY_LOCK_INTR) { return NULL; } @@ -281,41 +317,14 @@ rlock_dealloc(rlockobject *self) static PyObject * rlock_acquire(rlockobject *self, PyObject *args, PyObject *kwds) { - char *kwlist[] = {"blocking", "timeout", NULL}; - int blocking = 1; - double timeout = -1; - PY_TIMEOUT_T microseconds; + int blocking; + _PyTimeSpec timeout; long tid; - PyLockStatus r = PY_LOCK_ACQUIRED; + PyLockStatus r; - if (!PyArg_ParseTupleAndKeywords(args, kwds, "|id:acquire", kwlist, - &blocking, &timeout)) + if (lock_acquire_parse_args(args, kwds, &blocking, &timeout) < 0) return NULL; - if (!blocking && timeout != -1) { - PyErr_SetString(PyExc_ValueError, "can't specify a timeout " - "for a non-blocking call"); - return NULL; - } - if (timeout < 0 && timeout != -1) { - PyErr_SetString(PyExc_ValueError, "timeout value must be " - "strictly positive"); - return NULL; - } - if (!blocking) - microseconds = 0; - else if (timeout == -1) - microseconds = -1; - else { - timeout *= 1e6; - if (timeout >= (double) PY_TIMEOUT_MAX) { - PyErr_SetString(PyExc_OverflowError, - "timeout value is too large"); - return NULL; - } - microseconds = (PY_TIMEOUT_T) timeout; - } - tid = PyThread_get_thread_ident(); if (self->rlock_count > 0 && tid == self->rlock_owner) { unsigned long count = self->rlock_count + 1; @@ -327,7 +336,7 @@ rlock_acquire(rlockobject *self, PyObjec self->rlock_count = count; Py_RETURN_TRUE; } - r = acquire_timed(self->rlock_lock, microseconds); + r = acquire_timed(self->rlock_lock, &timeout); if (r == PY_LOCK_ACQUIRED) { assert(self->rlock_count == 0); self->rlock_owner = tid; diff -r 94d0e842b9ea -r 918faa56634f Modules/gcmodule.c --- a/Modules/gcmodule.c Fri Aug 01 12:28:49 2014 +0200 +++ b/Modules/gcmodule.c Fri Aug 01 22:52:18 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,9 @@ 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, dt; + double seconds; + _PyTimeSpec_get_time(&t2); if (m == 0 && n == 0) PySys_WriteStderr("gc: done"); @@ -1033,7 +1034,9 @@ 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)); + _PyTimeSpec_sub(&dt, &t2, &t1); + _PyTimeSpec_as_double(&dt, &seconds, _PyTime_ROUND_DOWN); + PySys_WriteStderr(", %.4fs elapsed\n", seconds); } /* Append instances in the uncollectable set to a Python diff -r 94d0e842b9ea -r 918faa56634f Modules/posixmodule.c --- a/Modules/posixmodule.c Fri Aug 01 12:28:49 2014 +0200 +++ b/Modules/posixmodule.c Fri Aug 01 22:52:18 2014 +0200 @@ -4880,8 +4880,8 @@ posix_utime(PyObject *self, PyObject *ar } if (times && (times != Py_None)) { - time_t a_sec, m_sec; - long a_nsec, m_nsec; + _PyTimeSpec ts; + if (!PyTuple_CheckExact(times) || (PyTuple_Size(times) != 2)) { PyErr_SetString(PyExc_TypeError, "utime: 'times' must be either" @@ -4889,16 +4889,18 @@ posix_utime(PyObject *self, PyObject *ar goto exit; } utime.now = 0; - if (_PyTime_ObjectToTimespec(PyTuple_GET_ITEM(times, 0), - &a_sec, &a_nsec, _PyTime_ROUND_DOWN) == -1 || - _PyTime_ObjectToTimespec(PyTuple_GET_ITEM(times, 1), - &m_sec, &m_nsec, _PyTime_ROUND_DOWN) == -1) { + + if (_PyTimeSpec_from_object(&ts, PyTuple_GET_ITEM(times, 0), + _PyTime_ROUND_DOWN) < 0) goto exit; - } - utime.atime_s = a_sec; - utime.atime_ns = a_nsec; - utime.mtime_s = m_sec; - utime.mtime_ns = m_nsec; + utime.atime_s = ts.tv_sec; + utime.atime_ns = ts.tv_nsec; + + if (_PyTimeSpec_from_object(&ts, PyTuple_GET_ITEM(times, 1), + _PyTime_ROUND_DOWN) < 0) + goto exit; + utime.mtime_s = ts.tv_sec; + utime.mtime_ns = ts.tv_nsec; } else if (ns) { if (!PyTuple_CheckExact(ns) || (PyTuple_Size(ns) != 2)) { diff -r 94d0e842b9ea -r 918faa56634f Modules/selectmodule.c --- a/Modules/selectmodule.c Fri Aug 01 12:28:49 2014 +0200 +++ b/Modules/selectmodule.c Fri Aug 01 22:52:18 2014 +0200 @@ -212,32 +212,13 @@ select_select(PyObject *self, PyObject * return NULL; } else { - /* On OpenBSD 5.4, timeval.tv_sec is a long. - * Example: long is 64-bit, whereas time_t is 32-bit. */ - time_t sec; - /* On OS X 64-bit, timeval.tv_usec is an int (and thus still 4 - bytes as required), but no longer defined by a long. */ - long usec; - if (_PyTime_ObjectToTimeval(tout, &sec, &usec, - _PyTime_ROUND_UP) == -1) + _PyTimeSpec ts; + + if (_PyTimeSpec_from_object(&ts, tout, _PyTime_ROUND_UP) < 0) return NULL; -#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 (sec > LONG_MAX) { - PyErr_SetString(PyExc_OverflowError, - "timeout is too large"); + if (_PyTimeSpec_to_timeval(&ts, &tv, _PyTime_ROUND_UP) < 0) return NULL; - } -#endif - tv.tv_sec = (long)sec; -#else - assert(sizeof(tv.tv_sec) >= sizeof(sec)); - tv.tv_sec = sec; -#endif - tv.tv_usec = usec; + if (tv.tv_sec < 0) { PyErr_SetString(PyExc_ValueError, "timeout must be non-negative"); return NULL; @@ -2042,9 +2023,12 @@ kqueue_queue_control(kqueue_queue_Object ptimeoutspec = NULL; } else if (PyNumber_Check(otimeout)) { - if (_PyTime_ObjectToTimespec(otimeout, &timeout.tv_sec, - &timeout.tv_nsec, _PyTime_ROUND_UP) == -1) + _PyTimeSpec ts; + + if (_PyTimeSpec_from_object(&ts, otimeout, _PyTime_ROUND_UP) < 0) return NULL; + timeout.tv_sec = ts.tv_sec; + timeout.tv_nsec = ts.tv_nsec; if (timeout.tv_sec < 0) { PyErr_SetString(PyExc_ValueError, diff -r 94d0e842b9ea -r 918faa56634f Modules/signalmodule.c --- a/Modules/signalmodule.c Fri Aug 01 12:28:49 2014 +0200 +++ b/Modules/signalmodule.c Fri Aug 01 22:52:18 2014 +0200 @@ -946,21 +946,19 @@ signal_sigtimedwait(PyObject *self, PyOb struct timespec buf; sigset_t set; siginfo_t si; - time_t tv_sec; - long tv_nsec; + _PyTimeSpec ts; int err; if (!PyArg_ParseTuple(args, "OO:sigtimedwait", &signals, &timeout)) return NULL; - if (_PyTime_ObjectToTimespec(timeout, &tv_sec, &tv_nsec, - _PyTime_ROUND_DOWN) == -1) + if (_PyTimeSpec_from_object(&ts, timeout, _PyTime_ROUND_DOWN) < 0) return NULL; - buf.tv_sec = tv_sec; - buf.tv_nsec = tv_nsec; + buf.tv_sec = ts.tv_sec; + buf.tv_nsec = ts.tv_nsec; - if (buf.tv_sec < 0 || buf.tv_nsec < 0) { + if (buf.tv_sec < 0) { PyErr_SetString(PyExc_ValueError, "timeout must be non-negative"); return NULL; } diff -r 94d0e842b9ea -r 918faa56634f Modules/socketmodule.c --- a/Modules/socketmodule.c Fri Aug 01 12:28:49 2014 +0200 +++ b/Modules/socketmodule.c Fri Aug 01 22:52:18 2014 +0200 @@ -448,6 +448,9 @@ static PyTypeObject sock_type; #define SOCKLEN_T_LIMIT INT_MAX #endif +/* True if timeout > 0.0 */ +#define HAS_TIMEOUT(s) (_PyTimeSpec_sign(&(s)->sock_timeout) > 0) + #ifdef HAVE_POLL /* Instead of select(), we'll use poll() since poll() works on any fd. */ #define IS_SELECTABLE(s) 1 @@ -455,7 +458,7 @@ static PyTypeObject sock_type; #else /* If there's no timeout left, we don't have to call select, so it's a safe, * little white lie. */ -#define IS_SELECTABLE(s) (_PyIsSelectable_fd((s)->sock_fd) || (s)->sock_timeout <= 0.0) +#define IS_SELECTABLE(s) (_PyIsSelectable_fd((s)->sock_fd) || !HAS_TIMEOUT(s)) #endif static PyObject* @@ -592,12 +595,12 @@ internal_setblocking(PySocketSockObject after they've reacquired the interpreter lock. Returns 1 on timeout, -1 on error, 0 otherwise. */ static int -internal_select_ex(PySocketSockObject *s, int writing, double interval) +internal_select_ex(PySocketSockObject *s, int writing, const _PyTimeSpec *interval) { int n; /* Nothing to do unless we're in timeout mode (not non-blocking) */ - if (s->sock_timeout <= 0.0) + if (!HAS_TIMEOUT(s)) return 0; /* Guard against closed socket */ @@ -605,7 +608,7 @@ internal_select_ex(PySocketSockObject *s return 0; /* Handling this condition here simplifies the select loops */ - if (interval < 0.0) + if (interval->tv_sec < 0.0) return 1; /* Prefer poll, if available, since you can poll() any fd @@ -613,22 +616,31 @@ internal_select_ex(PySocketSockObject *s #ifdef HAVE_POLL { struct pollfd pollfd; - int timeout; + long ms; + int err; pollfd.fd = s->sock_fd; pollfd.events = writing ? POLLOUT : POLLIN; - /* s->sock_timeout is in seconds, timeout in ms */ - timeout = (int)(interval * 1000 + 0.5); - n = poll(&pollfd, 1, timeout); + /* timeout is in millisecond, round away from zero */ + err = _PyTimeSpec_multiply(interval, 1000, + &ms, _PyTime_ROUND_UP); + /* sock_parse_timeout() already checked for overflow */ + assert(!err && ms <= INT_MAX); + + n = poll(&pollfd, 1, (int)ms); } #else { /* Construct the arguments to select */ fd_set fds; struct timeval tv; - tv.tv_sec = (int)interval; - tv.tv_usec = (int)((interval - tv.tv_sec) * 1e6); + int err; + + err = _PyTimeSpec_to_timeval(interval, &tv, _PyTime_ROUND_UP); + /* sock_parse_timeout() already checked for overflow */ + assert(!err && ms <= INT_MAX); + FD_ZERO(&fds); FD_SET(s->sock_fd, &fds); @@ -652,7 +664,7 @@ internal_select_ex(PySocketSockObject *s static int internal_select(PySocketSockObject *s, int writing) { - return internal_select_ex(s, writing, s->sock_timeout); + return internal_select_ex(s, writing, &s->sock_timeout); } /* @@ -676,13 +688,12 @@ internal_select(PySocketSockObject *s, i #define BEGIN_SELECT_LOOP(s) \ { \ - _PyTime_timeval now, deadline = {0, 0}; \ - double interval = s->sock_timeout; \ - int has_timeout = s->sock_timeout > 0.0; \ + _PyTimeSpec now, deadline = {0, 0}, interval; \ + int has_timeout = HAS_TIMEOUT(s); \ + interval = s->sock_timeout; \ if (has_timeout) { \ - _PyTime_gettimeofday(&now); \ - deadline = now; \ - _PyTime_ADD_SECONDS(deadline, s->sock_timeout); \ + _PyTimeSpec_get_time(&deadline); \ + _PyTimeSpec_add(&deadline, &deadline, &interval); \ } \ while (1) { \ errno = 0; \ @@ -691,14 +702,15 @@ 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); \ + _PyTimeSpec_sub(&interval, &deadline, &now); \ } \ } \ /* Initialize a new socket object. */ -static double defaulttimeout = -1.0; /* Default timeout for new sockets */ +/* Default timeout for new sockets */ +static _PyTimeSpec defaulttimeout = {-1, 0}; static void init_sockobject(PySocketSockObject *s, @@ -711,13 +723,15 @@ init_sockobject(PySocketSockObject *s, s->errorhandler = &set_error; #ifdef SOCK_NONBLOCK - if (type & SOCK_NONBLOCK) - s->sock_timeout = 0.0; + if (type & SOCK_NONBLOCK) { + s->sock_timeout.tv_sec = 0; + s->sock_timeout.tv_nsec = 0; + } else #endif { s->sock_timeout = defaulttimeout; - if (defaulttimeout >= 0.0) + if (_PyTimeSpec_sign(&s->sock_timeout) >= 0) internal_setblocking(s, 0); } @@ -1981,7 +1995,7 @@ sock_accept(PySocketSockObject *s) BEGIN_SELECT_LOOP(s) Py_BEGIN_ALLOW_THREADS - timeout = internal_select_ex(s, 0, interval); + timeout = internal_select_ex(s, 0, &interval); if (!timeout) { #if defined(HAVE_ACCEPT4) && defined(SOCK_CLOEXEC) if (accept4_works != 0) { @@ -2068,7 +2082,8 @@ sock_setblocking(PySocketSockObject *s, if (block == -1 && PyErr_Occurred()) return NULL; - s->sock_timeout = block ? -1.0 : 0.0; + s->sock_timeout.tv_sec = block ? -1 : 0; + s->sock_timeout.tv_nsec = 0; internal_setblocking(s, block); Py_INCREF(Py_None); @@ -2082,6 +2097,43 @@ Set the socket to blocking (flag is true setblocking(True) is equivalent to settimeout(None);\n\ setblocking(False) is equivalent to settimeout(0.0)."); +static int +sock_parse_timeout(_PyTimeSpec *ts, PyObject *arg) +{ + struct timeval tv; + long ms; + + if (arg == Py_None) { + ts->tv_sec = -1; + ts->tv_nsec = 0; + return 0; + } + + if (_PyTimeSpec_from_object(ts, arg, _PyTime_ROUND_UP) < 0) + return -1; + + if (ts->tv_sec < 0) { + ts->tv_sec = -1; + ts->tv_nsec = 0; + return 0; + } + + /* timeout in milliseconds must fit in an int */ + if (_PyTimeSpec_multiply(ts, 1000, + &ms, _PyTime_ROUND_UP) < 0) + return -1; + if (ms > INT_MAX) + goto overflow; + + /* timeout must fit in a timeval structure */ + if (_PyTimeSpec_to_timeval(ts, &tv, _PyTime_ROUND_UP) < 0) + goto overflow; + return 0; + +overflow: + return -1; +} + /* s.settimeout(timeout) method. Argument: None -- no timeout, blocking mode; same as setblocking(True) 0.0 -- non-blocking mode; same as setblocking(False) @@ -2091,22 +2143,13 @@ setblocking(False) is equivalent to sett static PyObject * sock_settimeout(PySocketSockObject *s, PyObject *arg) { - double timeout; - - if (arg == Py_None) - timeout = -1.0; - else { - timeout = PyFloat_AsDouble(arg); - if (timeout < 0.0) { - if (!PyErr_Occurred()) - PyErr_SetString(PyExc_ValueError, - "Timeout value out of range"); - return NULL; - } - } - - s->sock_timeout = timeout; - internal_setblocking(s, timeout < 0.0); + _PyTimeSpec ts; + + if (sock_parse_timeout(&ts, arg) < 0) + return NULL; + s->sock_timeout = ts; + + internal_setblocking(s, s->sock_timeout.tv_sec < 0); Py_INCREF(Py_None); return Py_None; @@ -2125,12 +2168,15 @@ Setting a timeout of zero is the same as static PyObject * sock_gettimeout(PySocketSockObject *s) { - if (s->sock_timeout < 0.0) { + if (s->sock_timeout.tv_sec < 0) { Py_INCREF(Py_None); return Py_None; } - else - return PyFloat_FromDouble(s->sock_timeout); + else { + double timeout; + _PyTimeSpec_as_double(&s->sock_timeout, &timeout, _PyTime_ROUND_UP); + return PyFloat_FromDouble(timeout); + } } PyDoc_STRVAR(gettimeout_doc, @@ -2311,19 +2357,24 @@ internal_connect(PySocketSockObject *s, #ifdef MS_WINDOWS - if (s->sock_timeout > 0.0) { + if (HAS_TIMEOUT(s->sock_timeout)) { if (res < 0 && WSAGetLastError() == WSAEWOULDBLOCK && IS_SELECTABLE(s)) { /* This is a mess. Best solution: trust select */ fd_set fds; fd_set fds_exc; struct timeval tv; - tv.tv_sec = (int)s->sock_timeout; - tv.tv_usec = (int)((s->sock_timeout - tv.tv_sec) * 1e6); + int err; + + /* sock_parse_timeout() already checked for overflow */ + err = _PyTimeSpec_to_timeval(&s->sock_timeout, + &tv, _PyTime_ROUND_UP); + assert(!err); FD_ZERO(&fds); + FD_ZERO(&fds_exc); FD_SET(s->sock_fd, &fds); - FD_ZERO(&fds_exc); FD_SET(s->sock_fd, &fds_exc); + res = select(Py_SAFE_DOWNCAST(s->sock_fd+1, SOCKET_T, int), NULL, &fds, &fds_exc, &tv); if (res == 0) { @@ -2358,7 +2409,7 @@ internal_connect(PySocketSockObject *s, #else - if (s->sock_timeout > 0.0) { + if (HAS_TIMEOUT(s)) { if (res < 0 && errno == EINPROGRESS && IS_SELECTABLE(s)) { timeout = internal_select(s, 1); if (timeout == 0) { @@ -2591,7 +2642,7 @@ sock_recv_guts(PySocketSockObject *s, ch BEGIN_SELECT_LOOP(s) Py_BEGIN_ALLOW_THREADS - timeout = internal_select_ex(s, 0, interval); + timeout = internal_select_ex(s, 0, &interval); if (!timeout) { #ifdef MS_WINDOWS if (len > INT_MAX) @@ -2762,7 +2813,7 @@ sock_recvfrom_guts(PySocketSockObject *s BEGIN_SELECT_LOOP(s) Py_BEGIN_ALLOW_THREADS memset(&addrbuf, 0, addrlen); - timeout = internal_select_ex(s, 0, interval); + timeout = internal_select_ex(s, 0, &interval); if (!timeout) { #ifdef MS_WINDOWS if (len > INT_MAX) @@ -2964,7 +3015,7 @@ sock_recvmsg_guts(PySocketSockObject *s, msg.msg_iovlen = iovlen; msg.msg_control = controlbuf; msg.msg_controllen = controllen; - timeout = internal_select_ex(s, 0, interval); + timeout = internal_select_ex(s, 0, &interval); if (!timeout) bytes_received = recvmsg(s->sock_fd, &msg, flags); Py_END_ALLOW_THREADS; @@ -3249,7 +3300,7 @@ sock_send(PySocketSockObject *s, PyObjec BEGIN_SELECT_LOOP(s) Py_BEGIN_ALLOW_THREADS - timeout = internal_select_ex(s, 1, interval); + timeout = internal_select_ex(s, 1, &interval); if (!timeout) { #ifdef MS_WINDOWS if (len > INT_MAX) @@ -3403,7 +3454,7 @@ sock_sendto(PySocketSockObject *s, PyObj BEGIN_SELECT_LOOP(s) Py_BEGIN_ALLOW_THREADS - timeout = internal_select_ex(s, 1, interval); + timeout = internal_select_ex(s, 1, &interval); if (!timeout) { #ifdef MS_WINDOWS if (len > INT_MAX) @@ -3615,7 +3666,7 @@ sock_sendmsg(PySocketSockObject *s, PyOb BEGIN_SELECT_LOOP(s) Py_BEGIN_ALLOW_THREADS; - timeout = internal_select_ex(s, 1, interval); + timeout = internal_select_ex(s, 1, &interval); if (!timeout) bytes_sent = sendmsg(s->sock_fd, &msg, flags); Py_END_ALLOW_THREADS; @@ -3838,10 +3889,16 @@ static PyMemberDef sock_memberlist[] = { {"family", T_INT, offsetof(PySocketSockObject, sock_family), READONLY, "the socket family"}, {"type", T_INT, offsetof(PySocketSockObject, sock_type), READONLY, "the socket type"}, {"proto", T_INT, offsetof(PySocketSockObject, sock_proto), READONLY, "the socket protocol"}, - {"timeout", T_DOUBLE, offsetof(PySocketSockObject, sock_timeout), READONLY, "the socket timeout"}, {0}, }; +static PyGetSetDef socket_getset[] = { + {"timeout", (getter)sock_gettimeout, (setter)NULL, + "the socket timeout", NULL}, + {NULL} /* Sentinel */ +}; + + /* Deallocate a socket object in response to the last Py_DECREF(). First close the file description. */ @@ -3904,9 +3961,11 @@ sock_new(PyTypeObject *type, PyObject *a new = type->tp_alloc(type, 0); if (new != NULL) { - ((PySocketSockObject *)new)->sock_fd = -1; - ((PySocketSockObject *)new)->sock_timeout = -1.0; - ((PySocketSockObject *)new)->errorhandler = &set_error; + PySocketSockObject *self = (PySocketSockObject *)new; + self->sock_fd = -1; + self->sock_timeout.tv_sec = -1; + self->sock_timeout.tv_nsec = 0; + self->errorhandler = &set_error; } return new; } @@ -4088,7 +4147,7 @@ static PyTypeObject sock_type = { 0, /* tp_iternext */ sock_methods, /* tp_methods */ sock_memberlist, /* tp_members */ - 0, /* tp_getset */ + socket_getset, /* tp_getset */ 0, /* tp_base */ 0, /* tp_dict */ 0, /* tp_descr_get */ @@ -5402,12 +5461,15 @@ Get host and port for a sockaddr."); static PyObject * socket_getdefaulttimeout(PyObject *self) { - if (defaulttimeout < 0.0) { + if (defaulttimeout.tv_sec < 0) { Py_INCREF(Py_None); return Py_None; } - else - return PyFloat_FromDouble(defaulttimeout); + else { + double timeout; + _PyTimeSpec_as_double(&defaulttimeout, &timeout, _PyTime_ROUND_UP); + return PyFloat_FromDouble(timeout); + } } PyDoc_STRVAR(getdefaulttimeout_doc, @@ -5420,20 +5482,15 @@ When the socket module is first imported static PyObject * socket_setdefaulttimeout(PyObject *self, PyObject *arg) { - double timeout; - - if (arg == Py_None) - timeout = -1.0; - else { - timeout = PyFloat_AsDouble(arg); - if (timeout < 0.0) { - if (!PyErr_Occurred()) - PyErr_SetString(PyExc_ValueError, - "Timeout value out of range"); - return NULL; - } - } - + _PyTimeSpec timeout; + + if (sock_parse_timeout(&timeout, arg) < 0) + return NULL; + if (timeout.tv_sec < 0) { + PyErr_SetString(PyExc_ValueError, + "Timeout value out of range"); + return NULL; + } defaulttimeout = timeout; Py_INCREF(Py_None); diff -r 94d0e842b9ea -r 918faa56634f Modules/socketmodule.h --- a/Modules/socketmodule.h Fri Aug 01 12:28:49 2014 +0200 +++ b/Modules/socketmodule.h Fri Aug 01 22:52:18 2014 +0200 @@ -91,6 +91,8 @@ typedef int socklen_t; #include #endif +#include "pytime.h" + #ifndef Py__SOCKET_H #define Py__SOCKET_H #ifdef __cplusplus @@ -167,8 +169,8 @@ typedef struct { PyObject *(*errorhandler)(void); /* Error handler; checks errno, returns NULL and sets a Python exception */ - double sock_timeout; /* Operation timeout in seconds; - 0.0 means non-blocking */ + _PyTimeSpec sock_timeout; /* Operation timeout in seconds; + {0, 0} means non-blocking */ } PySocketSockObject; /* --- C API ----------------------------------------------------*/ diff -r 94d0e842b9ea -r 918faa56634f Modules/timemodule.c --- a/Modules/timemodule.c Fri Aug 01 12:28:49 2014 +0200 +++ b/Modules/timemodule.c Fri Aug 01 22:52:18 2014 +0200 @@ -185,18 +185,17 @@ time_clock_settime(PyObject *self, PyObj { int clk_id; PyObject *obj; - time_t tv_sec; - long tv_nsec; + _PyTimeSpec ts; struct timespec tp; int ret; if (!PyArg_ParseTuple(args, "iO:clock_settime", &clk_id, &obj)) return NULL; - if (_PyTime_ObjectToTimespec(obj, &tv_sec, &tv_nsec, _PyTime_ROUND_DOWN) == -1) + if (_PyTimeSpec_from_object(&ts, obj, _PyTime_ROUND_DOWN) < 0) return NULL; - tp.tv_sec = tv_sec; - tp.tv_nsec = tv_nsec; + tp.tv_sec = ts.tv_sec; + tp.tv_nsec = ts.tv_nsec; ret = clock_settime((clockid_t)clk_id, &tp); if (ret != 0) { @@ -341,7 +340,11 @@ parse_time_t_args(PyObject *args, char * whent = time(NULL); } else { - if (_PyTime_ObjectToTime_t(ot, &whent, _PyTime_ROUND_DOWN) == -1) + _PyTimeSpec ts; + + if (_PyTimeSpec_from_object(&ts, ot, _PyTime_ROUND_DOWN) < 0) + return 0; + if (_PyTimeSpec_to_time_t(&ts, &whent, _PyTime_ROUND_DOWN) < 0) return 0; } *pwhen = whent; @@ -1534,7 +1537,8 @@ PyInit_time(void) static PyObject* floattime(_Py_clock_info_t *info) { - _PyTime_timeval t; + _PyTimeSpec ts; + double seconds; #ifdef HAVE_CLOCK_GETTIME struct timespec tp; int ret; @@ -1557,8 +1561,9 @@ 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); + _PyTimeSpec_get_time_info(&ts, info); + _PyTimeSpec_as_double(&ts, &seconds, _PyTime_ROUND_DOWN); + return PyFloat_FromDouble(seconds); } diff -r 94d0e842b9ea -r 918faa56634f Python/pythonrun.c --- a/Python/pythonrun.c Fri Aug 01 12:28:49 2014 +0200 +++ b/Python/pythonrun.c Fri Aug 01 22:52:18 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 94d0e842b9ea -r 918faa56634f Python/pytime.c --- a/Python/pytime.c Fri Aug 01 12:28:49 2014 +0200 +++ b/Python/pytime.c Fri Aug 01 22:52:18 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 Mac + * OS X 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,100 +18,33 @@ extern int ftime(struct timeb *); #endif -static void -pygettimeofday(_PyTime_timeval *tp, _Py_clock_info_t *info) -{ -#ifdef MS_WINDOWS - FILETIME system_time; - ULARGE_INTEGER large; - ULONGLONG microseconds; +#if defined(HAVE_LONG_LONG) && SIZEOF_TIME_T == SIZEOF_LONG_LONG +# define TIME_T_MAX PY_LLONG_MAX +# define TIME_T_MIN PY_LLONG_MIN +#elif SIZEOF_TIME_T == SIZEOF_LONG +# define TIME_T_MAX LONG_MAX +# define TIME_T_MIN LONG_MIN +#elif SIZEOF_TIME_T == SIZEOF_INT +# define TIME_T_MAX INT_MAX +# define TIME_T_MIN INT_MIN +#else +# error "unsupported size of time_t" +#endif - 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 / 1000000; - tp->tv_usec = microseconds % 1000000; - if (info) { - DWORD timeAdjustment, timeIncrement; - BOOL isTimeAdjustmentDisabled; +/* one second in milliseconds (ms, 10^-3) */ +#define SEC_IN_MS (1000UL) - info->implementation = "GetSystemTimeAsFileTime()"; - info->monotonic = 0; - (void) GetSystemTimeAdjustment(&timeAdjustment, &timeIncrement, - &isTimeAdjustmentDisabled); - 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! */ +/* one second in microseconds (us, 10^-6) */ +#define SEC_IN_US (1000000UL) -#ifdef HAVE_GETTIMEOFDAY - int err; -#ifdef GETTIMEOFDAY_NO_TZ - err = gettimeofday(tp); -#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; - } -#endif /* HAVE_GETTIMEOFDAY */ +/* one second in nanoseconds (ns, 10^-9) */ +#define SEC_IN_NS (1000000000UL) -#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; - } - } -#else /* !HAVE_FTIME */ - tp->tv_sec = time(NULL); - tp->tv_usec = 0; - if (info) { - info->implementation = "time()"; - info->resolution = 1.0; - info->monotonic = 0; - info->adjustable = 1; - } -#endif /* !HAVE_FTIME */ +/* one millisecond (ms, 10^-3) in nanoseconds (ns, 10^-9) */ +#define MS_IN_NS (1000000UL) -#endif /* MS_WINDOWS */ -} - -void -_PyTime_gettimeofday(_PyTime_timeval *tp) -{ - pygettimeofday(tp, NULL); -} - -void -_PyTime_gettimeofday_info(_PyTime_timeval *tp, _Py_clock_info_t *info) -{ - pygettimeofday(tp, info); -} +/* one microsecond (us, 10^-6) in nanoseconds (ns, 10^-9) */ +#define US_IN_NS (1000UL) static void error_time_t_overflow(void) @@ -124,8 +57,7 @@ time_t _PyLong_AsTime_t(PyObject *obj) { #if defined(HAVE_LONG_LONG) && SIZEOF_TIME_T == SIZEOF_LONG_LONG - PY_LONG_LONG val; - val = PyLong_AsLongLong(obj); + PY_LONG_LONG val = PyLong_AsLongLong(obj); #else long val; assert(sizeof(time_t) <= sizeof(long)); @@ -134,7 +66,7 @@ time_t if (val == -1 && PyErr_Occurred()) { if (PyErr_ExceptionMatches(PyExc_OverflowError)) error_time_t_overflow(); - return -1; + return (time_t)-1; } return (time_t)val; } @@ -151,102 +83,463 @@ PyObject * } static int -_PyTime_ObjectToDenominator(PyObject *obj, time_t *sec, long *numerator, - double denominator, _PyTime_round_t round) +_PyTimeSpec_check_consistency(const _PyTimeSpec *ts) { - 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; + assert(0 <= ts->tv_nsec && ts->tv_nsec < SEC_IN_NS); + return 1; +} - d = PyFloat_AsDouble(obj); - floatpart = modf(d, &intpart); - if (floatpart < 0) { - floatpart = 1.0 + floatpart; - intpart -= 1.0; +static int +_PyTimeSpec_underflow(_PyTimeSpec *ts) +{ + ts->tv_sec = TIME_T_MIN; + ts->tv_nsec = 0; + return -1; +} + +static int +_PyTimeSpec_overflow(_PyTimeSpec *ts) +{ + ts->tv_sec = TIME_T_MAX; + ts->tv_nsec = SEC_IN_NS - 1; + return -1; +} + +static int +_PyTimeSpec_from_double_err(_PyTimeSpec *ts, + double seconds, _PyTime_round_t round, + int raise) +{ + double intpart, err; + /* volatile avoids unsafe compiler optimization on floating point number */ + volatile double fracpart; + int up; + + fracpart = modf(seconds, &intpart); + if (fracpart < 0) { + fracpart = 1.0 + fracpart; + intpart -= 1.0; + } + fracpart *= 1e9; + + if (intpart >= 0) + up = (round == _PyTime_ROUND_UP); + else + up = (round == _PyTime_ROUND_DOWN); + if (up) { + fracpart = ceil(fracpart); + if (fracpart >= 1e9) { + fracpart -= 1e9; + 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; } else { - *sec = _PyLong_AsTime_t(obj); - if (*sec == (time_t)-1 && PyErr_Occurred()) + fracpart = floor(fracpart); + } + + ts->tv_sec = (time_t)intpart; + ts->tv_nsec = (long)fracpart; + assert(_PyTimeSpec_check_consistency(ts)); + + err = intpart - (double)ts->tv_sec; + if (fabs(err) >= 1.0) { + if (raise) + error_time_t_overflow(); + return -1; + } + return 0; +} + +int +_PyTimeSpec_from_double(_PyTimeSpec *ts, + double seconds, _PyTime_round_t round) +{ + return _PyTimeSpec_from_double_err(ts, seconds, round, 0); +} + +int +_PyTimeSpec_sign(const _PyTimeSpec *ts) +{ + if (ts->tv_sec < 0) + return -1; + if (ts->tv_sec > 0) + return 1; + return (ts->tv_nsec != 0); +} + +int +_PyTimeSpec_add(_PyTimeSpec *res, + const _PyTimeSpec *ts1, const _PyTimeSpec *ts2) +{ + assert(_PyTimeSpec_check_consistency(ts1)); + assert(_PyTimeSpec_check_consistency(ts2)); + +#if 0 + /* FIXME: the test itself can overflow */ + if (ts1->tv_sec > TIME_T_MAX - ts2->tv_sec) + return _PyTimeSpec_overflow(res); + if (ts1->tv_sec + ts2->tv_sec < TIME_T_MIN) + return _PyTimeSpec_underflow(res); +#endif + + res->tv_sec = ts1->tv_sec + ts2->tv_sec; + res->tv_nsec = ts1->tv_nsec + ts2->tv_nsec; + if (res->tv_nsec >= SEC_IN_NS) { + if (res->tv_sec == TIME_T_MAX) + return _PyTimeSpec_overflow(res); + res->tv_sec++; + res->tv_nsec -= SEC_IN_NS; + } + assert(_PyTimeSpec_check_consistency(res)); + return 0; +} + +int +_PyTimeSpec_sub(_PyTimeSpec *res, + const _PyTimeSpec *end, const _PyTimeSpec *start) +{ + assert(_PyTimeSpec_check_consistency(end)); + assert(_PyTimeSpec_check_consistency(start)); + +#if 0 + /* FIXME: the test itself can overflow */ + if (end->tv_sec > TIME_T_MAX + start->tv_sec) + return _PyTimeSpec_overflow(res); + if (end->tv_sec < TIME_T_MIN + start->tv_sec) + return _PyTimeSpec_underflow(res); +#endif + + res->tv_sec = end->tv_sec - start->tv_sec; + res->tv_nsec = end->tv_nsec; + if (end->tv_nsec < start->tv_nsec) { + if (end->tv_sec == TIME_T_MIN) + return _PyTimeSpec_underflow(res); + res->tv_sec--; + res->tv_nsec += SEC_IN_NS; + } + res->tv_nsec -= start->tv_nsec; + assert(_PyTimeSpec_check_consistency(res)); + return 0; +} + +int +_PyTimeSpec_from_object(_PyTimeSpec *ts, PyObject *obj, _PyTime_round_t round) +{ + if (PyFloat_Check(obj)) { + double seconds = PyFloat_AsDouble(obj); + if (seconds == -1 && PyErr_Occurred()) return -1; - *numerator = 0; + return _PyTimeSpec_from_double_err(ts, seconds, round, 1); + } + else { + ts->tv_sec = _PyLong_AsTime_t(obj); + ts->tv_nsec = 0; + if (ts->tv_sec == (time_t)-1 && PyErr_Occurred()) + return -1; + assert(_PyTimeSpec_check_consistency(ts)); return 0; } } int -_PyTime_ObjectToTime_t(PyObject *obj, time_t *sec, _PyTime_round_t round) +_PyTimeSpec_multiply(const _PyTimeSpec *ts, + unsigned long factor, long *value_p, _PyTime_round_t round) { - if (PyFloat_Check(obj)) { - double d, intpart, err; + long value; + long divisor; + unsigned long max_sec; - d = PyFloat_AsDouble(obj); - if (round == _PyTime_ROUND_UP) { - if (d >= 0) - d = ceil(d); - else - d = floor(d); - } - (void)modf(d, &intpart); + assert(_PyTimeSpec_check_consistency(ts)); + assert(factor <= LONG_MAX); - *sec = (time_t)intpart; - err = intpart - (double)*sec; - if (err <= -1.0 || err >= 1.0) { - error_time_t_overflow(); - return -1; - } + if (factor == 0) { + *value_p = 0; return 0; } + divisor = SEC_IN_NS / factor; + + if (round == _PyTime_ROUND_FLOOR) { + value = ts->tv_nsec / divisor; + } else { - *sec = _PyLong_AsTime_t(obj); - if (*sec == (time_t)-1 && PyErr_Occurred()) - return -1; - return 0; + if ((ts->tv_sec >= 0) ^ (round == _PyTime_ROUND_DOWN)) + value = (ts->tv_nsec + (divisor - 1)) / divisor; + else + value = ts->tv_nsec / divisor; } + + max_sec = ((unsigned long)LONG_MAX - (unsigned long)value) / factor; + if (ts->tv_sec > (time_t)max_sec) { + *value_p = LONG_MAX; + return -1; + } + if (ts->tv_sec < (time_t)(LONG_MIN / (long)factor)) { + *value_p = LONG_MIN; + return -1; + } + + value += ts->tv_sec * (time_t)factor; + *value_p = value; + return 0; } int -_PyTime_ObjectToTimespec(PyObject *obj, time_t *sec, long *nsec, - _PyTime_round_t round) +_PyTimeSpec_divide(const _PyTimeSpec *ts, unsigned int denominator, + time_t *seconds, + unsigned int *numerator, + _PyTime_round_t round) { - return _PyTime_ObjectToDenominator(obj, sec, nsec, 1e9, round); + time_t intpart; + unsigned long divisor, fracpart; + int up; + + assert(_PyTimeSpec_check_consistency(ts)); + + if (denominator == 0) { + PyErr_SetNone(PyExc_ZeroDivisionError); + goto error; + } + + intpart = ts->tv_sec; + + divisor = SEC_IN_NS / denominator; + + if (intpart < 0) + up = (round == _PyTime_ROUND_DOWN); + else + up = (round == _PyTime_ROUND_UP); + if (up) { + fracpart = ((unsigned long)ts->tv_nsec + divisor - 1) / divisor; + if (fracpart >= denominator) { + if (intpart == TIME_T_MAX) { + error_time_t_overflow(); + goto error; + } + + intpart++; + fracpart -= denominator; + assert(fracpart == 0); + } + } + else { + fracpart = (unsigned long)ts->tv_nsec / divisor; + } + + *seconds = intpart; + *numerator = fracpart; + assert(*numerator < denominator); + return 0; + +error: + *seconds = TIME_T_MAX; + *numerator = denominator - 1; + return -1; } int -_PyTime_ObjectToTimeval(PyObject *obj, time_t *sec, long *usec, - _PyTime_round_t round) +_PyTimeSpec_to_time_t(const _PyTimeSpec *ts, + time_t *seconds, _PyTime_round_t round) { - return _PyTime_ObjectToDenominator(obj, sec, usec, 1e6, round); + unsigned int num; + return _PyTimeSpec_divide(ts, 1, seconds, &num, round); +} + +int +_PyTimeSpec_as_double(const _PyTimeSpec *ts, + double *seconds, _PyTime_round_t round) +{ + _PyTimeSpec ts2; + + *seconds = ts->tv_sec + ts->tv_nsec * 1e-9; + if (_PyTimeSpec_from_double(&ts2, *seconds, round) < 0) + return -1; + if (ts2.tv_sec != ts->tv_sec || ts2.tv_nsec != ts->tv_nsec) + return -1; + return 0; +} + +/* _PyTimeSpec_divide() is not safe to cast to timeval because the type of + timeval.tv_sec can be different than time_t depending on the platform. */ +int +_PyTimeSpec_to_timeval(const _PyTimeSpec *ts, + struct timeval *tv, _PyTime_round_t round) +{ + time_t sec; + unsigned int us; + if (_PyTimeSpec_divide(ts, 1000 * 1000, &sec, &us, round) < 0) + return -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 (sec > LONG_MAX) { + tv->tv_sec = LONG_MAX; + tv->tv_usec = 999999; + PyErr_SetString(PyExc_OverflowError, + "seconds don't fit in a C long"); + return -1; + } +#endif + tv->tv_sec = (long)sec; +#else + /* On OpenBSD 5.4, timeval.tv_sec is a long. + Example: long is 64-bit, whereas time_t is 32-bit. + On OpenBSD 5.5, time_t size is always 64 bit. */ + assert(sizeof(tv->tv_sec) >= sizeof(sec)); + tv->tv_sec = sec; +#endif + tv->tv_usec = us; + return 0; +} + +static int +pygettimeofday(_PyTimeSpec *ts, _Py_clock_info_t *info, int raise) +{ +#ifdef MS_WINDOWS + FILETIME filetime; + ULARGE_INTEGER large; + ULONGLONG unix, sec, nsec; + + GetSystemTimeAsFileTime(&filetime); + large.u.LowPart = filetime.dwLowDateTime; + large.u.HighPart = filetime.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). */ + unix = large.QuadPart * 100 - 11644473600000000000; + + sec = unix / SEC_IN_NS; + assert(sec < TIME_T_MAX); + ts->tv_sec = (time_t)sec; + ts->tv_nsec = unix % SEC_IN_NS; + + if (info) { + DWORD timeAdjustment, timeIncrement; + BOOL isTimeAdjustmentDisabled; + + info->implementation = "GetSystemTimeAsFileTime()"; + info->monotonic = 0; + (void) GetSystemTimeAdjustment(&timeAdjustment, &timeIncrement, + &isTimeAdjustmentDisabled); + info->resolution = timeIncrement * 1e-7; + info->adjustable = 1; + } + assert(_PyTimeSpec_check_consistency(&ts)); + return 0; +#else + /* There are three ways to get the time: + (1) gettimeofday() -- resolution in microseconds + (2) ftime() -- resolution in milliseconds + (3) time() -- resolution in seconds + + 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! */ + +#if defined(HAVE_GETTIMEOFDAY) || defined(HAVE_FTIME) + int err; +#endif +#ifdef HAVE_GETTIMEOFDAY + struct timeval tv; +#endif +#ifdef HAVE_FTIME + struct timeb tb; +#endif + + /* test gettimeofday() */ +#ifdef HAVE_GETTIMEOFDAY +#ifdef GETTIMEOFDAY_NO_TZ + err = gettimeofday(&tv); +#else + err = gettimeofday(&tv, (struct timezone *)NULL); +#endif + 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_NS; + if (info) { + info->implementation = "gettimeofday()"; + info->resolution = 1e-6; + info->monotonic = 0; + info->adjustable = 1; + } + assert(_PyTimeSpec_check_consistency(ts)); + return 0; + } +#endif /* HAVE_GETTIMEOFDAY */ + + /* 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_NS; + if (info) { + info->implementation = "ftime()"; + info->resolution = 1e-3; + info->monotonic = 0; + info->adjustable = 1; + } + assert(_PyTimeSpec_check_consistency(ts)); + return 0; + } +#endif /* !HAVE_FTIME */ + + errno = 0; + ts->tv_sec = time(NULL); + if (ts->tv_sec == (time_t)-1 && errno != 0) { + 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; + } + assert(_PyTimeSpec_check_consistency(ts)); + return 0; + +error: + if (!raise) + Py_FatalError("pygettimeofday() failed"); + ts->tv_sec = 0; + ts->tv_nsec = 0; + return -1; + +#endif /* MS_WINDOWS */ } void -_PyTime_Init() +_PyTimeSpec_get_time(_PyTimeSpec *tp) { - /* Do nothing. Needed to force linking. */ + /* _PyTimeSpec_Init() checked that the system clock works. */ + (void)pygettimeofday(tp, NULL, 0); } + +void +_PyTimeSpec_get_time_info(_PyTimeSpec *tp, _Py_clock_info_t *info) +{ + /* _PyTimeSpec_Init() checked that the system clock works. */ + (void)pygettimeofday(tp, info, 0); +} + +int +_PyTimeSpec_Init(void) +{ + _PyTimeSpec ts; + /* ensure that the system clock works */ + if (pygettimeofday(&ts, NULL, 1) < 0) + return -1; + return 0; +}