libmicrohttpd

HTTP/1.x server C library (MHD 1.x, stable)
Log | Files | Refs | Submodules | README | LICENSE

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 }