Rietveld Code Review Tool
Help | Bug tracker | Discussion group | Source code | Sign in
(98678)

Side by Side Diff: Python/pytime.c

Issue 22043: Use a monotonic clock to compute timeouts
Patch Set: Created 5 years, 3 months ago
Left:
Right:
Use n/p to move between diff chunks; N/P to move between comments. Please Sign in to add in-line comments.
Jump to:
View unified diff | Download patch
« Modules/_threadmodule.c ('K') | « Modules/timemodule.c ('k') | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 #include "Python.h" 1 #include "Python.h"
2 #ifdef MS_WINDOWS 2 #ifdef MS_WINDOWS
3 #include <windows.h> 3 #include <windows.h>
4 #endif 4 #endif
5 5
6 #if defined(__APPLE__) && defined(HAVE_GETTIMEOFDAY) && defined(HAVE_FTIME) 6 #if defined(__APPLE__) && defined(HAVE_GETTIMEOFDAY) && defined(HAVE_FTIME)
7 /* 7 /*
8 * _PyTime_gettimeofday falls back to ftime when getttimeofday fails because t he latter 8 * _PyTime_gettimeofday falls back to ftime when getttimeofday fails because t he latter
9 * might fail on some platforms. This fallback is unwanted on MacOSX because 9 * might fail on some platforms. This fallback is unwanted on MacOSX because
10 * that makes it impossible to use a binary build on OSX 10.4 on earlier 10 * that makes it impossible to use a binary build on OSX 10.4 on earlier
11 * releases of the OS. Therefore claim we don't support ftime. 11 * releases of the OS. Therefore claim we don't support ftime.
12 */ 12 */
13 # undef HAVE_FTIME 13 # undef HAVE_FTIME
14 #endif 14 #endif
15 15
16 #if defined(HAVE_FTIME) && !defined(MS_WINDOWS) 16 #if defined(HAVE_FTIME) && !defined(MS_WINDOWS)
17 #include <sys/timeb.h> 17 #include <sys/timeb.h>
18 extern int ftime(struct timeb *); 18 extern int ftime(struct timeb *);
19 #endif 19 #endif
20 20
21 /* one seconde in nanoseconds */
22 #define SEC_IN_NANOSECONDS 1000000000
Charles-François Natali 2014/07/31 07:33:20 Maybe add an explicit 'UL' suffix?
haypo 2014/07/31 11:17:44 Oh yes.
23
21 static void 24 static void
22 pygettimeofday(_PyTime_timeval *tp, _Py_clock_info_t *info) 25 pygettimeofday(_PyTime_timespec *ts, _Py_clock_info_t *info)
23 { 26 {
24 #ifdef MS_WINDOWS 27 #ifdef MS_WINDOWS
25 FILETIME system_time; 28 FILETIME system_time;
26 ULARGE_INTEGER large; 29 ULARGE_INTEGER large;
27 ULONGLONG microseconds; 30 ULONGLONG nanoseconds;
28 31
29 GetSystemTimeAsFileTime(&system_time); 32 GetSystemTimeAsFileTime(&system_time);
30 large.u.LowPart = system_time.dwLowDateTime; 33 large.u.LowPart = system_time.dwLowDateTime;
31 large.u.HighPart = system_time.dwHighDateTime; 34 large.u.HighPart = system_time.dwHighDateTime;
32 /* 11,644,473,600,000,000: number of microseconds between 35 /* 11,644,473,600,000,000,000: number of nanoseconds between
33 the 1st january 1601 and the 1st january 1970 (369 years + 89 leap 36 the 1st january 1601 and the 1st january 1970 (369 years + 89 leap
34 days). */ 37 days). */
35 microseconds = large.QuadPart / 10 - 11644473600000000; 38 nanoseconds = large.QuadPart * 100 - 11644473600000000000;
36 tp->tv_sec = microseconds / 1000000; 39 ts->tv_sec = nanoseconds / SEC_IN_NANOSECONDS;
37 tp->tv_usec = microseconds % 1000000; 40 ts->tv_nsec = nanoseconds % SEC_IN_NANOSECONDS;
38 if (info) { 41 if (info) {
39 DWORD timeAdjustment, timeIncrement; 42 DWORD timeAdjustment, timeIncrement;
40 BOOL isTimeAdjustmentDisabled; 43 BOOL isTimeAdjustmentDisabled;
41 44
42 info->implementation = "GetSystemTimeAsFileTime()"; 45 info->implementation = "GetSystemTimeAsFileTime()";
43 info->monotonic = 0; 46 info->monotonic = 0;
44 (void) GetSystemTimeAdjustment(&timeAdjustment, &timeIncrement, 47 (void) GetSystemTimeAdjustment(&timeAdjustment, &timeIncrement,
45 &isTimeAdjustmentDisabled); 48 &isTimeAdjustmentDisabled);
46 info->resolution = timeIncrement * 1e-7; 49 info->resolution = timeIncrement * 1e-7;
47 info->adjustable = 1; 50 info->adjustable = 1;
48 } 51 }
49 #else 52 #else
50 /* There are three ways to get the time: 53 /* There are four ways to get the time:
51 (1) gettimeofday() -- resolution in microseconds 54 (1) clock_gettime() -- resolution in nanoseconds
52 (2) ftime() -- resolution in milliseconds 55 (2) gettimeofday() -- resolution in microseconds
53 (3) time() -- resolution in seconds 56 (3) ftime() -- resolution in milliseconds
57 (4) time() -- resolution in seconds
54 In all cases the return value in a timeval struct. 58 In all cases the return value in a timeval struct.
55 Since on some systems (e.g. SCO ODT 3.0) gettimeofday() may 59 Since on some systems (e.g. SCO ODT 3.0) gettimeofday() may
56 fail, so we fall back on ftime() or time(). 60 fail, so we fall back on ftime() or time().
57 Note: clock resolution does not imply clock accuracy! */ 61 Note: clock resolution does not imply clock accuracy! */
58 62
63 #if defined(HAVE_CLOCK_GETTIME) || defined(HAVE_GETTIMEOFDAY)
64 int err;
65 #endif
66 #ifdef HAVE_CLOCK_GETTIME
67 struct timespec tp;
68 #endif
59 #ifdef HAVE_GETTIMEOFDAY 69 #ifdef HAVE_GETTIMEOFDAY
60 int err; 70 struct timeval tv;
71 #endif
72 #ifdef HAVE_FTIME
73 struct timeb tb;
74 #endif
75
76 #ifdef HAVE_CLOCK_GETTIME
77 err = clock_gettime(CLOCK_REALTIME, &tp);
78 if (err == 0) {
79 if (info) {
80 struct timespec res;
81 info->implementation = "clock_gettime(CLOCK_REALTIME)";
82 info->monotonic = 0;
83 info->adjustable = 1;
84 if (clock_getres(CLOCK_REALTIME, &res) == 0)
85 info->resolution = res.tv_sec + res.tv_nsec * 1e-9;
86 else
87 info->resolution = 1e-9;
88 }
89 ts->tv_sec = tp.tv_sec;
90 ts->tv_nsec = tp.tv_nsec;
91 return;
92 }
93 #endif
94
95 #ifdef HAVE_GETTIMEOFDAY
61 #ifdef GETTIMEOFDAY_NO_TZ 96 #ifdef GETTIMEOFDAY_NO_TZ
62 err = gettimeofday(tp); 97 err = gettimeofday(&tv);
63 #else 98 #else
64 err = gettimeofday(tp, (struct timezone *)NULL); 99 err = gettimeofday(&tv, (struct timezone *)NULL);
65 #endif 100 #endif
66 if (err == 0) { 101 if (err == 0) {
102 ts->tv_sec = tv.tv_sec;
103 ts->tv_nsec = tv.tv_usec * 1000;
67 if (info) { 104 if (info) {
68 info->implementation = "gettimeofday()"; 105 info->implementation = "gettimeofday()";
69 info->resolution = 1e-6; 106 info->resolution = 1e-6;
70 info->monotonic = 0; 107 info->monotonic = 0;
71 info->adjustable = 1; 108 info->adjustable = 1;
72 } 109 }
73 return; 110 return;
74 } 111 }
75 #endif /* HAVE_GETTIMEOFDAY */ 112 #endif /* HAVE_GETTIMEOFDAY */
76 113
77 #if defined(HAVE_FTIME) 114 #ifdef HAVE_FTIME
78 { 115 ftime(&tb);
79 struct timeb t; 116 ts->tv_sec = tb.time;
80 ftime(&t); 117 ts->tv_nsec = tb.millitm * 1000000;
81 tp->tv_sec = t.time; 118 if (info) {
82 tp->tv_usec = t.millitm * 1000; 119 info->implementation = "ftime()";
83 if (info) { 120 info->resolution = 1e-3;
84 info->implementation = "ftime()"; 121 info->monotonic = 0;
85 info->resolution = 1e-3; 122 info->adjustable = 1;
86 info->monotonic = 0;
87 info->adjustable = 1;
88 }
89 } 123 }
90 #else /* !HAVE_FTIME */ 124 #else /* !HAVE_FTIME */
91 tp->tv_sec = time(NULL); 125 tp->tv_sec = time(NULL);
92 tp->tv_usec = 0; 126 tp->tv_nsec = 0;
93 if (info) { 127 if (info) {
94 info->implementation = "time()"; 128 info->implementation = "time()";
95 info->resolution = 1.0; 129 info->resolution = 1.0;
96 info->monotonic = 0; 130 info->monotonic = 0;
97 info->adjustable = 1; 131 info->adjustable = 1;
98 } 132 }
99 #endif /* !HAVE_FTIME */ 133 #endif /* !HAVE_FTIME */
100 134
101 #endif /* MS_WINDOWS */ 135 #endif /* MS_WINDOWS */
102 } 136 }
103 137
104 void 138 void
105 _PyTime_gettimeofday(_PyTime_timeval *tp) 139 _PyTime_gettimeofday(_PyTime_timespec *tp)
106 { 140 {
107 pygettimeofday(tp, NULL); 141 pygettimeofday(tp, NULL);
108 } 142 }
109 143
110 void 144 void
111 _PyTime_gettimeofday_info(_PyTime_timeval *tp, _Py_clock_info_t *info) 145 _PyTime_gettimeofday_info(_PyTime_timespec *tp, _Py_clock_info_t *info)
112 { 146 {
113 pygettimeofday(tp, info); 147 pygettimeofday(tp, info);
114 } 148 }
115 149
116 static void 150 static void
117 error_time_t_overflow(void) 151 error_time_t_overflow(void)
118 { 152 {
119 PyErr_SetString(PyExc_OverflowError, 153 PyErr_SetString(PyExc_OverflowError,
120 "timestamp out of range for platform time_t"); 154 "timestamp out of range for platform time_t");
121 } 155 }
(...skipping 19 matching lines...) Expand all
141 175
142 PyObject * 176 PyObject *
143 _PyLong_FromTime_t(time_t t) 177 _PyLong_FromTime_t(time_t t)
144 { 178 {
145 #if defined(HAVE_LONG_LONG) && SIZEOF_TIME_T == SIZEOF_LONG_LONG 179 #if defined(HAVE_LONG_LONG) && SIZEOF_TIME_T == SIZEOF_LONG_LONG
146 return PyLong_FromLongLong((PY_LONG_LONG)t); 180 return PyLong_FromLongLong((PY_LONG_LONG)t);
147 #else 181 #else
148 assert(sizeof(time_t) <= sizeof(long)); 182 assert(sizeof(time_t) <= sizeof(long));
149 return PyLong_FromLong((long)t); 183 return PyLong_FromLong((long)t);
150 #endif 184 #endif
185 }
186
187 static int
188 _PyTime_DoubleToDenominator(double d, time_t *sec, long *numerator,
189 double denominator, _PyTime_round_t round,
190 int raise)
191 {
192 double intpart, err;
193 /* volatile avoids unsafe optimization on float enabled by gcc -O3 */
194 volatile double floatpart;
195
196 floatpart = modf(d, &intpart);
197 if (floatpart < 0) {
198 floatpart = 1.0 + floatpart;
199 intpart -= 1.0;
200 }
201
202 floatpart *= denominator;
203 if (round == _PyTime_ROUND_UP) {
204 if (intpart >= 0) {
205 floatpart = ceil(floatpart);
206 if (floatpart >= denominator) {
207 floatpart -= denominator;
208 intpart += 1.0;
209 }
210 }
211 else {
212 floatpart = floor(floatpart);
213 }
214 }
215
216 *sec = (time_t)intpart;
217 err = intpart - (double)*sec;
218 if (err <= -1.0 || err >= 1.0) {
219 if (raise)
220 error_time_t_overflow();
221 return -1;
222 }
223
224 *numerator = (long)floatpart;
225 return 0;
151 } 226 }
152 227
153 static int 228 static int
154 _PyTime_ObjectToDenominator(PyObject *obj, time_t *sec, long *numerator, 229 _PyTime_ObjectToDenominator(PyObject *obj, time_t *sec, long *numerator,
155 double denominator, _PyTime_round_t round) 230 double denominator, _PyTime_round_t round)
156 { 231 {
157 assert(denominator <= LONG_MAX); 232 assert(denominator <= LONG_MAX);
158 if (PyFloat_Check(obj)) { 233 if (PyFloat_Check(obj)) {
159 double d, intpart, err; 234 double d = PyFloat_AsDouble(obj);
160 /* volatile avoids unsafe optimization on float enabled by gcc -O3 */ 235 return _PyTime_DoubleToDenominator(d, sec, numerator,
161 volatile double floatpart; 236 denominator, round, 1);
162
163 d = PyFloat_AsDouble(obj);
164 floatpart = modf(d, &intpart);
165 if (floatpart < 0) {
166 floatpart = 1.0 + floatpart;
167 intpart -= 1.0;
168 }
169
170 floatpart *= denominator;
171 if (round == _PyTime_ROUND_UP) {
172 if (intpart >= 0) {
173 floatpart = ceil(floatpart);
174 if (floatpart >= denominator) {
175 floatpart = 0.0;
176 intpart += 1.0;
177 }
178 }
179 else {
180 floatpart = floor(floatpart);
181 }
182 }
183
184 *sec = (time_t)intpart;
185 err = intpart - (double)*sec;
186 if (err <= -1.0 || err >= 1.0) {
187 error_time_t_overflow();
188 return -1;
189 }
190
191 *numerator = (long)floatpart;
192 return 0;
193 } 237 }
194 else { 238 else {
195 *sec = _PyLong_AsTime_t(obj); 239 *sec = _PyLong_AsTime_t(obj);
196 if (*sec == (time_t)-1 && PyErr_Occurred()) 240 if (*sec == (time_t)-1 && PyErr_Occurred())
197 return -1; 241 return -1;
198 *numerator = 0; 242 *numerator = 0;
199 return 0; 243 return 0;
200 } 244 }
201 } 245 }
202 246
(...skipping 33 matching lines...) Expand 10 before | Expand all | Expand 10 after
236 _PyTime_round_t round) 280 _PyTime_round_t round)
237 { 281 {
238 return _PyTime_ObjectToDenominator(obj, sec, nsec, 1e9, round); 282 return _PyTime_ObjectToDenominator(obj, sec, nsec, 1e9, round);
239 } 283 }
240 284
241 int 285 int
242 _PyTime_ObjectToTimeval(PyObject *obj, time_t *sec, long *usec, 286 _PyTime_ObjectToTimeval(PyObject *obj, time_t *sec, long *usec,
243 _PyTime_round_t round) 287 _PyTime_round_t round)
244 { 288 {
245 return _PyTime_ObjectToDenominator(obj, sec, usec, 1e6, round); 289 return _PyTime_ObjectToDenominator(obj, sec, usec, 1e6, round);
290 }
291
292 static int
293 pymonotonic(_PyTime_timespec *ts, _Py_clock_info_t *info, int raise)
294 {
295 #if defined(MS_WINDOWS)
296 static ULONGLONG (*GetTickCount64) (void) = NULL;
297 static ULONGLONG (CALLBACK *Py_GetTickCount64)(void);
298 static int has_getickcount64 = -1;
299 double result;
300
301 if (has_getickcount64 == -1) {
302 /* GetTickCount64() was added to Windows Vista */
303 if (winver.dwMajorVersion >= 6) {
304 HINSTANCE hKernel32;
305 hKernel32 = GetModuleHandleW(L"KERNEL32");
306 *(FARPROC*)&Py_GetTickCount64 = GetProcAddress(hKernel32,
307 "GetTickCount64");
308 has_getickcount64 = (Py_GetTickCount64 != NULL);
309 }
310 else
311 has_getickcount64 = 0;
312 }
313
314 if (has_getickcount64) {
315 ULONGLONG ticks;
316 ticks = Py_GetTickCount64();
317 ts->tv_sec = ticks / 1000;
318 ts->tv_nsec = (ticks % 1000) * 1000000;
319 }
320 else {
321 static DWORD last_ticks = 0;
322 DWORD ticks;
323 static ULONGLONG delta = 0;
324 ULONGLONG result;
325
326 ticks = GetTickCount();
327 if (ticks < last_ticks)
328 delta += (ULONGLONG)1 << 32;
329 last_ticks = ticks;
330
331 result = (ULONGLONG)ticks + delta;
332
333 ts->tv_sec = result / 1000;
334 ts->tv_nsec = (result % 1000) * 1000000;
335 }
336
337 if (info) {
338 DWORD timeAdjustment, timeIncrement;
339 BOOL isTimeAdjustmentDisabled, ok;
340 if (has_getickcount64)
341 info->implementation = "GetTickCount64()";
342 else
343 info->implementation = "GetTickCount()";
344 info->monotonic = 1;
345 ok = GetSystemTimeAdjustment(&timeAdjustment, &timeIncrement,
346 &isTimeAdjustmentDisabled);
347 if (!ok) {
348 if (raise)
349 PyErr_SetFromWindowsErr(0);
350 goto error;
351 }
352 info->resolution = timeIncrement * 1e-7;
353 info->adjustable = 0;
354 }
355 return 0;
356
357 #elif defined(__APPLE__)
358 static mach_timebase_info_data_t timebase;
359 uint64_t time;
360 double secs;
361
362 if (timebase.denom == 0) {
363 /* According to the Technical Q&A QA1398, mach_timebase_info() cannot
364 fail: https://developer.apple.com/library/mac/#qa/qa1398/ */
365 (void)mach_timebase_info(&timebase);
366 }
367
368 time = mach_absolute_time();
369 secs = (double)time * timebase.numer / timebase.denom * 1e-9;
370
371 if (_PyTime_DoubleToDenominator(secs, &ts->tv_sec, &ts->nsec,
372 1e9, _PyTime_ROUND_DOWN, raise) < 0)
373 goto error;
374
375 if (info) {
376 info->implementation = "mach_absolute_time()";
377 info->resolution = (double)timebase.numer / timebase.denom * 1e-9;
378 info->monotonic = 1;
379 info->adjustable = 0;
380 }
381 return 0;
382
383 #elif defined(HAVE_CLOCK_GETTIME) && (defined(CLOCK_HIGHRES) || defined(CLOCK_MO NOTONIC))
384 struct timespec tp;
385 #ifdef CLOCK_HIGHRES
386 const clockid_t clk_id = CLOCK_HIGHRES;
387 const char *function = "clock_gettime(CLOCK_HIGHRES)";
388 #else
389 const clockid_t clk_id = CLOCK_MONOTONIC;
390 const char *function = "clock_gettime(CLOCK_MONOTONIC)";
391 #endif
392
393 if (clock_gettime(clk_id, &tp) != 0) {
394 if (raise)
395 PyErr_SetFromErrno(PyExc_OSError);
396 goto error;
397 }
398 ts->tv_sec = tp.tv_sec;
399 ts->tv_nsec = tp.tv_nsec;
400
401 if (info) {
402 struct timespec res;
403 info->monotonic = 1;
404 info->implementation = function;
405 info->adjustable = 0;
406 if (clock_getres(clk_id, &res) == 0)
407 info->resolution = res.tv_sec + res.tv_nsec * 1e-9;
408 else
409 info->resolution = 1e-9;
410 }
411 return 0;
412 #endif
413
414 error:
415 ts->tv_sec = 0;
416 ts->tv_nsec = 0;
417 return -1;
418 }
419
420 void
421 _PyTime_monotonic(_PyTime_timespec *ts)
422 {
423 (void)pymonotonic(ts, NULL, 0);
424 }
Charles-François Natali 2014/07/31 07:33:20 I find the error handling a bit surprising: if pym
haypo 2014/07/31 11:17:44 Would it makes sense to call _PyTime_monotonic_inf
425
426 int
427 _PyTime_monotonic_info(_PyTime_timespec *ts, _Py_clock_info_t *info)
428 {
429 return pymonotonic(ts, info, 1);
246 } 430 }
247 431
248 void 432 void
249 _PyTime_Init() 433 _PyTime_Init()
250 { 434 {
251 /* Do nothing. Needed to force linking. */ 435 /* Do nothing. Needed to force linking. */
252 } 436 }
OLDNEW
« Modules/_threadmodule.c ('K') | « Modules/timemodule.c ('k') | no next file » | no next file with comments »

RSS Feeds Recent Issues | This issue
This is Rietveld 894c83f36cb7+