Rietveld Code Review Tool
Help | Bug tracker | Discussion group | Source code | Sign in
(199147)

Unified Diff: Python/pytime.c

Issue 22043: Use a monotonic clock to compute timeouts
Patch Set: Created 5 years, 4 months ago
Use n/p to move between diff chunks; N/P to move between comments. Please Sign in to add in-line comments.
Jump to:
View side-by-side diff with in-line comments
Download patch
« Modules/timemodule.c ('K') | « Python/pythonrun.c ('k') | no next file » | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
--- 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)
Charles-François Natali 2014/07/31 19:50:26 I don't understand those values. Should it be UINT
haypo 2014/08/01 01:44:20 Done.
+#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)
Charles-François Natali 2014/07/31 19:50:26 Why use the complete name for the denominator and
haypo 2014/08/01 01:44:20 Done.
+
+/* 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;
}
« Modules/timemodule.c ('K') | « Python/pythonrun.c ('k') | no next file » | no next file with comments »

RSS Feeds Recent Issues | This issue
This is Rietveld 894c83f36cb7+