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

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/_threadmodule.c ('K') | « Modules/timemodule.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 Tue Jul 22 21:29:52 2014 +0100
+++ b/Python/pytime.c Wed Jul 23 04:09:37 2014 +0200
@@ -18,23 +18,26 @@
extern int ftime(struct timeb *);
#endif
+/* one seconde in nanoseconds */
+#define SEC_IN_NANOSECONDS 1000000000
Charles-François Natali 2014/07/31 07:33:20 Maybe add an explicit 'UL' suffix?
haypo 2014/07/31 11:17:44 Oh yes.
+
static void
-pygettimeofday(_PyTime_timeval *tp, _Py_clock_info_t *info)
+pygettimeofday(_PyTime_timespec *ts, _Py_clock_info_t *info)
{
#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;
@@ -47,23 +50,57 @@ pygettimeofday(_PyTime_timeval *tp, _Py_
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
+ /* There are four ways to get the time:
+ (1) clock_gettime() -- resolution in nanoseconds
+ (2) gettimeofday() -- resolution in microseconds
+ (3) ftime() -- resolution in milliseconds
+ (4) 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! */
+#if defined(HAVE_CLOCK_GETTIME) || defined(HAVE_GETTIMEOFDAY)
+ int err;
+#endif
+#ifdef HAVE_CLOCK_GETTIME
+ struct timespec tp;
+#endif
#ifdef HAVE_GETTIMEOFDAY
- int err;
+ struct timeval tv;
+#endif
+#ifdef HAVE_FTIME
+ struct timeb tb;
+#endif
+
+#ifdef HAVE_CLOCK_GETTIME
+ err = clock_gettime(CLOCK_REALTIME, &tp);
+ if (err == 0) {
+ if (info) {
+ struct timespec res;
+ info->implementation = "clock_gettime(CLOCK_REALTIME)";
+ info->monotonic = 0;
+ info->adjustable = 1;
+ if (clock_getres(CLOCK_REALTIME, &res) == 0)
+ info->resolution = res.tv_sec + res.tv_nsec * 1e-9;
+ else
+ info->resolution = 1e-9;
+ }
+ ts->tv_sec = tp.tv_sec;
+ ts->tv_nsec = tp.tv_nsec;
+ return;
+ }
+#endif
+
+#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) {
+ ts->tv_sec = tv.tv_sec;
+ ts->tv_nsec = tv.tv_usec * 1000;
if (info) {
info->implementation = "gettimeofday()";
info->resolution = 1e-6;
@@ -74,22 +111,19 @@ pygettimeofday(_PyTime_timeval *tp, _Py_
}
#endif /* HAVE_GETTIMEOFDAY */
-#if defined(HAVE_FTIME)
- {
- struct timeb t;
- ftime(&t);
- tp->tv_sec = t.time;
- tp->tv_usec = t.millitm * 1000;
- if (info) {
- info->implementation = "ftime()";
- info->resolution = 1e-3;
- info->monotonic = 0;
- info->adjustable = 1;
- }
+#ifdef HAVE_FTIME
+ ftime(&tb);
+ ts->tv_sec = tb.time;
+ ts->tv_nsec = tb.millitm * 1000000;
+ 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;
+ tp->tv_nsec = 0;
if (info) {
info->implementation = "time()";
info->resolution = 1.0;
@@ -102,13 +136,13 @@ pygettimeofday(_PyTime_timeval *tp, _Py_
}
void
-_PyTime_gettimeofday(_PyTime_timeval *tp)
+_PyTime_gettimeofday(_PyTime_timespec *tp)
{
pygettimeofday(tp, NULL);
}
void
-_PyTime_gettimeofday_info(_PyTime_timeval *tp, _Py_clock_info_t *info)
+_PyTime_gettimeofday_info(_PyTime_timespec *tp, _Py_clock_info_t *info)
{
pygettimeofday(tp, info);
}
@@ -151,45 +185,55 @@ PyObject *
}
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;
+
+ 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
_PyTime_ObjectToDenominator(PyObject *obj, time_t *sec, long *numerator,
double denominator, _PyTime_round_t round)
{
assert(denominator <= LONG_MAX);
if (PyFloat_Check(obj)) {
- double d, 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,6 +289,146 @@ int
return _PyTime_ObjectToDenominator(obj, sec, usec, 1e6, round);
}
+static int
+pymonotonic(_PyTime_timespec *ts, _Py_clock_info_t *info, int raise)
+{
+#if defined(MS_WINDOWS)
+ static ULONGLONG (*GetTickCount64) (void) = NULL;
+ static ULONGLONG (CALLBACK *Py_GetTickCount64)(void);
+ static int has_getickcount64 = -1;
+ double result;
+
+ if (has_getickcount64 == -1) {
+ /* GetTickCount64() was added to Windows Vista */
+ if (winver.dwMajorVersion >= 6) {
+ HINSTANCE hKernel32;
+ hKernel32 = GetModuleHandleW(L"KERNEL32");
+ *(FARPROC*)&Py_GetTickCount64 = GetProcAddress(hKernel32,
+ "GetTickCount64");
+ has_getickcount64 = (Py_GetTickCount64 != NULL);
+ }
+ else
+ has_getickcount64 = 0;
+ }
+
+ if (has_getickcount64) {
+ ULONGLONG ticks;
+ ticks = Py_GetTickCount64();
+ ts->tv_sec = ticks / 1000;
+ ts->tv_nsec = (ticks % 1000) * 1000000;
+ }
+ else {
+ static DWORD last_ticks = 0;
+ DWORD ticks;
+ static ULONGLONG delta = 0;
+ ULONGLONG result;
+
+ ticks = GetTickCount();
+ if (ticks < last_ticks)
+ delta += (ULONGLONG)1 << 32;
+ last_ticks = ticks;
+
+ result = (ULONGLONG)ticks + delta;
+
+ ts->tv_sec = result / 1000;
+ ts->tv_nsec = (result % 1000) * 1000000;
+ }
+
+ if (info) {
+ DWORD timeAdjustment, timeIncrement;
+ BOOL isTimeAdjustmentDisabled, ok;
+ if (has_getickcount64)
+ info->implementation = "GetTickCount64()";
+ else
+ info->implementation = "GetTickCount()";
+ info->monotonic = 1;
+ ok = GetSystemTimeAdjustment(&timeAdjustment, &timeIncrement,
+ &isTimeAdjustmentDisabled);
+ if (!ok) {
+ if (raise)
+ PyErr_SetFromWindowsErr(0);
+ goto error;
+ }
+ info->resolution = timeIncrement * 1e-7;
+ info->adjustable = 0;
+ }
+ return 0;
+
+#elif defined(__APPLE__)
+ static mach_timebase_info_data_t timebase;
+ uint64_t time;
+ double secs;
+
+ if (timebase.denom == 0) {
+ /* According to the Technical Q&A QA1398, mach_timebase_info() cannot
+ fail: https://developer.apple.com/library/mac/#qa/qa1398/ */
+ (void)mach_timebase_info(&timebase);
+ }
+
+ time = mach_absolute_time();
+ secs = (double)time * timebase.numer / timebase.denom * 1e-9;
+
+ if (_PyTime_DoubleToDenominator(secs, &ts->tv_sec, &ts->nsec,
+ 1e9, _PyTime_ROUND_DOWN, raise) < 0)
+ goto error;
+
+ if (info) {
+ info->implementation = "mach_absolute_time()";
+ info->resolution = (double)timebase.numer / timebase.denom * 1e-9;
+ info->monotonic = 1;
+ info->adjustable = 0;
+ }
+ return 0;
+
+#elif defined(HAVE_CLOCK_GETTIME) && (defined(CLOCK_HIGHRES) || defined(CLOCK_MONOTONIC))
+ struct timespec tp;
+#ifdef CLOCK_HIGHRES
+ const clockid_t clk_id = CLOCK_HIGHRES;
+ const char *function = "clock_gettime(CLOCK_HIGHRES)";
+#else
+ const clockid_t clk_id = CLOCK_MONOTONIC;
+ const char *function = "clock_gettime(CLOCK_MONOTONIC)";
+#endif
+
+ if (clock_gettime(clk_id, &tp) != 0) {
+ if (raise)
+ PyErr_SetFromErrno(PyExc_OSError);
+ goto error;
+ }
+ ts->tv_sec = tp.tv_sec;
+ ts->tv_nsec = tp.tv_nsec;
+
+ if (info) {
+ struct timespec res;
+ info->monotonic = 1;
+ info->implementation = function;
+ info->adjustable = 0;
+ if (clock_getres(clk_id, &res) == 0)
+ info->resolution = res.tv_sec + res.tv_nsec * 1e-9;
+ else
+ info->resolution = 1e-9;
+ }
+ return 0;
+#endif
+
+error:
+ ts->tv_sec = 0;
+ ts->tv_nsec = 0;
+ return -1;
+}
+
+void
+_PyTime_monotonic(_PyTime_timespec *ts)
+{
+ (void)pymonotonic(ts, NULL, 0);
+}
Charles-François Natali 2014/07/31 07:33:20 I find the error handling a bit surprising: if pym
haypo 2014/07/31 11:17:44 Would it makes sense to call _PyTime_monotonic_inf
+
+int
+_PyTime_monotonic_info(_PyTime_timespec *ts, _Py_clock_info_t *info)
+{
+ return pymonotonic(ts, info, 1);
+}
+
void
_PyTime_Init()
{
« Modules/_threadmodule.c ('K') | « Modules/timemodule.c ('k') | no next file » | no next file with comments »

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