mhd_mono_clock.c (22470B)
1 /* SPDX-License-Identifier: LGPL-2.1-or-later OR (GPL-2.0-or-later WITH eCos-exception-2.0) */ 2 /* 3 This file is part of GNU libmicrohttpd. 4 Copyright (C) 2015-2024 Karlson2k (Evgeny Grin) 5 6 GNU libmicrohttpd is free software; you can redistribute it and/or 7 modify it under the terms of the GNU Lesser General Public 8 License as published by the Free Software Foundation; either 9 version 2.1 of the License, or (at your option) any later version. 10 11 GNU libmicrohttpd is distributed in the hope that it will be useful, 12 but WITHOUT ANY WARRANTY; without even the implied warranty of 13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 14 Lesser General Public License for more details. 15 16 Alternatively, you can redistribute GNU libmicrohttpd and/or 17 modify it under the terms of the GNU General Public License as 18 published by the Free Software Foundation; either version 2 of 19 the License, or (at your option) any later version, together 20 with the eCos exception, as follows: 21 22 As a special exception, if other files instantiate templates or 23 use macros or inline functions from this file, or you compile this 24 file and link it with other works to produce a work based on this 25 file, this file does not by itself cause the resulting work to be 26 covered by the GNU General Public License. However the source code 27 for this file must still be made available in accordance with 28 section (3) of the GNU General Public License v2. 29 30 This exception does not invalidate any other reasons why a work 31 based on this file might be covered by the GNU General Public 32 License. 33 34 You should have received copies of the GNU Lesser General Public 35 License and the GNU General Public License along with this library; 36 if not, see <https://www.gnu.org/licenses/>. 37 */ 38 39 /** 40 * @file src/mhd2/mhd_mono_clock.h 41 * @brief monotonic clock functions implementations 42 * @author Karlson2k (Evgeny Grin) 43 */ 44 45 #include "mhd_sys_options.h" 46 47 #include "mhd_mono_clock.h" 48 49 #if defined(_WIN32) && ! defined(__CYGWIN__) 50 /* Prefer native clock source over wrappers */ 51 # ifdef HAVE_CLOCK_GETTIME 52 # undef HAVE_CLOCK_GETTIME 53 # endif /* HAVE_CLOCK_GETTIME */ 54 # ifdef HAVE_GETTIMEOFDAY 55 # undef HAVE_GETTIMEOFDAY 56 # endif /* HAVE_GETTIMEOFDAY */ 57 #endif /* _WIN32 && ! __CYGWIN__ */ 58 59 #if defined(HAVE_MACH_CONTINUOUS_APPROXIMATE_TIME) || \ 60 defined(HAVE_MACH_APPROXIMATE_TIME) 61 /* Use mach_*_time() functions family */ 62 # define mhd_USE_MACH_TIME 1 63 #endif 64 65 #ifdef HAVE_TIME_H 66 # include <time.h> 67 #endif /* HAVE_TIME_H */ 68 #ifdef HAVE_SYS_TIME_H 69 # include <sys/time.h> 70 #endif /* HAVE_SYS_TIME_H */ 71 72 #if defined(HAVE_CLOCK_GET_TIME) || \ 73 defined(mhd_USE_MACH_TIME) 74 /* for host_get_clock_service(), mach_host_self(), mach_task_self() */ 75 /* also for compatibility with old headers structure */ 76 # include <mach/mach.h> 77 #endif 78 79 #ifdef HAVE_CLOCK_GET_TIME 80 /* for clock_get_time() */ 81 # include <mach/clock.h> 82 #endif /* HAVE_CLOCK_GET_TIME */ 83 84 #if defined(mhd_USE_MACH_TIME) 85 # include <mach/mach_time.h> 86 #endif 87 88 #ifdef _WIN32 89 # include <windows.h> 90 #endif /* _WIN32 */ 91 92 #include "mhd_assert.h" 93 #include "mhd_unreachable.h" 94 95 96 #ifdef HAVE_CLOCK_GETTIME 97 # ifdef CLOCK_REALTIME 98 # define mhd_CLOCK_ID_UNWANTED CLOCK_REALTIME 99 # else /* !CLOCK_REALTIME */ 100 # define mhd_CLOCK_ID_UNWANTED ((clockid_t) -2) 101 # endif /* !CLOCK_REALTIME */ 102 103 static clockid_t mono_clock_id = mhd_CLOCK_ID_UNWANTED; 104 #endif /* HAVE_CLOCK_GETTIME */ 105 106 /* sync clocks; reduce chance of value wrap */ 107 #if defined(HAVE_CLOCK_GETTIME) || defined(HAVE_CLOCK_GET_TIME) || \ 108 defined(HAVE_GETHRTIME) 109 static time_t mono_clock_start; 110 #endif /* HAVE_CLOCK_GETTIME || HAVE_CLOCK_GET_TIME || HAVE_GETHRTIME */ 111 112 #ifdef HAVE_CLOCK_GET_TIME 113 # if ! defined(SYSTEM_CLOCK) && defined(REALTIME_CLOCK) 114 # define SYSTEM_CLOCK REALTIME_CLOCK 115 # endif 116 # define mhd_CLOCK_SERV_INVALID ((clock_serv_t) -2) 117 118 static clock_serv_t mono_clock_service = mhd_CLOCK_SERV_INVALID; 119 #endif /* HAVE_CLOCK_GET_TIME */ 120 121 #if defined(mhd_USE_MACH_TIME) 122 123 /* The numerator to calculate milliseconds */ 124 static uint_fast32_t mach_time_mls_numer = 0; 125 /* The denominator to calculate milliseconds */ 126 static uint_fast64_t mach_time_mls_denom = 0; 127 /* The starting value. Used to lower chance of the final value wrap. */ 128 static uint64_t mach_time_start; /* uint64_t is always available with mach */ 129 #endif 130 131 #ifdef HAVE_GETHRTIME 132 static hrtime_t hrtime_start; 133 #endif /* HAVE_GETHRTIME */ 134 135 #ifdef _WIN32 136 # if _WIN32_WINNT >= 0x0600 137 static uint64_t tick_start; /* 'uint64_t' is available on W32 always */ 138 # else /* _WIN32_WINNT < 0x0600 */ 139 static uint64_t perf_freq; /* 'uint64_t' is available on W32 always */ 140 static uint64_t perf_start; /* 'uint64_t' is available on W32 always */ 141 # endif /* _WIN32_WINNT < 0x0600 */ 142 #endif /* _WIN32 */ 143 144 /* Start values for fallback sources */ 145 #if defined(HAVE_TIMESPEC_GET) || defined(HAVE_GETTIMEOFDAY) 146 /* The start value shared for timespec_get() and gettimeofday () */ 147 static time_t gettime_start; 148 #define mhd_HAVE_GETTIME_START_VAR 1 149 #endif /* HAVE_TIMESPEC_GET || HAVE_GETTIMEOFDAY */ 150 static time_t sys_clock_start; 151 152 153 #ifdef HAVE_CLOCK_GET_TIME 154 /** 155 * Initialise Darwin-specific resources for 'clock_get_time()' 156 * @param[out] cur_time the optional pointer to get the current time value, 157 * can be NULL 158 * @return 'true' if succeed, 159 * 'false' if failed 160 */ 161 static MHD_FN_PAR_OUT_ (1) MHD_FN_MUST_CHECK_RESULT_ bool 162 mclock_init_clock_get_time (mach_timespec_t *cur_time) 163 { 164 mhd_assert (mhd_CLOCK_SERV_INVALID == mono_clock_service); 165 166 if (KERN_SUCCESS == host_get_clock_service (mach_host_self (), 167 SYSTEM_CLOCK, 168 &mono_clock_service)) 169 return false; 170 171 if (NULL != cur_time) 172 { 173 if (KERN_SUCCESS != clock_get_time (mono_clock_service, 174 cur_time)) 175 { 176 (void) mach_port_deallocate (mach_task_self (), 177 mono_clock_service); 178 mono_clock_service = mhd_CLOCK_SERV_INVALID; 179 return false; 180 } 181 } 182 183 return true; 184 } 185 186 187 /** 188 * De-initialise Darwin-specific resources for 'clock_get_time()' 189 */ 190 static void 191 mclock_deinit_clock_get_time (void) 192 { 193 mhd_assert (mhd_CLOCK_SERV_INVALID != mono_clock_service); 194 (void) mach_port_deallocate (mach_task_self (), 195 mono_clock_service); 196 mono_clock_service = mhd_CLOCK_SERV_INVALID; 197 } 198 199 200 #else /* HAVE_CLOCK_GET_TIME */ 201 /* No-op implementation */ 202 # define mclock_init_clock_get_time(ptr) ((void) ptr, false) 203 # define mclock_deinit_clock_get_time() ((void) 0) 204 #endif /* HAVE_CLOCK_GET_TIME */ 205 206 #if defined(mhd_USE_MACH_TIME) 207 208 /** 209 * Calculate greatest common divisor. 210 * Based on Euclidean algorithm as it is fast enough and more compact then 211 * binary GCD algorithm. 212 * @param a the first value 213 * @param b the second value 214 * @return the greatest common divisor, 215 * if either of the input values is zero, the other input value 216 * returned 217 */ 218 mhd_static_inline uint_fast32_t 219 mclock_gcd (uint_fast32_t a, uint_fast32_t b) 220 { 221 if (0 == b) 222 return a; 223 224 while (1) 225 { 226 a %= b; 227 if (0 == a) 228 return b; 229 b %= a; 230 if (0 == b) 231 break; 232 } 233 return a; 234 } 235 236 237 /** 238 * Initialise data for mach_time functions 239 * @return 'true' if succeed, 240 * 'false' if failed 241 */ 242 static bool 243 mclock_init_mach_time (void) 244 { 245 struct mach_timebase_info mach_tb_info; 246 uint_fast32_t comm_div; 247 static const uint_fast32_t nanosec_in_milisec = 1000u * 1000u; 248 249 mhd_assert ((0 != mach_time_mls_denom) || (0 == mach_time_mls_numer)); 250 251 if (KERN_SUCCESS != mach_timebase_info (&mach_tb_info)) 252 return false; 253 254 mhd_assert (0 != mach_tb_info.numer); /* Help code analysers */ 255 mhd_assert (0 != mach_tb_info.denom); /* Help code analysers */ 256 257 comm_div = mclock_gcd (mach_tb_info.numer, nanosec_in_milisec); 258 mach_time_mls_numer = mach_tb_info.numer / comm_div; 259 mach_time_mls_denom = 260 ((uint_fast64_t) mach_tb_info.denom) * (nanosec_in_milisec / comm_div); 261 262 return true; 263 } 264 265 266 #else /* ! mhd_USE_MACH_TIME */ 267 # define mclock_init_mach_time() (true) 268 #endif /* ! mhd_USE_MACH_TIME */ 269 270 /** 271 * Type of monotonic clock source 272 */ 273 enum mhd_mono_clock_source 274 { 275 /** 276 * No monotonic clock source. 277 */ 278 mhd_MCLOCK_SOUCE_NO_SOURCE = 0 279 280 #ifdef HAVE_CLOCK_GETTIME 281 , 282 /** 283 * clock_gettime() with specific clock. 284 * Generic standard source. 285 */ 286 mhd_MCLOCK_SOUCE_GETTIME 287 #endif /* HAVE_CLOCK_GETTIME */ 288 289 #if defined(mhd_USE_MACH_TIME) 290 , 291 /** 292 * mach_continuous_approximate_time() or mach_approximate_time() 293 * with coefficient. 294 * Darwin-specific clock source. 295 */ 296 mhd_MCLOCK_SOUCE_MACH_TIME 297 #endif /* mhd_USE_MACH_TIME */ 298 299 #ifdef HAVE_CLOCK_GET_TIME 300 , 301 /** 302 * clock_get_time() with specific clock service. 303 * Darwin-specific clock source. 304 */ 305 mhd_MCLOCK_SOUCE_GET_TIME 306 #endif /* HAVE_CLOCK_GET_TIME */ 307 308 #ifdef HAVE_GETHRTIME 309 , 310 /** 311 * gethrtime() / 1000000 312 * HP-UX and Solaris monotonic clock source. 313 */ 314 mhd_MCLOCK_SOUCE_GETHRTIME 315 #endif /* HAVE_GETHRTIME */ 316 317 #ifdef _WIN32 318 #if _WIN32_WINNT >= 0x0600 319 , 320 /** 321 * GetTickCount64() 322 * W32 tick counter source. 323 */ 324 mhd_MCLOCK_SOUCE_GETTICKCOUNT64 325 #else /* _WIN32_WINNT < 0x0600 */ 326 327 , 328 /** 329 * QueryPerformanceCounter() / QueryPerformanceFrequency() 330 * Older W32 monotonic time source. 331 */ 332 mhd_MCLOCK_SOUCE_PERFCOUNTER 333 #endif /* _WIN32_WINNT < 0x0600 */ 334 #endif /* _WIN32 */ 335 }; 336 337 /** 338 * The active source of the monotonic time 339 */ 340 static enum mhd_mono_clock_source mono_clock_source = 341 mhd_MCLOCK_SOUCE_NO_SOURCE; 342 343 /** 344 * Initialise milliseconds counters. 345 */ 346 MHD_INTERNAL void 347 mhd_mclock_init_once (void) 348 { 349 #ifdef HAVE_CLOCK_GET_TIME 350 mach_timespec_t cur_time; 351 #endif /* HAVE_CLOCK_GET_TIME */ 352 #ifdef HAVE_CLOCK_GETTIME 353 struct timespec ts; 354 355 mono_clock_id = mhd_CLOCK_ID_UNWANTED; 356 #endif /* HAVE_CLOCK_GETTIME */ 357 #ifdef HAVE_CLOCK_GET_TIME 358 mono_clock_service = mhd_CLOCK_SERV_INVALID; 359 #endif /* HAVE_CLOCK_GET_TIME */ 360 361 mono_clock_source = mhd_MCLOCK_SOUCE_NO_SOURCE; 362 363 /* Try specialised fast sources */ 364 #ifdef _WIN32 365 #if _WIN32_WINNT >= 0x0600 366 /* W32 Vista or later specific monotonic clock */ 367 /* Available since Vista, ~15ms accuracy */ 368 if (1) 369 { 370 tick_start = GetTickCount64 (); 371 mono_clock_source = mhd_MCLOCK_SOUCE_GETTICKCOUNT64; 372 } 373 else 374 #else /* _WIN32_WINNT < 0x0600 */ 375 /* W32 specific monotonic clock */ 376 /* Available on Windows 2000 and later */ 377 if (1) 378 { 379 LARGE_INTEGER freq; 380 LARGE_INTEGER perf_counter; 381 382 (void) QueryPerformanceFrequency (&freq); /* never fail on XP and later */ 383 (void) QueryPerformanceCounter (&perf_counter); /* never fail on XP and later */ 384 perf_freq = (uint64_t) freq.QuadPart; 385 perf_start = (uint64_t) perf_counter.QuadPart; 386 mono_clock_source = mhd_MCLOCK_SOUCE_PERFCOUNTER; 387 } 388 else 389 #endif /* _WIN32_WINNT < 0x0600 */ 390 #endif /* _WIN32 */ 391 #if defined(mhd_USE_MACH_TIME) 392 /* Mach (Darwin) specific monotonic clock */ 393 /* mach_continuous_approximate_time() counts time in suspend, 394 mach_approximate_time() does not count time in suspend. 395 Both function are fast and used as a basis for universal portable functions 396 on Darwin. */ 397 if (mclock_init_mach_time ()) 398 { 399 # ifdef HAVE_MACH_CONTINUOUS_APPROXIMATE_TIME 400 mach_time_start = mach_continuous_approximate_time (); 401 # else /* HAVE_MACH_APPROXIMATE_TIME */ 402 mach_time_start = mach_approximate_time (); 403 # endif 404 mono_clock_source = mhd_MCLOCK_SOUCE_MACH_TIME; 405 } 406 else 407 #endif /* mhd_USE_MACH_TIME */ 408 409 /* Try universally available sources */ 410 #ifdef HAVE_CLOCK_GETTIME 411 #ifdef CLOCK_MONOTONIC_COARSE 412 /* Linux-specific fast value-getting clock */ 413 /* Can be affected by frequency adjustment and doesn't count time 414 * in suspend, but preferred since it's fast */ 415 if (0 == clock_gettime (CLOCK_MONOTONIC_COARSE, 416 &ts)) 417 { 418 mono_clock_id = CLOCK_MONOTONIC_COARSE; 419 mono_clock_start = ts.tv_sec; 420 mono_clock_source = mhd_MCLOCK_SOUCE_GETTIME; 421 } 422 else 423 #endif /* CLOCK_MONOTONIC_COARSE */ 424 #ifdef CLOCK_MONOTONIC_FAST 425 /* FreeBSD/DragonFly fast value-getting clock */ 426 /* Can be affected by frequency adjustment, but preferred since it's fast */ 427 if (0 == clock_gettime (CLOCK_MONOTONIC_FAST, 428 &ts)) 429 { 430 mono_clock_id = CLOCK_MONOTONIC_FAST; 431 mono_clock_start = ts.tv_sec; 432 mono_clock_source = mhd_MCLOCK_SOUCE_GETTIME; 433 } 434 else 435 #endif /* CLOCK_MONOTONIC_COARSE */ 436 #ifdef CLOCK_UPTIME_FAST 437 /* FreeBSD/DragonFly fast value-getting clock */ 438 /* Can be affected by frequency adjustment and doesn't count time 439 * in suspend, but preferred since it's fast */ 440 if (0 == clock_gettime (CLOCK_UPTIME_FAST, 441 &ts)) 442 { 443 mono_clock_id = CLOCK_MONOTONIC_FAST; 444 mono_clock_start = ts.tv_sec; 445 mono_clock_source = mhd_MCLOCK_SOUCE_GETTIME; 446 } 447 else 448 #endif /* CLOCK_UPTIME_FAST */ 449 #ifdef CLOCK_MONOTONIC_RAW_APPROX 450 /* Darwin-specific clock */ 451 /* Not affected by frequency adjustment, returns clock value cached at 452 * context switch. Can be "milliseconds old", but it's fast. */ 453 if (0 == clock_gettime (CLOCK_MONOTONIC_RAW_APPROX, 454 &ts)) 455 { 456 mono_clock_id = CLOCK_MONOTONIC_RAW_APPROX; 457 mono_clock_start = ts.tv_sec; 458 mono_clock_source = mhd_MCLOCK_SOUCE_GETTIME; 459 } 460 else 461 #endif /* CLOCK_MONOTONIC_RAW */ 462 #ifdef CLOCK_UPTIME_RAW_APPROX 463 /* Darwin-specific clock */ 464 /* Not affected by frequency adjustment, but doesn't count time in suspend. 465 * Returns clock value cached at context switch. 466 * Can be "milliseconds old", but it's fast. */ 467 if (0 == clock_gettime (CLOCK_UPTIME_RAW_APPROX, 468 &ts)) 469 { 470 mono_clock_id = CLOCK_UPTIME_RAW_APPROX; 471 mono_clock_start = ts.tv_sec; 472 mono_clock_source = mhd_MCLOCK_SOUCE_GETTIME; 473 } 474 else 475 #endif /* CLOCK_UPTIME_RAW_APPROX */ 476 #ifdef CLOCK_MONOTONIC_RAW 477 /* Linux and Darwin clock */ 478 /* Not affected by frequency adjustment, 479 * on Linux doesn't count time in suspend */ 480 if (0 == clock_gettime (CLOCK_MONOTONIC_RAW, 481 &ts)) 482 { 483 mono_clock_id = CLOCK_MONOTONIC_RAW; 484 mono_clock_start = ts.tv_sec; 485 mono_clock_source = mhd_MCLOCK_SOUCE_GETTIME; 486 } 487 else 488 #endif /* CLOCK_MONOTONIC_RAW */ 489 #ifdef CLOCK_BOOTTIME 490 /* Counts time in suspend on Linux so it's real monotonic, */ 491 /* but can be slower value-getting than other clocks */ 492 if (0 == clock_gettime (CLOCK_BOOTTIME, 493 &ts)) 494 { 495 mono_clock_id = CLOCK_BOOTTIME; 496 mono_clock_start = ts.tv_sec; 497 mono_clock_source = mhd_MCLOCK_SOUCE_GETTIME; 498 } 499 else 500 #endif /* CLOCK_BOOTTIME */ 501 #ifdef CLOCK_MONOTONIC 502 /* Monotonic clock */ 503 /* Widely supported, may be affected by frequency adjustment */ 504 /* On Linux it's not truly monotonic as it doesn't count time in suspend */ 505 if (0 == clock_gettime (CLOCK_MONOTONIC, 506 &ts)) 507 { 508 mono_clock_id = CLOCK_MONOTONIC; 509 mono_clock_start = ts.tv_sec; 510 mono_clock_source = mhd_MCLOCK_SOUCE_GETTIME; 511 } 512 else 513 #endif /* CLOCK_MONOTONIC */ 514 #ifdef CLOCK_UPTIME 515 /* non-Linux clock */ 516 /* Doesn't count time in suspend */ 517 if (0 == clock_gettime (CLOCK_UPTIME, 518 &ts)) 519 { 520 mono_clock_id = CLOCK_UPTIME; 521 mono_clock_start = ts.tv_sec; 522 mono_clock_source = mhd_MCLOCK_SOUCE_GETTIME; 523 } 524 else 525 #endif /* CLOCK_BOOTTIME */ 526 #endif /* HAVE_CLOCK_GETTIME */ 527 #ifdef HAVE_CLOCK_GET_TIME 528 /* Darwin-specific monotonic clock source */ 529 /* Should be monotonic as clock_set_time function always unconditionally */ 530 /* failed on modern kernels */ 531 if (mclock_init_clock_get_time (&cur_time)) 532 { 533 mono_clock_start = cur_time.tv_sec; 534 mono_clock_source = mhd_MCLOCK_SOUCE_GET_TIME; 535 } 536 else 537 #endif /* HAVE_CLOCK_GET_TIME */ 538 #ifdef HAVE_CLOCK_GETTIME 539 #ifdef CLOCK_HIGHRES 540 /* Solaris-specific monotonic high-resolution clock */ 541 /* Not preferred due to be potentially resource-hungry */ 542 if (0 == clock_gettime (CLOCK_HIGHRES, 543 &ts)) 544 { 545 mono_clock_id = CLOCK_HIGHRES; 546 mono_clock_start = ts.tv_sec; 547 mono_clock_source = mhd_MCLOCK_SOUCE_GETTIME; 548 } 549 else 550 #endif /* CLOCK_HIGHRES */ 551 #endif /* HAVE_CLOCK_GETTIME */ 552 #ifdef HAVE_GETHRTIME 553 /* HP-UX and Solaris monotonic clock */ 554 /* Not preferred due to be potentially resource-hungry */ 555 if (1) 556 { 557 hrtime_start = gethrtime (); 558 mono_clock_source = mhd_MCLOCK_SOUCE_GETHRTIME; 559 } 560 else 561 #endif /* HAVE_GETHRTIME */ 562 (void) 0; /* The end of if-else chain */ 563 564 /* Initialise start values for fallbacks */ 565 #ifdef HAVE_TIMESPEC_GET 566 if (1) 567 { 568 struct timespec tsg; 569 if (TIME_UTC == timespec_get (&tsg, TIME_UTC)) 570 gettime_start = tsg.tv_sec; 571 else 572 gettime_start = 0; 573 } 574 #elif defined(HAVE_GETTIMEOFDAY) 575 if (1) 576 { 577 struct timeval tv; 578 if (0 == gettimeofday (&tv, NULL)) 579 gettime_start = tv.tv_sec; 580 else 581 gettime_start = 0; 582 } 583 #endif /* HAVE_GETTIMEOFDAY */ 584 585 sys_clock_start = time (NULL); 586 #ifdef mhd_HAVE_GETTIME_START_VAR 587 if (((time_t) -1) == sys_clock_start) 588 sys_clock_start = gettime_start; 589 #endif /* mhd_HAVE_GETTIME_START_VAR */ 590 } 591 592 593 #ifdef HAVE_CLOCK_GET_TIME 594 /* Resources may be allocated only for Darwin clock_get_time() */ 595 596 MHD_INTERNAL void 597 mhd_mclock_deinit (void) 598 { 599 if (mhd_MCLOCK_SOUCE_GET_TIME == mono_clock_source) 600 mclock_deinit_clock_get_time (); 601 } 602 603 604 MHD_INTERNAL void 605 mhd_mclock_re_init (void) 606 { 607 if (mhd_MCLOCK_SOUCE_GET_TIME == mono_clock_source) 608 { 609 if (! mclock_init_clock_get_time (NULL)) 610 /* Fallback to full initialisation */ 611 mhd_mclock_init_once (); 612 } 613 } 614 615 616 #endif /* HAVE_CLOCK_GET_TIME */ 617 618 /** 619 * Monotonic milliseconds counter, useful for timeout calculation. 620 * Tries to be not affected by manually setting the system real time 621 * clock or adjustments by NTP synchronization. 622 * 623 * @return number of microseconds from some fixed moment 624 */ 625 MHD_INTERNAL uint_fast64_t 626 mhd_monotonic_msec_counter (void) 627 { 628 enum mhd_mono_clock_source source_to_use; 629 630 /* Optimise binary if the source is fixed */ 631 #if defined(_WIN32) && _WIN32_WINNT >= 0x0600 632 if (1) 633 source_to_use = mhd_MCLOCK_SOUCE_GETTICKCOUNT64; 634 else 635 #endif /* _WIN32 && _WIN32_WINNT >= 0x0600 */ 636 source_to_use = mono_clock_source; 637 638 mhd_assert (mono_clock_source == source_to_use); 639 640 switch (source_to_use) 641 { 642 case mhd_MCLOCK_SOUCE_NO_SOURCE: 643 break; /* Use fallbacks */ 644 #ifdef HAVE_CLOCK_GETTIME 645 case mhd_MCLOCK_SOUCE_GETTIME: 646 mhd_assert (mhd_CLOCK_ID_UNWANTED != mono_clock_id); 647 if (1) 648 { 649 struct timespec ts; 650 if (0 == clock_gettime (mono_clock_id, 651 &ts)) 652 return (uint_fast64_t) 653 (((uint_fast64_t) (ts.tv_sec - mono_clock_start)) * 1000 654 + (uint_fast64_t) (ts.tv_nsec / 1000000)); 655 } 656 break; 657 #endif /* HAVE_CLOCK_GETTIME */ 658 659 #if defined(mhd_USE_MACH_TIME) 660 case mhd_MCLOCK_SOUCE_MACH_TIME: 661 mhd_assert (0 != mach_time_mls_numer); 662 mhd_assert (0 != mach_time_mls_denom); 663 if (1) 664 { 665 uint64_t t; 666 # ifdef HAVE_MACH_CONTINUOUS_APPROXIMATE_TIME 667 t = mach_continuous_approximate_time () - mach_time_start; 668 # else /* HAVE_MACH_APPROXIMATE_TIME */ 669 t = mach_approximate_time () - mach_time_start; 670 # endif 671 # ifndef MHD_FAVOR_SMALL_CODE 672 if (1 == mach_time_mls_numer) /* Shortcut for the most common situation */ 673 return (uint_fast64_t) t / mach_time_mls_denom; 674 # endif /* MHD_FAVOR_SMALL_CODE */ 675 676 /* Avoid float point arithmetic as it lower precision on higher values. 677 Two stages calculations to avoid overflow of integer values and keep 678 precision high enough. */ 679 return (((uint_fast64_t) t) / mach_time_mls_denom) * mach_time_mls_numer 680 + ((((uint_fast64_t) t) % mach_time_mls_denom) 681 * mach_time_mls_numer) / mach_time_mls_denom; 682 } 683 break; 684 #endif /* mhd_USE_MACH_TIME */ 685 686 #ifdef HAVE_CLOCK_GET_TIME 687 case mhd_MCLOCK_SOUCE_GET_TIME: 688 mhd_assert (mhd_CLOCK_SERV_INVALID != mono_clock_service); 689 if (1) 690 { 691 mach_timespec_t cur_time; 692 693 if (KERN_SUCCESS == clock_get_time (mono_clock_service, 694 &cur_time)) 695 return (uint_fast64_t) 696 (((uint_fast64_t) (cur_time.tv_sec - mono_clock_start)) * 1000 697 + (uint_fast64_t) (cur_time.tv_nsec / 1000000)); 698 } 699 break; 700 #endif /* HAVE_CLOCK_GET_TIME */ 701 702 #ifdef HAVE_GETHRTIME 703 case mhd_MCLOCK_SOUCE_GETHRTIME: 704 return ((uint_fast64_t) (gethrtime () - hrtime_start)) / 1000000; 705 #endif /* HAVE_GETHRTIME */ 706 707 #ifdef _WIN32 708 #if _WIN32_WINNT >= 0x0600 709 case mhd_MCLOCK_SOUCE_GETTICKCOUNT64: 710 return (uint_fast64_t) (GetTickCount64 () - tick_start); 711 #else /* _WIN32_WINNT < 0x0600 */ 712 case mhd_MCLOCK_SOUCE_PERFCOUNTER: 713 mhd_assert (0 != perf_freq); 714 if (1) 715 { 716 LARGE_INTEGER perf_counter; 717 uint_fast64_t num_ticks; 718 719 (void) QueryPerformanceCounter (&perf_counter); /* never fail on XP and later */ 720 num_ticks = (uint_fast64_t) (perf_counter.QuadPart - perf_start); 721 return ((num_ticks / perf_freq) * 1000) 722 + (((num_ticks % perf_freq) * 1000) / perf_freq); 723 } 724 break; 725 #endif /* _WIN32_WINNT < 0x0600 */ 726 #endif /* _WIN32 */ 727 default: 728 mhd_UNREACHABLE (); 729 break; 730 } 731 732 /* Fallbacks, affected by system time change */ 733 #ifdef HAVE_TIMESPEC_GET 734 if (1) 735 { 736 struct timespec ts; 737 if (TIME_UTC == timespec_get (&ts, TIME_UTC)) 738 return (uint_fast64_t) 739 (((uint_fast64_t) (ts.tv_sec - gettime_start)) * 1000 740 + (uint_fast64_t) (ts.tv_nsec / 1000000)); 741 } 742 #elif defined(HAVE_GETTIMEOFDAY) 743 if (1) 744 { 745 struct timeval tv; 746 if (0 == gettimeofday (&tv, NULL)) 747 return (uint_fast64_t) 748 (((uint_fast64_t) (tv.tv_sec - gettime_start)) * 1000 749 + (uint_fast64_t) (tv.tv_usec / 1000)); 750 } 751 #endif /* HAVE_GETTIMEOFDAY */ 752 753 /* The last resort fallback with very low resolution */ 754 #ifdef mhd_HAVE_GETTIME_START_VAR 755 if (1) 756 { 757 time_t time_now; 758 time_now = time (NULL); 759 if (((time_t) -1) != time_now) 760 return ((uint_fast64_t) (time_now - sys_clock_start)) * 1000; 761 } 762 return 0; /* No time source, should not really happen */ 763 #else /* ! mhd_HAVE_GETTIME_START_VAR */ 764 return ((uint_fast64_t) (time (NULL) - sys_clock_start)) * 1000; 765 #endif 766 }