diff options
Diffstat (limited to 'Python/pytime.c')
| -rw-r--r-- | Python/pytime.c | 304 | 
1 files changed, 240 insertions, 64 deletions
| diff --git a/Python/pytime.c b/Python/pytime.c index de6a41f..a8460c6 100644 --- a/Python/pytime.c +++ b/Python/pytime.c @@ -3,29 +3,24 @@  #include <windows.h>  #endif -#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. -   */ -# undef HAVE_FTIME +#if defined(__APPLE__) +#include <mach/mach_time.h>   /* mach_absolute_time(), mach_timebase_info() */  #endif -#if defined(HAVE_FTIME) && !defined(MS_WINDOWS) -#include <sys/timeb.h> -extern int ftime(struct timeb *); +#ifdef MS_WINDOWS +static OSVERSIONINFOEX winver;  #endif -static void -pygettimeofday(_PyTime_timeval *tp, _Py_clock_info_t *info) +static int +pygettimeofday(_PyTime_timeval *tp, _Py_clock_info_t *info, int raise)  {  #ifdef MS_WINDOWS      FILETIME system_time;      ULARGE_INTEGER large;      ULONGLONG microseconds; +    assert(info == NULL || raise); +      GetSystemTimeAsFileTime(&system_time);      large.u.LowPart = system_time.dwLowDateTime;      large.u.HighPart = system_time.dwHighDateTime; @@ -37,80 +32,244 @@ pygettimeofday(_PyTime_timeval *tp, _Py_clock_info_t *info)      tp->tv_usec = microseconds % 1000000;      if (info) {          DWORD timeAdjustment, timeIncrement; -        BOOL isTimeAdjustmentDisabled; +        BOOL isTimeAdjustmentDisabled, ok;          info->implementation = "GetSystemTimeAsFileTime()";          info->monotonic = 0; -        (void) GetSystemTimeAdjustment(&timeAdjustment, &timeIncrement, -                                       &isTimeAdjustmentDisabled); +        ok = GetSystemTimeAdjustment(&timeAdjustment, &timeIncrement, +                                     &isTimeAdjustmentDisabled); +        if (!ok) { +            PyErr_SetFromWindowsErr(0); +            return -1; +        }          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! */ - -#ifdef HAVE_GETTIMEOFDAY + +#else   /* MS_WINDOWS */      int err; +#ifdef HAVE_CLOCK_GETTIME +    struct timespec ts; +#endif + +    assert(info == NULL || raise); + +#ifdef HAVE_CLOCK_GETTIME +    err = clock_gettime(CLOCK_REALTIME, &ts); +    if (err) { +        if (raise) +            PyErr_SetFromErrno(PyExc_OSError); +        return -1; +    } +    tp->tv_sec = ts.tv_sec; +    tp->tv_usec = ts.tv_nsec / 1000; + +    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; +    } +#else   /* HAVE_CLOCK_GETTIME */ + +     /* test gettimeofday() */  #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 */ - -#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; -        } +    if (err) { +        if (raise) +            PyErr_SetFromErrno(PyExc_OSError); +        return -1;      } -#else /* !HAVE_FTIME */ -    tp->tv_sec = time(NULL); -    tp->tv_usec = 0; +      if (info) { -        info->implementation = "time()"; -        info->resolution = 1.0; +        info->implementation = "gettimeofday()"; +        info->resolution = 1e-6;          info->monotonic = 0;          info->adjustable = 1;      } -#endif /* !HAVE_FTIME */ - -#endif /* MS_WINDOWS */ +#endif   /* !HAVE_CLOCK_GETTIME */ +#endif   /* !MS_WINDOWS */ +    assert(0 <= tp->tv_usec && tp->tv_usec < 1000 * 1000); +    return 0;  }  void  _PyTime_gettimeofday(_PyTime_timeval *tp)  { -    pygettimeofday(tp, NULL); +    if (pygettimeofday(tp, NULL, 0) < 0) { +        /* cannot happen, _PyTime_Init() checks that pygettimeofday() works */ +        assert(0); +        tp->tv_sec = 0; +        tp->tv_usec = 0; +    }  } -void +int  _PyTime_gettimeofday_info(_PyTime_timeval *tp, _Py_clock_info_t *info)  { -    pygettimeofday(tp, 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 = {0, -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(last.tv_usec == -1 +           || 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 @@ -245,8 +404,25 @@ _PyTime_ObjectToTimeval(PyObject *obj, time_t *sec, long *usec,      return _PyTime_ObjectToDenominator(obj, sec, usec, 1e6, round);  } -void -_PyTime_Init() +int +_PyTime_Init(void)  { -    /* Do nothing.  Needed to force linking. */ +    _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;  } | 
