mhd_mono_clock.c (14654B)
1 /* 2 This file is part of libmicrohttpd 3 Copyright (C) 2015-2022 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__) 29 /* Prefer native clock source over wrappers */ 30 #ifdef HAVE_CLOCK_GETTIME 31 #undef HAVE_CLOCK_GETTIME 32 #endif /* HAVE_CLOCK_GETTIME */ 33 #ifdef HAVE_GETTIMEOFDAY 34 #undef HAVE_GETTIMEOFDAY 35 #endif /* HAVE_GETTIMEOFDAY */ 36 #endif /* _WIN32 && ! __CYGWIN__ */ 37 38 #ifdef HAVE_TIME_H 39 #include <time.h> 40 #endif /* HAVE_TIME_H */ 41 #ifdef HAVE_SYS_TIME_H 42 #include <sys/time.h> 43 #endif /* HAVE_SYS_TIME_H */ 44 45 #ifdef HAVE_CLOCK_GET_TIME 46 #include <mach/mach.h> 47 /* for host_get_clock_service(), mach_host_self(), mach_task_self() */ 48 #include <mach/clock.h> 49 /* for clock_get_time() */ 50 51 #define _MHD_INVALID_CLOCK_SERV ((clock_serv_t) -2) 52 53 static clock_serv_t mono_clock_service = _MHD_INVALID_CLOCK_SERV; 54 #endif /* HAVE_CLOCK_GET_TIME */ 55 56 #ifdef _WIN32 57 #ifndef WIN32_LEAN_AND_MEAN 58 /* Do not include unneeded parts of W32 headers. */ 59 #define WIN32_LEAN_AND_MEAN 1 60 #endif /* !WIN32_LEAN_AND_MEAN */ 61 #include <windows.h> 62 #include <stdint.h> 63 #endif /* _WIN32 */ 64 65 #ifndef NULL 66 #define NULL ((void*)0) 67 #endif /* ! NULL */ 68 69 #ifdef HAVE_CLOCK_GETTIME 70 #ifdef CLOCK_REALTIME 71 #define _MHD_UNWANTED_CLOCK CLOCK_REALTIME 72 #else /* !CLOCK_REALTIME */ 73 #define _MHD_UNWANTED_CLOCK ((clockid_t) -2) 74 #endif /* !CLOCK_REALTIME */ 75 76 static clockid_t mono_clock_id = _MHD_UNWANTED_CLOCK; 77 #endif /* HAVE_CLOCK_GETTIME */ 78 79 /* sync clocks; reduce chance of value wrap */ 80 #if defined(HAVE_CLOCK_GETTIME) || defined(HAVE_CLOCK_GET_TIME) || \ 81 defined(HAVE_GETHRTIME) 82 static time_t mono_clock_start; 83 #endif /* HAVE_CLOCK_GETTIME || HAVE_CLOCK_GET_TIME || HAVE_GETHRTIME */ 84 #if defined(HAVE_TIMESPEC_GET) || defined(HAVE_GETTIMEOFDAY) 85 /* The start value shared for timespec_get() and gettimeofday () */ 86 static time_t gettime_start; 87 #endif /* HAVE_TIMESPEC_GET || HAVE_GETTIMEOFDAY */ 88 static time_t sys_clock_start; 89 #ifdef HAVE_GETHRTIME 90 static hrtime_t hrtime_start; 91 #endif /* HAVE_GETHRTIME */ 92 #ifdef _WIN32 93 #if _WIN32_WINNT >= 0x0600 94 static uint64_t tick_start; 95 #else /* _WIN32_WINNT < 0x0600 */ 96 static uint64_t perf_freq; 97 static uint64_t perf_start; 98 #endif /* _WIN32_WINNT < 0x0600 */ 99 #endif /* _WIN32 */ 100 101 102 /** 103 * Type of monotonic clock source 104 */ 105 enum _MHD_mono_clock_source 106 { 107 /** 108 * No monotonic clock 109 */ 110 _MHD_CLOCK_NO_SOURCE = 0, 111 112 /** 113 * clock_gettime() with specific clock 114 */ 115 _MHD_CLOCK_GETTIME, 116 117 /** 118 * clock_get_time() with specific clock service 119 */ 120 _MHD_CLOCK_GET_TIME, 121 122 /** 123 * gethrtime() / 1000000000 124 */ 125 _MHD_CLOCK_GETHRTIME, 126 127 /** 128 * GetTickCount64() / 1000 129 */ 130 _MHD_CLOCK_GETTICKCOUNT64, 131 132 /** 133 * QueryPerformanceCounter() / QueryPerformanceFrequency() 134 */ 135 _MHD_CLOCK_PERFCOUNTER 136 }; 137 138 139 /** 140 * Initialise monotonic seconds and milliseconds counters. 141 */ 142 void 143 MHD_monotonic_sec_counter_init (void) 144 { 145 #ifdef HAVE_CLOCK_GET_TIME 146 mach_timespec_t cur_time; 147 #endif /* HAVE_CLOCK_GET_TIME */ 148 enum _MHD_mono_clock_source mono_clock_source = _MHD_CLOCK_NO_SOURCE; 149 #ifdef HAVE_CLOCK_GETTIME 150 struct timespec ts; 151 152 mono_clock_id = _MHD_UNWANTED_CLOCK; 153 #endif /* HAVE_CLOCK_GETTIME */ 154 #ifdef HAVE_CLOCK_GET_TIME 155 mono_clock_service = _MHD_INVALID_CLOCK_SERV; 156 #endif /* HAVE_CLOCK_GET_TIME */ 157 158 /* just a little syntactic trick to get the 159 various following ifdef's to work out nicely */ 160 if (0) 161 { 162 (void) 0; /* Mute possible compiler warning */ 163 } 164 else 165 #ifdef HAVE_CLOCK_GETTIME 166 #ifdef CLOCK_MONOTONIC_COARSE 167 /* Linux-specific fast value-getting clock */ 168 /* Can be affected by frequency adjustment and don't count time in suspend, */ 169 /* but preferred since it's fast */ 170 if (0 == clock_gettime (CLOCK_MONOTONIC_COARSE, 171 &ts)) 172 { 173 mono_clock_id = CLOCK_MONOTONIC_COARSE; 174 mono_clock_start = ts.tv_sec; 175 mono_clock_source = _MHD_CLOCK_GETTIME; 176 } 177 else 178 #endif /* CLOCK_MONOTONIC_COARSE */ 179 #ifdef CLOCK_MONOTONIC_FAST 180 /* FreeBSD/DragonFly fast value-getting clock */ 181 /* Can be affected by frequency adjustment, but preferred since it's fast */ 182 if (0 == clock_gettime (CLOCK_MONOTONIC_FAST, 183 &ts)) 184 { 185 mono_clock_id = CLOCK_MONOTONIC_FAST; 186 mono_clock_start = ts.tv_sec; 187 mono_clock_source = _MHD_CLOCK_GETTIME; 188 } 189 else 190 #endif /* CLOCK_MONOTONIC_COARSE */ 191 #ifdef CLOCK_MONOTONIC_RAW_APPROX 192 /* Darwin-specific clock */ 193 /* Not affected by frequency adjustment, returns clock value cached at 194 * context switch. Can be "milliseconds old", but it's fast. */ 195 if (0 == clock_gettime (CLOCK_MONOTONIC_RAW_APPROX, 196 &ts)) 197 { 198 mono_clock_id = CLOCK_MONOTONIC_RAW_APPROX; 199 mono_clock_start = ts.tv_sec; 200 mono_clock_source = _MHD_CLOCK_GETTIME; 201 } 202 else 203 #endif /* CLOCK_MONOTONIC_RAW */ 204 #ifdef CLOCK_MONOTONIC_RAW 205 /* Linux and Darwin clock */ 206 /* Not affected by frequency adjustment, 207 * on Linux don't count time in suspend */ 208 if (0 == clock_gettime (CLOCK_MONOTONIC_RAW, 209 &ts)) 210 { 211 mono_clock_id = CLOCK_MONOTONIC_RAW; 212 mono_clock_start = ts.tv_sec; 213 mono_clock_source = _MHD_CLOCK_GETTIME; 214 } 215 else 216 #endif /* CLOCK_MONOTONIC_RAW */ 217 #ifdef CLOCK_BOOTTIME 218 /* Count time in suspend on Linux so it's real monotonic, */ 219 /* but can be slower value-getting than other clocks */ 220 if (0 == clock_gettime (CLOCK_BOOTTIME, 221 &ts)) 222 { 223 mono_clock_id = CLOCK_BOOTTIME; 224 mono_clock_start = ts.tv_sec; 225 mono_clock_source = _MHD_CLOCK_GETTIME; 226 } 227 else 228 #endif /* CLOCK_BOOTTIME */ 229 #ifdef CLOCK_MONOTONIC 230 /* Monotonic clock */ 231 /* Widely supported, may be affected by frequency adjustment */ 232 /* On Linux it's not truly monotonic as it doesn't count time in suspend */ 233 if (0 == clock_gettime (CLOCK_MONOTONIC, 234 &ts)) 235 { 236 mono_clock_id = CLOCK_MONOTONIC; 237 mono_clock_start = ts.tv_sec; 238 mono_clock_source = _MHD_CLOCK_GETTIME; 239 } 240 else 241 #endif /* CLOCK_MONOTONIC */ 242 #ifdef CLOCK_UPTIME 243 /* non-Linux clock */ 244 /* Doesn't count time in suspend */ 245 if (0 == clock_gettime (CLOCK_UPTIME, 246 &ts)) 247 { 248 mono_clock_id = CLOCK_UPTIME; 249 mono_clock_start = ts.tv_sec; 250 mono_clock_source = _MHD_CLOCK_GETTIME; 251 } 252 else 253 #endif /* CLOCK_BOOTTIME */ 254 #endif /* HAVE_CLOCK_GETTIME */ 255 #ifdef HAVE_CLOCK_GET_TIME 256 /* Darwin-specific monotonic clock */ 257 /* Should be monotonic as clock_set_time function always unconditionally */ 258 /* failed on latest kernels */ 259 if ( (KERN_SUCCESS == host_get_clock_service (mach_host_self (), 260 SYSTEM_CLOCK, 261 &mono_clock_service)) && 262 (KERN_SUCCESS == clock_get_time (mono_clock_service, 263 &cur_time)) ) 264 { 265 mono_clock_start = cur_time.tv_sec; 266 mono_clock_source = _MHD_CLOCK_GET_TIME; 267 } 268 else 269 #endif /* HAVE_CLOCK_GET_TIME */ 270 #ifdef _WIN32 271 #if _WIN32_WINNT >= 0x0600 272 /* W32 Vista or later specific monotonic clock */ 273 /* Available since Vista, ~15ms accuracy */ 274 if (1) 275 { 276 tick_start = GetTickCount64 (); 277 mono_clock_source = _MHD_CLOCK_GETTICKCOUNT64; 278 } 279 else 280 #else /* _WIN32_WINNT < 0x0600 */ 281 /* W32 specific monotonic clock */ 282 /* Available on Windows 2000 and later */ 283 if (1) 284 { 285 LARGE_INTEGER freq; 286 LARGE_INTEGER perf_counter; 287 288 QueryPerformanceFrequency (&freq); /* never fail on XP and later */ 289 QueryPerformanceCounter (&perf_counter); /* never fail on XP and later */ 290 perf_freq = (uint64_t) freq.QuadPart; 291 perf_start = (uint64_t) perf_counter.QuadPart; 292 mono_clock_source = _MHD_CLOCK_PERFCOUNTER; 293 } 294 else 295 #endif /* _WIN32_WINNT < 0x0600 */ 296 #endif /* _WIN32 */ 297 #ifdef HAVE_CLOCK_GETTIME 298 #ifdef CLOCK_HIGHRES 299 /* Solaris-specific monotonic high-resolution clock */ 300 /* Not preferred due to be potentially resource-hungry */ 301 if (0 == clock_gettime (CLOCK_HIGHRES, 302 &ts)) 303 { 304 mono_clock_id = CLOCK_HIGHRES; 305 mono_clock_start = ts.tv_sec; 306 mono_clock_source = _MHD_CLOCK_GETTIME; 307 } 308 else 309 #endif /* CLOCK_HIGHRES */ 310 #endif /* HAVE_CLOCK_GETTIME */ 311 #ifdef HAVE_GETHRTIME 312 /* HP-UX and Solaris monotonic clock */ 313 /* Not preferred due to be potentially resource-hungry */ 314 if (1) 315 { 316 hrtime_start = gethrtime (); 317 mono_clock_source = _MHD_CLOCK_GETHRTIME; 318 } 319 else 320 #endif /* HAVE_GETHRTIME */ 321 { 322 /* no suitable clock source was found */ 323 mono_clock_source = _MHD_CLOCK_NO_SOURCE; 324 } 325 326 #ifdef HAVE_CLOCK_GET_TIME 327 if ( (_MHD_CLOCK_GET_TIME != mono_clock_source) && 328 (_MHD_INVALID_CLOCK_SERV != mono_clock_service) ) 329 { 330 /* clock service was initialised but clock_get_time failed */ 331 mach_port_deallocate (mach_task_self (), 332 mono_clock_service); 333 mono_clock_service = _MHD_INVALID_CLOCK_SERV; 334 } 335 #else 336 (void) mono_clock_source; /* avoid compiler warning */ 337 #endif /* HAVE_CLOCK_GET_TIME */ 338 339 #ifdef HAVE_TIMESPEC_GET 340 if (1) 341 { 342 struct timespec tsg; 343 if (TIME_UTC == timespec_get (&tsg, TIME_UTC)) 344 gettime_start = tsg.tv_sec; 345 else 346 gettime_start = 0; 347 } 348 #elif defined(HAVE_GETTIMEOFDAY) 349 if (1) 350 { 351 struct timeval tv; 352 if (0 == gettimeofday (&tv, NULL)) 353 gettime_start = tv.tv_sec; 354 else 355 gettime_start = 0; 356 } 357 #endif /* HAVE_GETTIMEOFDAY */ 358 sys_clock_start = time (NULL); 359 } 360 361 362 /** 363 * Deinitialise monotonic seconds and milliseconds counters by freeing 364 * any allocated resources 365 */ 366 void 367 MHD_monotonic_sec_counter_finish (void) 368 { 369 #ifdef HAVE_CLOCK_GET_TIME 370 if (_MHD_INVALID_CLOCK_SERV != mono_clock_service) 371 { 372 mach_port_deallocate (mach_task_self (), 373 mono_clock_service); 374 mono_clock_service = _MHD_INVALID_CLOCK_SERV; 375 } 376 #endif /* HAVE_CLOCK_GET_TIME */ 377 } 378 379 380 /** 381 * Monotonic seconds counter. 382 * Tries to be not affected by manually setting the system real time 383 * clock or adjustments by NTP synchronization. 384 * 385 * @return number of seconds from some fixed moment 386 */ 387 time_t 388 MHD_monotonic_sec_counter (void) 389 { 390 #ifdef HAVE_CLOCK_GETTIME 391 struct timespec ts; 392 393 if ( (_MHD_UNWANTED_CLOCK != mono_clock_id) && 394 (0 == clock_gettime (mono_clock_id, 395 &ts)) ) 396 return ts.tv_sec - mono_clock_start; 397 #endif /* HAVE_CLOCK_GETTIME */ 398 #ifdef HAVE_CLOCK_GET_TIME 399 if (_MHD_INVALID_CLOCK_SERV != mono_clock_service) 400 { 401 mach_timespec_t cur_time; 402 403 if (KERN_SUCCESS == clock_get_time (mono_clock_service, 404 &cur_time)) 405 return cur_time.tv_sec - mono_clock_start; 406 } 407 #endif /* HAVE_CLOCK_GET_TIME */ 408 #if defined(_WIN32) 409 #if _WIN32_WINNT >= 0x0600 410 if (1) 411 return (time_t) (((uint64_t) (GetTickCount64 () - tick_start)) / 1000); 412 #else /* _WIN32_WINNT < 0x0600 */ 413 if (0 != perf_freq) 414 { 415 LARGE_INTEGER perf_counter; 416 417 QueryPerformanceCounter (&perf_counter); /* never fail on XP and later */ 418 return (time_t) (((uint64_t) perf_counter.QuadPart - perf_start) 419 / perf_freq); 420 } 421 #endif /* _WIN32_WINNT < 0x0600 */ 422 #endif /* _WIN32 */ 423 #ifdef HAVE_GETHRTIME 424 if (1) 425 return (time_t) (((uint64_t) (gethrtime () - hrtime_start)) / 1000000000); 426 #endif /* HAVE_GETHRTIME */ 427 428 return time (NULL) - sys_clock_start; 429 } 430 431 432 /** 433 * Monotonic milliseconds counter, useful for timeout calculation. 434 * Tries to be not affected by manually setting the system real time 435 * clock or adjustments by NTP synchronization. 436 * 437 * @return number of microseconds from some fixed moment 438 */ 439 uint64_t 440 MHD_monotonic_msec_counter (void) 441 { 442 #if defined(HAVE_CLOCK_GETTIME) || defined(HAVE_TIMESPEC_GET) 443 struct timespec ts; 444 #endif /* HAVE_CLOCK_GETTIME || HAVE_TIMESPEC_GET */ 445 446 #ifdef HAVE_CLOCK_GETTIME 447 if ( (_MHD_UNWANTED_CLOCK != mono_clock_id) && 448 (0 == clock_gettime (mono_clock_id, 449 &ts)) ) 450 return (uint64_t) (((uint64_t) (ts.tv_sec - mono_clock_start)) * 1000 451 + (uint64_t) (ts.tv_nsec / 1000000)); 452 #endif /* HAVE_CLOCK_GETTIME */ 453 #ifdef HAVE_CLOCK_GET_TIME 454 if (_MHD_INVALID_CLOCK_SERV != mono_clock_service) 455 { 456 mach_timespec_t cur_time; 457 458 if (KERN_SUCCESS == clock_get_time (mono_clock_service, 459 &cur_time)) 460 return (uint64_t) (((uint64_t) (cur_time.tv_sec - mono_clock_start)) 461 * 1000 + (uint64_t) (cur_time.tv_nsec / 1000000)); 462 } 463 #endif /* HAVE_CLOCK_GET_TIME */ 464 #if defined(_WIN32) 465 #if _WIN32_WINNT >= 0x0600 466 if (1) 467 return (uint64_t) (GetTickCount64 () - tick_start); 468 #else /* _WIN32_WINNT < 0x0600 */ 469 if (0 != perf_freq) 470 { 471 LARGE_INTEGER perf_counter; 472 uint64_t num_ticks; 473 474 QueryPerformanceCounter (&perf_counter); /* never fail on XP and later */ 475 num_ticks = (uint64_t) (perf_counter.QuadPart - perf_start); 476 return ((num_ticks / perf_freq) * 1000) 477 + ((num_ticks % perf_freq) / (perf_freq / 1000)); 478 } 479 #endif /* _WIN32_WINNT < 0x0600 */ 480 #endif /* _WIN32 */ 481 #ifdef HAVE_GETHRTIME 482 if (1) 483 return ((uint64_t) (gethrtime () - hrtime_start)) / 1000000; 484 #endif /* HAVE_GETHRTIME */ 485 486 /* Fallbacks, affected by system time change */ 487 #ifdef HAVE_TIMESPEC_GET 488 if (TIME_UTC == timespec_get (&ts, TIME_UTC)) 489 return (uint64_t) (((uint64_t) (ts.tv_sec - gettime_start)) * 1000 490 + (uint64_t) (ts.tv_nsec / 1000000)); 491 #elif defined(HAVE_GETTIMEOFDAY) 492 if (1) 493 { 494 struct timeval tv; 495 if (0 == gettimeofday (&tv, NULL)) 496 return (uint64_t) (((uint64_t) (tv.tv_sec - gettime_start)) * 1000 497 + (uint64_t) (tv.tv_usec / 1000)); 498 } 499 #endif /* HAVE_GETTIMEOFDAY */ 500 501 /* The last resort fallback with very low resolution */ 502 return (uint64_t) (time (NULL) - sys_clock_start) * 1000; 503 }