aboutsummaryrefslogtreecommitdiff
path: root/src/lib/mhd_mono_clock.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/lib/mhd_mono_clock.c')
-rw-r--r--src/lib/mhd_mono_clock.c377
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
56static 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
75static 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)
80static time_t mono_clock_start;
81#endif /* HAVE_CLOCK_GETTIME || HAVE_CLOCK_GET_TIME || HAVE_GETHRTIME */
82static time_t sys_clock_start;
83#ifdef HAVE_GETHRTIME
84static hrtime_t hrtime_start;
85#endif /* HAVE_GETHRTIME */
86#ifdef _WIN32
87#if _WIN32_WINNT >= 0x0600
88static uint64_t tick_start;
89#else /* _WIN32_WINNT < 0x0600 */
90static int64_t perf_freq;
91static int64_t perf_start;
92#endif /* _WIN32_WINNT < 0x0600 */
93#endif /* _WIN32 */
94
95
96
97/**
98 * Type of monotonic clock source
99 */
100enum _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 */
137void
138MHD_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 */
315void
316MHD_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 */
336time_t
337MHD_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}