summaryrefslogtreecommitdiffstats
path: root/Python/pytime.c
diff options
context:
space:
mode:
authorVictor Stinner <victor.stinner@gmail.com>2014-09-02 21:18:25 (GMT)
committerVictor Stinner <victor.stinner@gmail.com>2014-09-02 21:18:25 (GMT)
commitae58649721ec898ea4a101b0861e16fff3511cfa (patch)
treebb08b1a9b5f354212d7ca365d76659b09aed879a /Python/pytime.c
parent9bb758cee7f1694807ebe600b6230fb8e565d3ea (diff)
downloadcpython-ae58649721ec898ea4a101b0861e16fff3511cfa.zip
cpython-ae58649721ec898ea4a101b0861e16fff3511cfa.tar.gz
cpython-ae58649721ec898ea4a101b0861e16fff3511cfa.tar.bz2
Issue #22043: time.monotonic() is now always available
threading.Lock.acquire(), threading.RLock.acquire() and socket operations now use a monotonic clock, instead of the system clock, when a timeout is used.
Diffstat (limited to 'Python/pytime.c')
-rw-r--r--Python/pytime.c175
1 files changed, 175 insertions, 0 deletions
diff --git a/Python/pytime.c b/Python/pytime.c
index 395a432..9964195 100644
--- a/Python/pytime.c
+++ b/Python/pytime.c
@@ -3,6 +3,14 @@
#include <windows.h>
#endif
+#if defined(__APPLE__)
+#include <mach/mach_time.h> /* mach_absolute_time(), mach_timebase_info() */
+#endif
+
+#ifdef MS_WINDOWS
+static OSVERSIONINFOEX winver;
+#endif
+
static int
pygettimeofday(_PyTime_timeval *tp, _Py_clock_info_t *info, int raise)
{
@@ -109,6 +117,160 @@ _PyTime_gettimeofday_info(_PyTime_timeval *tp, _Py_clock_info_t *info)
return pygettimeofday(tp, info, 1);
}
+static int
+pymonotonic(_PyTime_timeval *tp, _Py_clock_info_t *info, int raise)
+{
+#ifdef Py_DEBUG
+ static _PyTime_timeval last = {-1, -1};
+#endif
+#if defined(MS_WINDOWS)
+ static ULONGLONG (*GetTickCount64) (void) = NULL;
+ static ULONGLONG (CALLBACK *Py_GetTickCount64)(void);
+ static int has_gettickcount64 = -1;
+ ULONGLONG result;
+
+ assert(info == NULL || raise);
+
+ if (has_gettickcount64 == -1) {
+ /* GetTickCount64() was added to Windows Vista */
+ has_gettickcount64 = (winver.dwMajorVersion >= 6);
+ if (has_gettickcount64) {
+ HINSTANCE hKernel32;
+ hKernel32 = GetModuleHandleW(L"KERNEL32");
+ *(FARPROC*)&Py_GetTickCount64 = GetProcAddress(hKernel32,
+ "GetTickCount64");
+ assert(Py_GetTickCount64 != NULL);
+ }
+ }
+
+ if (has_gettickcount64) {
+ result = Py_GetTickCount64();
+ }
+ else {
+ static DWORD last_ticks = 0;
+ static DWORD n_overflow = 0;
+ DWORD ticks;
+
+ ticks = GetTickCount();
+ if (ticks < last_ticks)
+ n_overflow++;
+ last_ticks = ticks;
+
+ result = (ULONGLONG)n_overflow << 32;
+ result += ticks;
+ }
+
+ tp->tv_sec = result / 1000;
+ tp->tv_usec = (result % 1000) * 1000;
+
+ if (info) {
+ DWORD timeAdjustment, timeIncrement;
+ BOOL isTimeAdjustmentDisabled, ok;
+ if (has_gettickcount64)
+ info->implementation = "GetTickCount64()";
+ else
+ info->implementation = "GetTickCount()";
+ info->monotonic = 1;
+ ok = GetSystemTimeAdjustment(&timeAdjustment, &timeIncrement,
+ &isTimeAdjustmentDisabled);
+ if (!ok) {
+ PyErr_SetFromWindowsErr(0);
+ return -1;
+ }
+ info->resolution = timeIncrement * 1e-7;
+ info->adjustable = 0;
+ }
+
+#elif defined(__APPLE__)
+ static mach_timebase_info_data_t timebase;
+ uint64_t time;
+
+ if (timebase.denom == 0) {
+ /* According to the Technical Q&A QA1398, mach_timebase_info() cannot
+ fail: https://developer.apple.com/library/mac/#qa/qa1398/ */
+ (void)mach_timebase_info(&timebase);
+ }
+
+ time = mach_absolute_time();
+
+ /* nanoseconds => microseconds */
+ time /= 1000;
+ /* apply timebase factor */
+ time *= timebase.numer;
+ time /= timebase.denom;
+ tp->tv_sec = time / (1000 * 1000);
+ tp->tv_usec = time % (1000 * 1000);
+
+ if (info) {
+ info->implementation = "mach_absolute_time()";
+ info->resolution = (double)timebase.numer / timebase.denom * 1e-9;
+ info->monotonic = 1;
+ info->adjustable = 0;
+ }
+
+#else
+ struct timespec ts;
+#ifdef CLOCK_HIGHRES
+ const clockid_t clk_id = CLOCK_HIGHRES;
+ const char *implementation = "clock_gettime(CLOCK_HIGHRES)";
+#else
+ const clockid_t clk_id = CLOCK_MONOTONIC;
+ const char *implementation = "clock_gettime(CLOCK_MONOTONIC)";
+#endif
+
+ assert(info == NULL || raise);
+
+ if (clock_gettime(clk_id, &ts) != 0) {
+ if (raise) {
+ PyErr_SetFromErrno(PyExc_OSError);
+ return -1;
+ }
+ tp->tv_sec = 0;
+ tp->tv_usec = 0;
+ return -1;
+ }
+
+ if (info) {
+ struct timespec res;
+ info->monotonic = 1;
+ info->implementation = implementation;
+ info->adjustable = 0;
+ if (clock_getres(clk_id, &res) != 0) {
+ PyErr_SetFromErrno(PyExc_OSError);
+ return -1;
+ }
+ info->resolution = res.tv_sec + res.tv_nsec * 1e-9;
+ }
+ tp->tv_sec = ts.tv_sec;
+ tp->tv_usec = ts.tv_nsec / 1000;
+#endif
+ assert(0 <= tp->tv_usec && tp->tv_usec < 1000 * 1000);
+#ifdef Py_DEBUG
+ /* monotonic clock cannot go backward */
+ assert(tp->tv_sec > last.tv_sec
+ || (tp->tv_sec == last.tv_sec && tp->tv_usec >= last.tv_usec));
+ last = *tp;
+#endif
+ return 0;
+}
+
+void
+_PyTime_monotonic(_PyTime_timeval *tp)
+{
+ if (pymonotonic(tp, NULL, 0) < 0) {
+ /* cannot happen, _PyTime_Init() checks that pymonotonic() works */
+ assert(0);
+ tp->tv_sec = 0;
+ tp->tv_usec = 0;
+ }
+}
+
+int
+_PyTime_monotonic_info(_PyTime_timeval *tp, _Py_clock_info_t *info)
+{
+ return pymonotonic(tp, info, 1);
+}
+
static void
error_time_t_overflow(void)
{
@@ -245,8 +407,21 @@ int
_PyTime_Init(void)
{
_PyTime_timeval tv;
+
+#ifdef MS_WINDOWS
+ winver.dwOSVersionInfoSize = sizeof(winver);
+ if (!GetVersionEx((OSVERSIONINFO*)&winver)) {
+ PyErr_SetFromWindowsErr(0);
+ return -1;
+ }
+#endif
+
/* ensure that the system clock works */
if (_PyTime_gettimeofday_info(&tv, NULL) < 0)
return -1;
+
+ /* ensure that the operating system provides a monotonic clock */
+ if (_PyTime_monotonic_info(&tv, NULL) < 0)
+ return -1;
return 0;
}