diff --git a/pep-0418.txt b/pep-0418.txt index 7b886b610..0f86ded15 100644 --- a/pep-0418.txt +++ b/pep-0418.txt @@ -13,7 +13,7 @@ Python-Version: 3.3 Abstract ======== -Add time.monotonic() and time.highres() functions to Python 3.3. +Add time.monotonic(fallback=True) and time.highres() functions to Python 3.3. Rationale @@ -24,12 +24,19 @@ Use cases: * Display the current time to a human (e.g. display a calendar or draw a wall clock): use system clock. time.time() or datetime.datetime.now() * Benchmark, profiling, timeout: time.highres() - * Event scheduler: time.monotonic() + * Event scheduler: time.monotonic(), or time.monotonic(fallback=False) Functions ========= + * time.time(): system clock, "wall clock" + * time.highres(): clock with the best accuracy + * time.monotonic(fallback=True): monotonic clock. If no monotonic clock is + available, falls back to system clock by default, or raises an OSError if + *fallback* is False. time.monotonic(fallback=True) cannot go backward. + + time.time() ----------- @@ -68,12 +75,12 @@ Pseudo-code [#pseudo]_: :: return _time.time() -time.monotonic() ----------------- +time.monotonic(fallback=True) +----------------------------- -Clock advancing at a monotonic rate relative to real time. It cannot go -backward. Its rate may be adjusted by NTP. The reference point of the returned -value is undefined so only the difference of consecutive calls is valid. +Clock that cannot go backward, its rate is as steady as possible. Its rate may +be adjusted by NTP. The reference point of the returned value is undefined so +only the difference of consecutive calls is valid. It is not available on all platforms and may raise an OSError. It is not available on GNU/Hurd for example. @@ -85,34 +92,21 @@ Pseudo-code [#pseudo]_: :: if os.name == 'nt': # GetTickCount64() requires Windows Vista, Server 2008 or later if hasattr(time, '_GetTickCount64'): - _get_tick_count = _time.GetTickCount64 + def monotonic(fallback=True): + return _time.GetTickCount64() else: - def _get_tick_count(): + def monotonic(fallback=True): ticks = _time.GetTickCount() - if ticks < _get_tick_count.last: + if ticks < monotonic.last: # Integer overflow detected - _get_tick_count.delta += 2**32 - _get_tick_count.last = ticks - return ticks + _get_tick_count.delta - _get_tick_count.last = 0 - _get_tick_count.delta = 0 - - def monotonic(): - if monotonic.use_performance_counter: - try: - return _time.QueryPerformanceCounter() - except OSError: - # QueryPerformanceFrequency() may fail, if the installed - # hardware does not support a high-resolution performance - # counter for example - monotonic.use_performance_counter = False - # Fallback to GetTickCount/GetTickCount64 which has - # a lower resolution - return _get_tick_count() - monotonic.use_performance_counter = True + monotonic.delta += 2**32 + monotonic.last = ticks + return ticks + monotonic.delta + monotonic.last = 0 + monotonic.delta = 0 elif os.name == 'mac': - def monotonic(): + def monotonic(fallback=True): if monotonic.factor is None: factor = _time.mach_timebase_info() monotonic.factor = timebase[0] / timebase[1] @@ -120,31 +114,45 @@ Pseudo-code [#pseudo]_: :: monotonic.factor = None elif os.name.startswith('sunos'): - def monotonic(): + def monotonic(fallback=True): if monotonic.use_clock_highres: try: time.clock_gettime(time.CLOCK_HIGHRES) except OSError: monotonic.use_clock_highres = False - return time.gethrtime() + if monotonic.use_gethrtime: + try: + return time.gethrtime() + except OSError: + if not fallback: + raise + monotonic.use_gethrtime = False + return time.time() monotonic.use_clock_highres = (hasattr(time, 'clock_gettime') and hasattr(time, 'CLOCK_HIGHRES')) + monotonic.use_gethrtime = True elif hasattr(time, "clock_gettime"): - def monotonic(): + def monotonic(fallback=True): while monotonic.clocks: try: clk_id = monotonic.clocks[0] return time.clock_gettime(clk_id) except OSError: # CLOCK_MONOTONIC_RAW requires a Linux kernel >= 2.6.28 + if len(monotonic.clocks) == 1 and not fallback: + raise del monotonic.clocks[0] - return time.clock_gettime(time.CLOCK_MONOTONIC) + return time.time() monotonic.clocks = [] if hasattr(time, 'CLOCK_MONOTONIC_RAW'): monotonic.clocks.append(time.CLOCK_MONOTONIC_RAW) if hasattr(time, 'CLOCK_HIGHRES'): monotonic.clocks.append(time.CLOCK_HIGHRES) + monotonic.clocks.append(time.CLOCK_MONOTONIC) + +On Windows, QueryPerformanceCounter() is not used even if it has a better +resolution than GetTickCount(). It is not reliable and has too much issues. .. note:: @@ -157,20 +165,29 @@ Pseudo-code [#pseudo]_: :: time.highres() -------------- -High-resolution clock: use a monotonic clock if available, or fallback to the -system time. +Clock with the best available resolution. It is available on all platforms and cannot fail. Pseudo-code:: def highres(): + if monotonic.use_performance_counter: + try: + return _time.QueryPerformanceCounter() + except OSError: + # QueryPerformanceFrequency() may fail, if the installed + # hardware does not support a high-resolution performance + # counter for example + monotonic.use_performance_counter = False if highres.use_monotonic: + # Monotonic clock is preferred over system clock try: return time.monotonic() except OSError: highres.use_monotonic = False return time.time() + highres.use_performance_counter = (os.name == 'nt') highres.use_monotonic = hasattr(time, 'monotonic')