2 Backport of time.monotonic() of Python 3.3 (PEP 418) for Python 2.7.
4 - time_monotonic(). This clock may or may not be monotonic depending on the
6 - time_monotonic_resolution: Resolution of time_monotonic() clock in second
8 Support Windows, Mac OS X, Linux, FreeBSD, OpenBSD and Solaris, but requires
13 from .log import logger
14 from .py33_exceptions import get_error_class
16 __all__ = ('time_monotonic',)
18 # default implementation: system clock (non monotonic!)
19 from time import time as time_monotonic
20 # the worst resolution is 15.6 ms on Windows
21 time_monotonic_resolution = 0.050
24 # Windows: use GetTickCount64() or GetTickCount()
27 from ctypes import windll
28 from ctypes.wintypes import DWORD
30 logger.error("time_monotonic import error", exc_info=True)
32 # GetTickCount64() requires Windows Vista, Server 2008 or later
33 if hasattr(windll.kernel32, 'GetTickCount64'):
34 ULONGLONG = ctypes.c_uint64
36 GetTickCount64 = windll.kernel32.GetTickCount64
37 GetTickCount64.restype = ULONGLONG
38 GetTickCount64.argtypes = ()
41 return GetTickCount64() * 1e-3
42 time_monotonic_resolution = 1e-3
44 GetTickCount = windll.kernel32.GetTickCount
45 GetTickCount.restype = DWORD
46 GetTickCount.argtypes = ()
48 # Detect GetTickCount() integer overflow (32 bits, roll-over after 49.7
49 # days). It increases an internal epoch (reference time) by 2^32 each
50 # time that an overflow is detected. The epoch is stored in the
51 # process-local state and so the value of time_monotonic() may be
52 # different in two Python processes running for more than 49 days.
54 ticks = GetTickCount()
55 if ticks < time_monotonic.last:
56 # Integer overflow detected
57 time_monotonic.delta += 2**32
58 time_monotonic.last = ticks
59 return (ticks + time_monotonic.delta) * 1e-3
60 time_monotonic.last = 0
61 time_monotonic.delta = 0
62 time_monotonic_resolution = 1e-3
64 elif sys.platform == 'darwin':
65 # Mac OS X: use mach_absolute_time() and mach_timebase_info()
69 libc_name = ctypes.util.find_library('c')
71 logger.error("time_monotonic import error", exc_info=True)
74 libc = ctypes.CDLL(libc_name, use_errno=True)
76 mach_absolute_time = libc.mach_absolute_time
77 mach_absolute_time.argtypes = ()
78 mach_absolute_time.restype = ctypes.c_uint64
80 class mach_timebase_info_data_t(ctypes.Structure):
82 ('numer', ctypes.c_uint32),
83 ('denom', ctypes.c_uint32),
85 mach_timebase_info_data_p = ctypes.POINTER(mach_timebase_info_data_t)
87 mach_timebase_info = libc.mach_timebase_info
88 mach_timebase_info.argtypes = (mach_timebase_info_data_p,)
89 mach_timebase_info.restype = ctypes.c_int
92 return mach_absolute_time() * time_monotonic.factor
94 timebase = mach_timebase_info_data_t()
95 mach_timebase_info(ctypes.byref(timebase))
96 time_monotonic.factor = float(timebase.numer) / timebase.denom * 1e-9
97 time_monotonic_resolution = time_monotonic.factor
100 elif sys.platform.startswith(("linux", "freebsd", "openbsd", "sunos")):
101 # Linux, FreeBSD, OpenBSD: use clock_gettime(CLOCK_MONOTONIC)
102 # Solaris: use clock_gettime(CLOCK_HIGHRES)
109 logger.error("time_monotonic import error", exc_info=True)
112 if sys.platform.startswith(("freebsd", "openbsd")):
114 elif sys.platform.startswith("linux"):
115 # Linux: in glibc 2.17+, clock_gettime() is provided by the libc,
116 # on older versions, it is provided by librt
117 libraries = ('c', 'rt')
122 for name in libraries:
123 filename = ctypes.util.find_library(name)
126 library = ctypes.CDLL(filename, use_errno=True)
127 if not hasattr(library, 'clock_gettime'):
130 if library is not None:
131 if sys.platform.startswith("openbsd"):
133 release = platform.release()
134 release = tuple(map(int, release.split('.')))
135 if release >= (5, 5):
136 time_t = ctypes.c_int64
138 time_t = ctypes.c_int32
140 time_t = ctypes.c_long
141 clockid_t = ctypes.c_int
143 class timespec(ctypes.Structure):
146 ('tv_nsec', ctypes.c_long),
148 timespec_p = ctypes.POINTER(timespec)
150 clock_gettime = library.clock_gettime
151 clock_gettime.argtypes = (clockid_t, timespec_p)
152 clock_gettime.restype = ctypes.c_int
154 def ctypes_oserror():
155 errno = ctypes.get_errno()
156 message = os.strerror(errno)
157 error_class = get_error_class(errno, OSError)
158 return error_class(errno, message)
160 def time_monotonic():
162 err = clock_gettime(time_monotonic.clk_id, ctypes.byref(ts))
164 raise ctypes_oserror()
165 return ts.tv_sec + ts.tv_nsec * 1e-9
167 if sys.platform.startswith("linux"):
168 time_monotonic.clk_id = 1 # CLOCK_MONOTONIC
169 elif sys.platform.startswith("freebsd"):
170 time_monotonic.clk_id = 4 # CLOCK_MONOTONIC
171 elif sys.platform.startswith("openbsd"):
172 time_monotonic.clk_id = 3 # CLOCK_MONOTONIC
174 assert sys.platform.startswith("sunos")
175 time_monotonic.clk_id = 4 # CLOCK_HIGHRES
177 def get_resolution():
178 _clock_getres = library.clock_getres
179 _clock_getres.argtypes = (clockid_t, timespec_p)
180 _clock_getres.restype = ctypes.c_int
183 err = _clock_getres(time_monotonic.clk_id, ctypes.byref(ts))
185 raise ctypes_oserror()
186 return ts.tv_sec + ts.tv_nsec * 1e-9
187 time_monotonic_resolution = get_resolution()
191 logger.error("time_monotonic: unspported platform %r", sys.platform)