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 <[email protected] >
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 time
42
+
43
+
44
+ __all__ = ('monotonic' ,)
45
+
46
+
47
+ try :
48
+ monotonic = time .monotonic
49
+ except AttributeError :
50
+ import ctypes
51
+ import ctypes .util
52
+ import os
53
+ import sys
54
+ import threading
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 ))
0 commit comments