diff options
Diffstat (limited to 'src/lib/mhd_mono_clock.c')
-rw-r--r-- | src/lib/mhd_mono_clock.c | 377 |
1 files changed, 377 insertions, 0 deletions
diff --git a/src/lib/mhd_mono_clock.c b/src/lib/mhd_mono_clock.c new file mode 100644 index 00000000..97dbfb9f --- /dev/null +++ b/src/lib/mhd_mono_clock.c | |||
@@ -0,0 +1,377 @@ | |||
1 | /* | ||
2 | This file is part of libmicrohttpd | ||
3 | Copyright (C) 2015 Karlson2k (Evgeny Grin) | ||
4 | |||
5 | This library is free software; you can redistribute it and/or | ||
6 | modify it under the terms of the GNU Lesser General Public | ||
7 | License as published by the Free Software Foundation; either | ||
8 | version 2.1 of the License, or (at your option) any later version. | ||
9 | |||
10 | This library is distributed in the hope that it will be useful, | ||
11 | but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
12 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | ||
13 | Lesser General Public License for more details. | ||
14 | |||
15 | You should have received a copy of the GNU Lesser General Public | ||
16 | License along with this library; if not, write to the Free Software | ||
17 | Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA | ||
18 | */ | ||
19 | |||
20 | /** | ||
21 | * @file microhttpd/mhd_mono_clock.h | ||
22 | * @brief internal monotonic clock functions implementations | ||
23 | * @author Karlson2k (Evgeny Grin) | ||
24 | */ | ||
25 | |||
26 | #include "mhd_mono_clock.h" | ||
27 | |||
28 | #if defined(_WIN32) && ! defined(__CYGWIN__) && defined(HAVE_CLOCK_GETTIME) | ||
29 | /* Prefer native clock source over wrappers */ | ||
30 | #undef HAVE_CLOCK_GETTIME | ||
31 | #endif /* _WIN32 && ! __CYGWIN__ && HAVE_CLOCK_GETTIME */ | ||
32 | |||
33 | #ifdef HAVE_CLOCK_GETTIME | ||
34 | #include <time.h> | ||
35 | #endif /* HAVE_CLOCK_GETTIME */ | ||
36 | |||
37 | #ifdef HAVE_GETHRTIME | ||
38 | #ifdef HAVE_SYS_TIME_H | ||
39 | /* Solaris defines gethrtime() in sys/time.h */ | ||
40 | #include <sys/time.h> | ||
41 | #endif /* HAVE_SYS_TIME_H */ | ||
42 | #ifdef HAVE_TIME_H | ||
43 | /* HP-UX defines gethrtime() in time.h */ | ||
44 | #include <time.h> | ||
45 | #endif /* HAVE_TIME_H */ | ||
46 | #endif /* HAVE_GETHRTIME */ | ||
47 | |||
48 | #ifdef HAVE_CLOCK_GET_TIME | ||
49 | #include <mach/mach.h> | ||
50 | /* for host_get_clock_service(), mach_host_self(), mach_task_self() */ | ||
51 | #include <mach/clock.h> | ||
52 | /* for clock_get_time() */ | ||
53 | |||
54 | #define _MHD_INVALID_CLOCK_SERV ((clock_serv_t) -2) | ||
55 | |||
56 | static clock_serv_t mono_clock_service = _MHD_INVALID_CLOCK_SERV; | ||
57 | #endif /* HAVE_CLOCK_GET_TIME */ | ||
58 | |||
59 | #ifdef _WIN32 | ||
60 | #ifndef WIN32_LEAN_AND_MEAN | ||
61 | /* Do not include unneeded parts of W32 headers. */ | ||
62 | #define WIN32_LEAN_AND_MEAN 1 | ||
63 | #endif /* !WIN32_LEAN_AND_MEAN */ | ||
64 | #include <windows.h> | ||
65 | #include <stdint.h> | ||
66 | #endif /* _WIN32 */ | ||
67 | |||
68 | #ifdef HAVE_CLOCK_GETTIME | ||
69 | #ifdef CLOCK_REALTIME | ||
70 | #define _MHD_UNWANTED_CLOCK CLOCK_REALTIME | ||
71 | #else /* !CLOCK_REALTIME */ | ||
72 | #define _MHD_UNWANTED_CLOCK ((clockid_t) -2) | ||
73 | #endif /* !CLOCK_REALTIME */ | ||
74 | |||
75 | static clockid_t mono_clock_id = _MHD_UNWANTED_CLOCK; | ||
76 | #endif /* HAVE_CLOCK_GETTIME */ | ||
77 | |||
78 | /* sync clocks; reduce chance of value wrap */ | ||
79 | #if defined(HAVE_CLOCK_GETTIME) || defined(HAVE_CLOCK_GET_TIME) || defined(HAVE_GETHRTIME) | ||
80 | static time_t mono_clock_start; | ||
81 | #endif /* HAVE_CLOCK_GETTIME || HAVE_CLOCK_GET_TIME || HAVE_GETHRTIME */ | ||
82 | static time_t sys_clock_start; | ||
83 | #ifdef HAVE_GETHRTIME | ||
84 | static hrtime_t hrtime_start; | ||
85 | #endif /* HAVE_GETHRTIME */ | ||
86 | #ifdef _WIN32 | ||
87 | #if _WIN32_WINNT >= 0x0600 | ||
88 | static uint64_t tick_start; | ||
89 | #else /* _WIN32_WINNT < 0x0600 */ | ||
90 | static int64_t perf_freq; | ||
91 | static int64_t perf_start; | ||
92 | #endif /* _WIN32_WINNT < 0x0600 */ | ||
93 | #endif /* _WIN32 */ | ||
94 | |||
95 | |||
96 | |||
97 | /** | ||
98 | * Type of monotonic clock source | ||
99 | */ | ||
100 | enum _MHD_mono_clock_source | ||
101 | { | ||
102 | /** | ||
103 | * No monotonic clock | ||
104 | */ | ||
105 | _MHD_CLOCK_NO_SOURCE = 0, | ||
106 | |||
107 | /** | ||
108 | * clock_gettime() with specific clock | ||
109 | */ | ||
110 | _MHD_CLOCK_GETTIME, | ||
111 | |||
112 | /** | ||
113 | * clock_get_time() with specific clock service | ||
114 | */ | ||
115 | _MHD_CLOCK_GET_TIME, | ||
116 | |||
117 | /** | ||
118 | * gethrtime() / 1000000000 | ||
119 | */ | ||
120 | _MHD_CLOCK_GETHRTIME, | ||
121 | |||
122 | /** | ||
123 | * GetTickCount64() / 1000 | ||
124 | */ | ||
125 | _MHD_CLOCK_GETTICKCOUNT64, | ||
126 | |||
127 | /** | ||
128 | * QueryPerformanceCounter() / QueryPerformanceFrequency() | ||
129 | */ | ||
130 | _MHD_CLOCK_PERFCOUNTER | ||
131 | }; | ||
132 | |||
133 | |||
134 | /** | ||
135 | * Initialise monotonic seconds counter. | ||
136 | */ | ||
137 | void | ||
138 | MHD_monotonic_sec_counter_init (void) | ||
139 | { | ||
140 | #ifdef HAVE_CLOCK_GET_TIME | ||
141 | mach_timespec_t cur_time; | ||
142 | #endif /* HAVE_CLOCK_GET_TIME */ | ||
143 | enum _MHD_mono_clock_source mono_clock_source = _MHD_CLOCK_NO_SOURCE; | ||
144 | #ifdef HAVE_CLOCK_GETTIME | ||
145 | struct timespec ts; | ||
146 | |||
147 | mono_clock_id = _MHD_UNWANTED_CLOCK; | ||
148 | #endif /* HAVE_CLOCK_GETTIME */ | ||
149 | #ifdef HAVE_CLOCK_GET_TIME | ||
150 | mono_clock_service = _MHD_INVALID_CLOCK_SERV; | ||
151 | #endif /* HAVE_CLOCK_GET_TIME */ | ||
152 | |||
153 | /* just a little syntactic trick to get the | ||
154 | various following ifdef's to work out nicely */ | ||
155 | if (0) | ||
156 | { | ||
157 | } | ||
158 | else | ||
159 | #ifdef HAVE_CLOCK_GETTIME | ||
160 | #ifdef CLOCK_MONOTONIC_COARSE | ||
161 | /* Linux-specific fast value-getting clock */ | ||
162 | /* Can be affected by frequency adjustment and don't count time in suspend, */ | ||
163 | /* but preferred since it's fast */ | ||
164 | if (0 == clock_gettime (CLOCK_MONOTONIC_COARSE, | ||
165 | &ts)) | ||
166 | { | ||
167 | mono_clock_id = CLOCK_MONOTONIC_COARSE; | ||
168 | mono_clock_start = ts.tv_sec; | ||
169 | mono_clock_source = _MHD_CLOCK_GETTIME; | ||
170 | } | ||
171 | else | ||
172 | #endif /* CLOCK_MONOTONIC_COARSE */ | ||
173 | #ifdef CLOCK_MONOTONIC_FAST | ||
174 | /* FreeBSD/DragonFly fast value-getting clock */ | ||
175 | /* Can be affected by frequency adjustment, but preferred since it's fast */ | ||
176 | if (0 == clock_gettime (CLOCK_MONOTONIC_FAST, | ||
177 | &ts)) | ||
178 | { | ||
179 | mono_clock_id = CLOCK_MONOTONIC_FAST; | ||
180 | mono_clock_start = ts.tv_sec; | ||
181 | mono_clock_source = _MHD_CLOCK_GETTIME; | ||
182 | } | ||
183 | else | ||
184 | #endif /* CLOCK_MONOTONIC_COARSE */ | ||
185 | #ifdef CLOCK_MONOTONIC_RAW | ||
186 | /* Linux-specific clock */ | ||
187 | /* Not affected by frequency adjustment, but don't count time in suspend */ | ||
188 | if (0 == clock_gettime (CLOCK_MONOTONIC_RAW, | ||
189 | &ts)) | ||
190 | { | ||
191 | mono_clock_id = CLOCK_MONOTONIC_RAW; | ||
192 | mono_clock_start = ts.tv_sec; | ||
193 | mono_clock_source = _MHD_CLOCK_GETTIME; | ||
194 | } | ||
195 | else | ||
196 | #endif /* CLOCK_MONOTONIC_RAW */ | ||
197 | #ifdef CLOCK_BOOTTIME | ||
198 | /* Linux-specific clock */ | ||
199 | /* Count time in suspend so it's real monotonic on Linux, */ | ||
200 | /* but can be slower value-getting than other clocks */ | ||
201 | if (0 == clock_gettime (CLOCK_BOOTTIME, | ||
202 | &ts)) | ||
203 | { | ||
204 | mono_clock_id = CLOCK_BOOTTIME; | ||
205 | mono_clock_start = ts.tv_sec; | ||
206 | mono_clock_source = _MHD_CLOCK_GETTIME; | ||
207 | } | ||
208 | else | ||
209 | #endif /* CLOCK_BOOTTIME */ | ||
210 | #ifdef CLOCK_MONOTONIC | ||
211 | /* Monotonic clock */ | ||
212 | /* Widely supported, may be affected by frequency adjustment */ | ||
213 | /* On Linux it's not truly monotonic as it doesn't count time in suspend */ | ||
214 | if (0 == clock_gettime (CLOCK_MONOTONIC, | ||
215 | &ts)) | ||
216 | { | ||
217 | mono_clock_id = CLOCK_MONOTONIC; | ||
218 | mono_clock_start = ts.tv_sec; | ||
219 | mono_clock_source = _MHD_CLOCK_GETTIME; | ||
220 | } | ||
221 | else | ||
222 | #endif /* CLOCK_BOOTTIME */ | ||
223 | #endif /* HAVE_CLOCK_GETTIME */ | ||
224 | #ifdef HAVE_CLOCK_GET_TIME | ||
225 | /* Darwin-specific monotonic clock */ | ||
226 | /* Should be monotonic as clock_set_time function always unconditionally */ | ||
227 | /* failed on latest kernels */ | ||
228 | if ( (KERN_SUCCESS == host_get_clock_service (mach_host_self(), | ||
229 | SYSTEM_CLOCK, | ||
230 | &mono_clock_service)) && | ||
231 | (KERN_SUCCESS == clock_get_time (mono_clock_service, | ||
232 | &cur_time)) ) | ||
233 | { | ||
234 | mono_clock_start = cur_time.tv_sec; | ||
235 | mono_clock_source = _MHD_CLOCK_GET_TIME; | ||
236 | } | ||
237 | else | ||
238 | #endif /* HAVE_CLOCK_GET_TIME */ | ||
239 | #ifdef _WIN32 | ||
240 | #if _WIN32_WINNT >= 0x0600 | ||
241 | /* W32 Vista or later specific monotonic clock */ | ||
242 | /* Available since Vista, ~15ms accuracy */ | ||
243 | if (1) | ||
244 | { | ||
245 | tick_start = GetTickCount64 (); | ||
246 | mono_clock_source = _MHD_CLOCK_GETTICKCOUNT64; | ||
247 | } | ||
248 | else | ||
249 | #else /* _WIN32_WINNT < 0x0600 */ | ||
250 | /* W32 specific monotonic clock */ | ||
251 | /* Available on Windows 2000 and later */ | ||
252 | if (1) | ||
253 | { | ||
254 | LARGE_INTEGER freq; | ||
255 | LARGE_INTEGER perf_counter; | ||
256 | |||
257 | QueryPerformanceFrequency (&freq); /* never fail on XP and later */ | ||
258 | QueryPerformanceCounter (&perf_counter); /* never fail on XP and later */ | ||
259 | perf_freq = freq.QuadPart; | ||
260 | perf_start = perf_counter.QuadPart; | ||
261 | mono_clock_source = _MHD_CLOCK_PERFCOUNTER; | ||
262 | } | ||
263 | else | ||
264 | #endif /* _WIN32_WINNT < 0x0600 */ | ||
265 | #endif /* _WIN32 */ | ||
266 | #ifdef HAVE_CLOCK_GETTIME | ||
267 | #ifdef CLOCK_HIGHRES | ||
268 | /* Solaris-specific monotonic high-resolution clock */ | ||
269 | /* Not preferred due to be potentially resource-hungry */ | ||
270 | if (0 == clock_gettime (CLOCK_HIGHRES, | ||
271 | &ts)) | ||
272 | { | ||
273 | mono_clock_id = CLOCK_HIGHRES; | ||
274 | mono_clock_start = ts.tv_sec; | ||
275 | mono_clock_source = _MHD_CLOCK_GETTIME; | ||
276 | } | ||
277 | else | ||
278 | #endif /* CLOCK_HIGHRES */ | ||
279 | #endif /* HAVE_CLOCK_GETTIME */ | ||
280 | #ifdef HAVE_GETHRTIME | ||
281 | /* HP-UX and Solaris monotonic clock */ | ||
282 | /* Not preferred due to be potentially resource-hungry */ | ||
283 | if (1) | ||
284 | { | ||
285 | hrtime_start = gethrtime (); | ||
286 | mono_clock_source = _MHD_CLOCK_GETHRTIME; | ||
287 | } | ||
288 | else | ||
289 | #endif /* HAVE_GETHRTIME */ | ||
290 | { | ||
291 | /* no suitable clock source was found */ | ||
292 | mono_clock_source = _MHD_CLOCK_NO_SOURCE; | ||
293 | } | ||
294 | |||
295 | #ifdef HAVE_CLOCK_GET_TIME | ||
296 | if ( (_MHD_CLOCK_GET_TIME != mono_clock_source) && | ||
297 | (_MHD_INVALID_CLOCK_SERV != mono_clock_service) ) | ||
298 | { | ||
299 | /* clock service was initialised but clock_get_time failed */ | ||
300 | mach_port_deallocate (mach_task_self(), | ||
301 | mono_clock_service); | ||
302 | mono_clock_service = _MHD_INVALID_CLOCK_SERV; | ||
303 | } | ||
304 | #else | ||
305 | (void) mono_clock_source; /* avoid compiler warning */ | ||
306 | #endif /* HAVE_CLOCK_GET_TIME */ | ||
307 | |||
308 | sys_clock_start = time (NULL); | ||
309 | } | ||
310 | |||
311 | |||
312 | /** | ||
313 | * Deinitialise monotonic seconds counter by freeing any allocated resources | ||
314 | */ | ||
315 | void | ||
316 | MHD_monotonic_sec_counter_finish (void) | ||
317 | { | ||
318 | #ifdef HAVE_CLOCK_GET_TIME | ||
319 | if (_MHD_INVALID_CLOCK_SERV != mono_clock_service) | ||
320 | { | ||
321 | mach_port_deallocate (mach_task_self(), | ||
322 | mono_clock_service); | ||
323 | mono_clock_service = _MHD_INVALID_CLOCK_SERV; | ||
324 | } | ||
325 | #endif /* HAVE_CLOCK_GET_TIME */ | ||
326 | } | ||
327 | |||
328 | |||
329 | /** | ||
330 | * Monotonic seconds counter, useful for timeout calculation. | ||
331 | * Tries to be not affected by manually setting the system real time | ||
332 | * clock or adjustments by NTP synchronization. | ||
333 | * | ||
334 | * @return number of seconds from some fixed moment | ||
335 | */ | ||
336 | time_t | ||
337 | MHD_monotonic_sec_counter (void) | ||
338 | { | ||
339 | #ifdef HAVE_CLOCK_GETTIME | ||
340 | struct timespec ts; | ||
341 | |||
342 | if ( (_MHD_UNWANTED_CLOCK != mono_clock_id) && | ||
343 | (0 == clock_gettime (mono_clock_id , | ||
344 | &ts)) ) | ||
345 | return ts.tv_sec - mono_clock_start; | ||
346 | #endif /* HAVE_CLOCK_GETTIME */ | ||
347 | #ifdef HAVE_CLOCK_GET_TIME | ||
348 | if (_MHD_INVALID_CLOCK_SERV != mono_clock_service) | ||
349 | { | ||
350 | mach_timespec_t cur_time; | ||
351 | |||
352 | if (KERN_SUCCESS == clock_get_time(mono_clock_service, | ||
353 | &cur_time)) | ||
354 | return cur_time.tv_sec - mono_clock_start; | ||
355 | } | ||
356 | #endif /* HAVE_CLOCK_GET_TIME */ | ||
357 | #if defined(_WIN32) | ||
358 | #if _WIN32_WINNT >= 0x0600 | ||
359 | if (1) | ||
360 | return (time_t)(((uint64_t)(GetTickCount64() - tick_start)) / 1000); | ||
361 | #else /* _WIN32_WINNT < 0x0600 */ | ||
362 | if (0 != perf_freq) | ||
363 | { | ||
364 | LARGE_INTEGER perf_counter; | ||
365 | |||
366 | QueryPerformanceCounter (&perf_counter); /* never fail on XP and later */ | ||
367 | return (time_t)(((uint64_t)(perf_counter.QuadPart - perf_start)) / perf_freq); | ||
368 | } | ||
369 | #endif /* _WIN32_WINNT < 0x0600 */ | ||
370 | #endif /* _WIN32 */ | ||
371 | #ifdef HAVE_GETHRTIME | ||
372 | if (1) | ||
373 | return (time_t)(((uint64_t) (gethrtime () - hrtime_start)) / 1000000000); | ||
374 | #endif /* HAVE_GETHRTIME */ | ||
375 | |||
376 | return time (NULL) - sys_clock_start; | ||
377 | } | ||