Edouard@3420: # vi: set ft=python sw=4 ts=4 et: Edouard@3420: Edouard@3420: '''monotonic time for Python 2 and 3, on Linux, FreeBSD, Mac OS X, and Windows. Edouard@3420: Edouard@3420: Copyright 2010, 2011, 2017 Gavin Beatty Edouard@3420: Edouard@3420: Permission is hereby granted, free of charge, to any person obtaining a copy of Edouard@3420: this software and associated documentation files (the "Software"), to deal in Edouard@3420: the Software without restriction, including without limitation the rights to Edouard@3420: use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies Edouard@3420: of the Software, and to permit persons to whom the Software is furnished to do Edouard@3420: so, subject to the following conditions: Edouard@3420: Edouard@3420: The above copyright notice and this permission notice shall be included in all Edouard@3420: copies or substantial portions of the Software. Edouard@3420: Edouard@3420: THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR Edouard@3420: IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, Edouard@3420: FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE Edouard@3420: AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER Edouard@3420: LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, Edouard@3420: OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE Edouard@3420: SOFTWARE. Edouard@3420: ''' Edouard@3420: Edouard@3420: from __future__ import print_function Edouard@3420: from __future__ import unicode_literals Edouard@3420: Edouard@3420: __author__ = 'Gavin Beatty ' Edouard@3420: __version__ = '2.1.0.dev0' Edouard@3420: __date__ = '2017-05-28' Edouard@3420: __all__ = ['monotonic'] Edouard@3420: Edouard@3420: import contextlib Edouard@3420: import ctypes Edouard@3420: import errno Edouard@3420: import os Edouard@3420: import platform Edouard@3420: import sys Edouard@3420: import time Edouard@3420: Edouard@3420: _machine64 = (platform.machine(), sys.maxsize > 2**32) Edouard@3420: Edouard@3420: Edouard@3420: class _NS(): Edouard@3420: pass Edouard@3420: Edouard@3420: Edouard@3420: class _mach_timespec(ctypes.Structure): Edouard@3420: _fields_ = [('tv_sec', ctypes.c_uint), ('tv_nsec', ctypes.c_int)] Edouard@3420: Edouard@3420: Edouard@3420: class _posix_timespec(ctypes.Structure): Edouard@3420: _fields_ = [('tv_sec', ctypes.c_long), ('tv_nsec', ctypes.c_long)] Edouard@3420: Edouard@3420: Edouard@3420: def _timespec_to_seconds(ts): Edouard@3420: return float(ts.tv_sec) + float(ts.tv_nsec) * 1e-9 Edouard@3420: Edouard@3420: Edouard@3420: def _get_ctypes_libmacho_macho_functions(): Edouard@3420: libmacho = ctypes.CDLL('/usr/lib/system/libmacho.dylib', use_errno=True) Edouard@3420: macho = _NS() Edouard@3420: macho.get_host = libmacho.mach_host_self Edouard@3420: macho.get_host.argtypes = [] Edouard@3420: macho.get_host.restype = ctypes.c_uint Edouard@3420: macho.get_clock = libmacho.host_get_clock_service Edouard@3420: macho.get_clock.argtypes = [ctypes.c_uint, Edouard@3420: ctypes.c_int, Edouard@3420: ctypes.POINTER(ctypes.c_uint) Edouard@3420: ] Edouard@3420: macho.get_time = libmacho.clock_get_time Edouard@3420: macho.get_time.argtypes = [ctypes.c_uint, ctypes.POINTER(_mach_timespec)] Edouard@3420: macho.deallocate = libmacho.mach_port_deallocate Edouard@3420: macho.deallocate.argtypes = [ctypes.c_uint, ctypes.c_uint] Edouard@3420: return libmacho, macho Edouard@3420: Edouard@3420: Edouard@3420: def _get_ctypes_clock_gettime(library): Edouard@3420: clock_gettime = ctypes.CDLL(library, use_errno=True).clock_gettime Edouard@3420: clock_gettime.argtypes = [ctypes.c_int, ctypes.POINTER(_posix_timespec)] Edouard@3420: return clock_gettime Edouard@3420: Edouard@3420: Edouard@3420: def _call_ctypes_clock_gettime(clock_gettime, clockid): Edouard@3420: timespec = _posix_timespec() Edouard@3420: ret = clock_gettime(clockid, ctypes.pointer(timespec)) Edouard@3420: if int(ret) != 0: Edouard@3420: errno_ = ctypes.get_errno() Edouard@3420: raise OSError(errno_, os.strerror(errno_)) Edouard@3420: return timespec Edouard@3420: Edouard@3420: Edouard@3420: _py_monotonic = getattr(time, 'monotonic', None) Edouard@3420: if _py_monotonic is not None: Edouard@3420: monotonic = _py_monotonic Edouard@3420: elif sys.platform.startswith('linux'): Edouard@3420: _clock_gettime = _get_ctypes_clock_gettime('librt.so.1') Edouard@3420: Edouard@3420: def monotonic(): Edouard@3420: clockid = ctypes.c_int(1) Edouard@3420: timespec = _call_ctypes_clock_gettime(_clock_gettime, clockid) Edouard@3420: return _timespec_to_seconds(timespec) Edouard@3420: elif sys.platform.startswith('freebsd'): Edouard@3420: _clock_gettime = _get_ctypes_clock_gettime('libc.so') Edouard@3420: Edouard@3420: def monotonic(): Edouard@3420: clockid = ctypes.c_int(4) Edouard@3420: timespec = _call_ctypes_clock_gettime(_clock_gettime, clockid) Edouard@3420: return _timespec_to_seconds(timespec) Edouard@3420: elif sys.platform.startswith('darwin') and _machine64 == ('x86_64', True): Edouard@3420: _libmacho, _macho = _get_ctypes_libmacho_macho_functions() Edouard@3420: Edouard@3420: @contextlib.contextmanager Edouard@3420: def _deallocate(task, port): Edouard@3420: try: Edouard@3420: yield Edouard@3420: finally: Edouard@3420: if int(_macho.deallocate(task, port)) == 0: Edouard@3420: return Edouard@3420: errno_ = ctypes.get_errno() Edouard@3420: raise OSError(errno_, os.strerror(errno_)) Edouard@3420: Edouard@3420: def monotonic(): Edouard@3420: task = ctypes.c_uint.in_dll(_libmacho, 'mach_task_self_') Edouard@3420: host = _macho.get_host() Edouard@3420: with _deallocate(task, host): Edouard@3420: clock = ctypes.c_uint(0) Edouard@3420: clockid = ctypes.c_int(0) Edouard@3420: ret = _macho.get_clock(host, clockid, ctypes.pointer(clock)) Edouard@3420: if int(ret) != 0: Edouard@3420: errno_ = ctypes.get_errno() Edouard@3420: raise OSError(errno_, os.strerror(errno_)) Edouard@3420: with _deallocate(task, clock): Edouard@3420: timespec = _mach_timespec() Edouard@3420: ret = _macho.get_time(clock, ctypes.pointer(timespec)) Edouard@3420: if int(ret) != 0: Edouard@3420: errno_ = ctypes.get_errno() Edouard@3420: raise OSError(errno_, os.strerror(errno_)) Edouard@3420: return _timespec_to_seconds(timespec) Edouard@3420: elif sys.platform.startswith('win32'): Edouard@3420: _GetTickCount = getattr(ctypes.windll.kernel32, 'GetTickCount64', None) Edouard@3420: Edouard@3420: if _GetTickCount is not None: Edouard@3420: _GetTickCount.restype = ctypes.c_uint64 Edouard@3420: else: Edouard@3420: _GetTickCount = ctypes.windll.kernel32.GetTickCount Edouard@3420: _GetTickCount.restype = ctypes.c_uint32 Edouard@3420: Edouard@3420: def monotonic(): Edouard@3420: return float(_GetTickCount()) * 1e-3 Edouard@3420: else: Edouard@3420: def monotonic(): Edouard@3420: msg = 'monotonic not supported on your platform' Edouard@3420: raise OSError(errno.ENOSYS, msg) Edouard@3420: Edouard@3420: Edouard@3420: if __name__ == '__main__': Edouard@3420: print(monotonic())