Package pype :: Module monotonic
[frames] | no frames]

Source Code for Module pype.monotonic

  1  # -*- coding: utf-8 -*- 
  2  """ 
  3    monotonic 
  4    ~~~~~~~~~ 
  5   
  6    This module provides a ``monotonic()`` function which returns the 
  7    value (in fractional seconds) of a clock which never goes backwards. 
  8   
  9    On Python 3.3 or newer, ``monotonic`` will be an alias of 
 10    ``time.monotonic`` from the standard library. On older versions, 
 11    it will fall back to an equivalent implementation: 
 12   
 13    +-------------+----------------------------------------+ 
 14    | Linux, BSD  | ``clock_gettime(3)``                   | 
 15    +-------------+----------------------------------------+ 
 16    | Windows     | ``GetTickCount`` or ``GetTickCount64`` | 
 17    +-------------+----------------------------------------+ 
 18    | OS X        | ``mach_absolute_time``                 | 
 19    +-------------+----------------------------------------+ 
 20   
 21    If no suitable implementation exists for the current platform, 
 22    attempting to import this module (or to import from it) will 
 23    cause a ``RuntimeError`` exception to be raised. 
 24   
 25   
 26    Copyright 2014, 2015, 2016 Ori Livneh <ori@wikimedia.org> 
 27   
 28    Licensed under the Apache License, Version 2.0 (the "License"); 
 29    you may not use this file except in compliance with the License. 
 30    You may obtain a copy of the License at 
 31   
 32      http://www.apache.org/licenses/LICENSE-2.0 
 33   
 34    Unless required by applicable law or agreed to in writing, software 
 35    distributed under the License is distributed on an "AS IS" BASIS, 
 36    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 
 37    See the License for the specific language governing permissions and 
 38    limitations under the License. 
 39   
 40  """ 
 41  import ctypes 
 42  import ctypes.util 
 43  import os 
 44  import sys 
 45  import threading 
 46  import time 
 47   
 48   
 49  __all__ = ('monotonic',) 
 50   
 51   
 52  try: 
 53      monotonic = time.monotonic 
 54  except AttributeError: 
 55      try: 
 56          if sys.platform == 'darwin':  # OS X, iOS 
 57              # See Technical Q&A QA1398 of the Mac Developer Library: 
 58              #  <https://developer.apple.com/library/mac/qa/qa1398/> 
 59              libc = ctypes.CDLL('/usr/lib/libc.dylib', use_errno=True) 
 60   
61 - class mach_timebase_info_data_t(ctypes.Structure):
62 """System timebase info. Defined in <mach/mach_time.h>.""" 63 _fields_ = (('numer', ctypes.c_uint32), 64 ('denom', ctypes.c_uint32))
65 66 mach_absolute_time = libc.mach_absolute_time 67 mach_absolute_time.restype = ctypes.c_uint64 68 69 timebase = mach_timebase_info_data_t() 70 libc.mach_timebase_info(ctypes.byref(timebase)) 71 ticks_per_second = timebase.numer / timebase.denom * 1.0e9 72
73 - def monotonic():
74 """Monotonic clock, cannot go backward.""" 75 return mach_absolute_time() / ticks_per_second
76 77 elif sys.platform.startswith('win32') or sys.platform.startswith('cygwin'): 78 if sys.platform.startswith('cygwin'): 79 # Note: cygwin implements clock_gettime (CLOCK_MONOTONIC = 4) since 80 # version 1.7.6. Using raw WinAPI for maximum version compatibility. 81 82 # Ugly hack using the wrong calling convention (in 32-bit mode) 83 # because ctypes has no windll under cygwin (and it also seems that 84 # the code letting you select stdcall in _ctypes doesn't exist under 85 # the preprocessor definitions relevant to cygwin). 86 # This is 'safe' because: 87 # 1. The ABI of GetTickCount and GetTickCount64 is identical for 88 # both calling conventions because they both have no parameters. 89 # 2. libffi masks the problem because after making the call it doesn't 90 # touch anything through esp and epilogue code restores a correct 91 # esp from ebp afterwards. 92 try: 93 kernel32 = ctypes.cdll.kernel32 94 except OSError: # 'No such file or directory' 95 kernel32 = ctypes.cdll.LoadLibrary('kernel32.dll') 96 else: 97 kernel32 = ctypes.windll.kernel32 98 99 GetTickCount64 = getattr(kernel32, 'GetTickCount64', None) 100 if GetTickCount64: 101 # Windows Vista / Windows Server 2008 or newer. 102 GetTickCount64.restype = ctypes.c_ulonglong 103
104 - def monotonic():
105 """Monotonic clock, cannot go backward.""" 106 return GetTickCount64() / 1000.0
107 108 else: 109 # Before Windows Vista. 110 GetTickCount = kernel32.GetTickCount 111 GetTickCount.restype = ctypes.c_uint32 112 113 get_tick_count_lock = threading.Lock() 114 get_tick_count_last_sample = 0 115 get_tick_count_wraparounds = 0 116
117 - def monotonic():
118 """Monotonic clock, cannot go backward.""" 119 global get_tick_count_last_sample 120 global get_tick_count_wraparounds 121 122 with get_tick_count_lock: 123 current_sample = GetTickCount() 124 if current_sample < get_tick_count_last_sample: 125 get_tick_count_wraparounds += 1 126 get_tick_count_last_sample = current_sample 127 128 final_milliseconds = get_tick_count_wraparounds << 32 129 final_milliseconds += get_tick_count_last_sample 130 return final_milliseconds / 1000.0
131 132 else: 133 try: 134 clock_gettime = ctypes.CDLL(ctypes.util.find_library('c'), 135 use_errno=True).clock_gettime 136 except Exception: 137 clock_gettime = ctypes.CDLL(ctypes.util.find_library('rt'), 138 use_errno=True).clock_gettime 139
140 - class timespec(ctypes.Structure):
141 """Time specification, as described in clock_gettime(3).""" 142 _fields_ = (('tv_sec', ctypes.c_long), 143 ('tv_nsec', ctypes.c_long))
144 145 if sys.platform.startswith('linux'): 146 CLOCK_MONOTONIC = 1 147 elif sys.platform.startswith('freebsd'): 148 CLOCK_MONOTONIC = 4 149 elif sys.platform.startswith('sunos5'): 150 CLOCK_MONOTONIC = 4 151 elif 'bsd' in sys.platform: 152 CLOCK_MONOTONIC = 3 153 elif sys.platform.startswith('aix'): 154 CLOCK_MONOTONIC = ctypes.c_longlong(10) 155
156 - def monotonic():
157 """Monotonic clock, cannot go backward.""" 158 ts = timespec() 159 if clock_gettime(CLOCK_MONOTONIC, ctypes.pointer(ts)): 160 errno = ctypes.get_errno() 161 raise OSError(errno, os.strerror(errno)) 162 return ts.tv_sec + ts.tv_nsec / 1.0e9
163 164 # Perform a sanity-check. 165 if monotonic() - monotonic() > 0: 166 raise ValueError('monotonic() is not monotonic!') 167 168 except Exception as e: 169 raise RuntimeError('no suitable implementation for this system: ' + repr(e)) 170