/* This file is part of libmicrohttpd Copyright (C) 2015 Karlson2k (Evgeny Grin) This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ /** * @file microhttpd/mhd_mono_clock.h * @brief internal monotonic clock functions implementations * @author Karlson2k (Evgeny Grin) */ #include "mhd_mono_clock.h" #if defined(_WIN32) && ! defined(__CYGWIN__) /* Prefer native clock source over wrappers */ #ifdef HAVE_CLOCK_GETTIME #undef HAVE_CLOCK_GETTIME #endif /* HAVE_CLOCK_GETTIME */ #ifdef HAVE_GETTIMEOFDAY #undef HAVE_GETTIMEOFDAY #endif /* HAVE_GETTIMEOFDAY */ #endif /* _WIN32 && ! __CYGWIN__ */ #ifdef HAVE_TIME_H #include #endif /* HAVE_TIME_H */ #ifdef HAVE_SYS_TIME_H #include #endif /* HAVE_SYS_TIME_H */ #ifdef HAVE_CLOCK_GET_TIME #include /* for host_get_clock_service(), mach_host_self(), mach_task_self() */ #include /* for clock_get_time() */ #define _MHD_INVALID_CLOCK_SERV ((clock_serv_t) -2) static clock_serv_t mono_clock_service = _MHD_INVALID_CLOCK_SERV; #endif /* HAVE_CLOCK_GET_TIME */ #ifdef _WIN32 #ifndef WIN32_LEAN_AND_MEAN /* Do not include unneeded parts of W32 headers. */ #define WIN32_LEAN_AND_MEAN 1 #endif /* !WIN32_LEAN_AND_MEAN */ #include #include #endif /* _WIN32 */ #ifndef NULL #define NULL ((void*)0) #endif /* ! NULL */ #ifdef HAVE_CLOCK_GETTIME #ifdef CLOCK_REALTIME #define _MHD_UNWANTED_CLOCK CLOCK_REALTIME #else /* !CLOCK_REALTIME */ #define _MHD_UNWANTED_CLOCK ((clockid_t) -2) #endif /* !CLOCK_REALTIME */ static clockid_t mono_clock_id = _MHD_UNWANTED_CLOCK; #endif /* HAVE_CLOCK_GETTIME */ /* sync clocks; reduce chance of value wrap */ #if defined(HAVE_CLOCK_GETTIME) || defined(HAVE_CLOCK_GET_TIME) || \ defined(HAVE_GETHRTIME) static time_t mono_clock_start; #endif /* HAVE_CLOCK_GETTIME || HAVE_CLOCK_GET_TIME || HAVE_GETHRTIME */ #if defined(HAVE_TIMESPEC_GET) || defined(HAVE_GETTIMEOFDAY) /* The start value shared for timespec_get() and gettimeofday () */ static time_t gettime_start; #endif /* HAVE_TIMESPEC_GET || HAVE_GETTIMEOFDAY */ static time_t sys_clock_start; #ifdef HAVE_GETHRTIME static hrtime_t hrtime_start; #endif /* HAVE_GETHRTIME */ #ifdef _WIN32 #if _WIN32_WINNT >= 0x0600 static uint64_t tick_start; #else /* _WIN32_WINNT < 0x0600 */ static uint64_t perf_freq; static uint64_t perf_start; #endif /* _WIN32_WINNT < 0x0600 */ #endif /* _WIN32 */ /** * Type of monotonic clock source */ enum _MHD_mono_clock_source { /** * No monotonic clock */ _MHD_CLOCK_NO_SOURCE = 0, /** * clock_gettime() with specific clock */ _MHD_CLOCK_GETTIME, /** * clock_get_time() with specific clock service */ _MHD_CLOCK_GET_TIME, /** * gethrtime() / 1000000000 */ _MHD_CLOCK_GETHRTIME, /** * GetTickCount64() / 1000 */ _MHD_CLOCK_GETTICKCOUNT64, /** * QueryPerformanceCounter() / QueryPerformanceFrequency() */ _MHD_CLOCK_PERFCOUNTER }; /** * Initialise monotonic seconds and milliseconds counters. */ void MHD_monotonic_sec_counter_init (void) { #ifdef HAVE_CLOCK_GET_TIME mach_timespec_t cur_time; #endif /* HAVE_CLOCK_GET_TIME */ enum _MHD_mono_clock_source mono_clock_source = _MHD_CLOCK_NO_SOURCE; #ifdef HAVE_CLOCK_GETTIME struct timespec ts; mono_clock_id = _MHD_UNWANTED_CLOCK; #endif /* HAVE_CLOCK_GETTIME */ #ifdef HAVE_CLOCK_GET_TIME mono_clock_service = _MHD_INVALID_CLOCK_SERV; #endif /* HAVE_CLOCK_GET_TIME */ /* just a little syntactic trick to get the various following ifdef's to work out nicely */ if (0) { (void) 0; /* Mute possible compiler warning */ } else #ifdef HAVE_CLOCK_GETTIME #ifdef CLOCK_MONOTONIC_COARSE /* Linux-specific fast value-getting clock */ /* Can be affected by frequency adjustment and don't count time in suspend, */ /* but preferred since it's fast */ if (0 == clock_gettime (CLOCK_MONOTONIC_COARSE, &ts)) { mono_clock_id = CLOCK_MONOTONIC_COARSE; mono_clock_start = ts.tv_sec; mono_clock_source = _MHD_CLOCK_GETTIME; } else #endif /* CLOCK_MONOTONIC_COARSE */ #ifdef CLOCK_MONOTONIC_FAST /* FreeBSD/DragonFly fast value-getting clock */ /* Can be affected by frequency adjustment, but preferred since it's fast */ if (0 == clock_gettime (CLOCK_MONOTONIC_FAST, &ts)) { mono_clock_id = CLOCK_MONOTONIC_FAST; mono_clock_start = ts.tv_sec; mono_clock_source = _MHD_CLOCK_GETTIME; } else #endif /* CLOCK_MONOTONIC_COARSE */ #ifdef CLOCK_MONOTONIC_RAW_APPROX /* Darwin-specific clock */ /* Not affected by frequency adjustment, returns clock value cached at * context switch. Can be "milliseconds old", but it's fast. */ if (0 == clock_gettime (CLOCK_MONOTONIC_RAW_APPROX, &ts)) { mono_clock_id = CLOCK_MONOTONIC_RAW_APPROX; mono_clock_start = ts.tv_sec; mono_clock_source = _MHD_CLOCK_GETTIME; } else #endif /* CLOCK_MONOTONIC_RAW */ #ifdef CLOCK_MONOTONIC_RAW /* Linux and Darwin clock */ /* Not affected by frequency adjustment, * on Linux don't count time in suspend */ if (0 == clock_gettime (CLOCK_MONOTONIC_RAW, &ts)) { mono_clock_id = CLOCK_MONOTONIC_RAW; mono_clock_start = ts.tv_sec; mono_clock_source = _MHD_CLOCK_GETTIME; } else #endif /* CLOCK_MONOTONIC_RAW */ #ifdef CLOCK_BOOTTIME /* Count time in suspend on Linux so it's real monotonic, */ /* but can be slower value-getting than other clocks */ if (0 == clock_gettime (CLOCK_BOOTTIME, &ts)) { mono_clock_id = CLOCK_BOOTTIME; mono_clock_start = ts.tv_sec; mono_clock_source = _MHD_CLOCK_GETTIME; } else #endif /* CLOCK_BOOTTIME */ #ifdef CLOCK_MONOTONIC /* Monotonic clock */ /* Widely supported, may be affected by frequency adjustment */ /* On Linux it's not truly monotonic as it doesn't count time in suspend */ if (0 == clock_gettime (CLOCK_MONOTONIC, &ts)) { mono_clock_id = CLOCK_MONOTONIC; mono_clock_start = ts.tv_sec; mono_clock_source = _MHD_CLOCK_GETTIME; } else #endif /* CLOCK_MONOTONIC */ #ifdef CLOCK_UPTIME /* non-Linux clock */ /* Doesn't count time in suspend */ if (0 == clock_gettime (CLOCK_UPTIME, &ts)) { mono_clock_id = CLOCK_UPTIME; mono_clock_start = ts.tv_sec; mono_clock_source = _MHD_CLOCK_GETTIME; } else #endif /* CLOCK_BOOTTIME */ #endif /* HAVE_CLOCK_GETTIME */ #ifdef HAVE_CLOCK_GET_TIME /* Darwin-specific monotonic clock */ /* Should be monotonic as clock_set_time function always unconditionally */ /* failed on latest kernels */ if ( (KERN_SUCCESS == host_get_clock_service (mach_host_self (), SYSTEM_CLOCK, &mono_clock_service)) && (KERN_SUCCESS == clock_get_time (mono_clock_service, &cur_time)) ) { mono_clock_start = cur_time.tv_sec; mono_clock_source = _MHD_CLOCK_GET_TIME; } else #endif /* HAVE_CLOCK_GET_TIME */ #ifdef _WIN32 #if _WIN32_WINNT >= 0x0600 /* W32 Vista or later specific monotonic clock */ /* Available since Vista, ~15ms accuracy */ if (1) { tick_start = GetTickCount64 (); mono_clock_source = _MHD_CLOCK_GETTICKCOUNT64; } else #else /* _WIN32_WINNT < 0x0600 */ /* W32 specific monotonic clock */ /* Available on Windows 2000 and later */ if (1) { LARGE_INTEGER freq; LARGE_INTEGER perf_counter; QueryPerformanceFrequency (&freq); /* never fail on XP and later */ QueryPerformanceCounter (&perf_counter); /* never fail on XP and later */ perf_freq = (uint64_t) freq.QuadPart; perf_start = (uint64_t) perf_counter.QuadPart; mono_clock_source = _MHD_CLOCK_PERFCOUNTER; } else #endif /* _WIN32_WINNT < 0x0600 */ #endif /* _WIN32 */ #ifdef HAVE_CLOCK_GETTIME #ifdef CLOCK_HIGHRES /* Solaris-specific monotonic high-resolution clock */ /* Not preferred due to be potentially resource-hungry */ if (0 == clock_gettime (CLOCK_HIGHRES, &ts)) { mono_clock_id = CLOCK_HIGHRES; mono_clock_start = ts.tv_sec; mono_clock_source = _MHD_CLOCK_GETTIME; } else #endif /* CLOCK_HIGHRES */ #endif /* HAVE_CLOCK_GETTIME */ #ifdef HAVE_GETHRTIME /* HP-UX and Solaris monotonic clock */ /* Not preferred due to be potentially resource-hungry */ if (1) { hrtime_start = gethrtime (); mono_clock_source = _MHD_CLOCK_GETHRTIME; } else #endif /* HAVE_GETHRTIME */ { /* no suitable clock source was found */ mono_clock_source = _MHD_CLOCK_NO_SOURCE; } #ifdef HAVE_CLOCK_GET_TIME if ( (_MHD_CLOCK_GET_TIME != mono_clock_source) && (_MHD_INVALID_CLOCK_SERV != mono_clock_service) ) { /* clock service was initialised but clock_get_time failed */ mach_port_deallocate (mach_task_self (), mono_clock_service); mono_clock_service = _MHD_INVALID_CLOCK_SERV; } #else (void) mono_clock_source; /* avoid compiler warning */ #endif /* HAVE_CLOCK_GET_TIME */ #ifdef HAVE_TIMESPEC_GET if (1) { struct timespec tsg; if (TIME_UTC == timespec_get (&tsg, TIME_UTC)) gettime_start = tsg.tv_sec; else gettime_start = 0; } #elif defined(HAVE_GETTIMEOFDAY) if (1) { struct timeval tv; if (0 == gettimeofday (&tv, NULL)) gettime_start = tv.tv_sec; else gettime_start = 0; } #endif /* HAVE_GETTIMEOFDAY */ sys_clock_start = time (NULL); } /** * Deinitialise monotonic seconds and milliseconds counters by freeing * any allocated resources */ void MHD_monotonic_sec_counter_finish (void) { #ifdef HAVE_CLOCK_GET_TIME if (_MHD_INVALID_CLOCK_SERV != mono_clock_service) { mach_port_deallocate (mach_task_self (), mono_clock_service); mono_clock_service = _MHD_INVALID_CLOCK_SERV; } #endif /* HAVE_CLOCK_GET_TIME */ } /** * Monotonic seconds counter. * Tries to be not affected by manually setting the system real time * clock or adjustments by NTP synchronization. * * @return number of seconds from some fixed moment */ time_t MHD_monotonic_sec_counter (void) { #ifdef HAVE_CLOCK_GETTIME struct timespec ts; if ( (_MHD_UNWANTED_CLOCK != mono_clock_id) && (0 == clock_gettime (mono_clock_id, &ts)) ) return ts.tv_sec - mono_clock_start; #endif /* HAVE_CLOCK_GETTIME */ #ifdef HAVE_CLOCK_GET_TIME if (_MHD_INVALID_CLOCK_SERV != mono_clock_service) { mach_timespec_t cur_time; if (KERN_SUCCESS == clock_get_time (mono_clock_service, &cur_time)) return cur_time.tv_sec - mono_clock_start; } #endif /* HAVE_CLOCK_GET_TIME */ #if defined(_WIN32) #if _WIN32_WINNT >= 0x0600 if (1) return (time_t) (((uint64_t) (GetTickCount64 () - tick_start)) / 1000); #else /* _WIN32_WINNT < 0x0600 */ if (0 != perf_freq) { LARGE_INTEGER perf_counter; QueryPerformanceCounter (&perf_counter); /* never fail on XP and later */ return (time_t) (((uint64_t) perf_counter.QuadPart - perf_start) / perf_freq); } #endif /* _WIN32_WINNT < 0x0600 */ #endif /* _WIN32 */ #ifdef HAVE_GETHRTIME if (1) return (time_t) (((uint64_t) (gethrtime () - hrtime_start)) / 1000000000); #endif /* HAVE_GETHRTIME */ return time (NULL) - sys_clock_start; } /** * Monotonic milliseconds counter, useful for timeout calculation. * Tries to be not affected by manually setting the system real time * clock or adjustments by NTP synchronization. * * @return number of microseconds from some fixed moment */ uint64_t MHD_monotonic_msec_counter (void) { #if defined(HAVE_CLOCK_GETTIME) || defined(HAVE_TIMESPEC_GET) struct timespec ts; #endif /* HAVE_CLOCK_GETTIME || HAVE_TIMESPEC_GET */ #ifdef HAVE_CLOCK_GETTIME if ( (_MHD_UNWANTED_CLOCK != mono_clock_id) && (0 == clock_gettime (mono_clock_id, &ts)) ) return (uint64_t) (((uint64_t) (ts.tv_sec - mono_clock_start)) * 1000 + (uint64_t) (ts.tv_nsec / 1000000)); #endif /* HAVE_CLOCK_GETTIME */ #ifdef HAVE_CLOCK_GET_TIME if (_MHD_INVALID_CLOCK_SERV != mono_clock_service) { mach_timespec_t cur_time; if (KERN_SUCCESS == clock_get_time (mono_clock_service, &cur_time)) return (uint64_t) (((uint64_t) (cur_time.tv_sec - mono_clock_start)) * 1000 + (uint64_t) (cur_time.tv_nsec / 1000000)); } #endif /* HAVE_CLOCK_GET_TIME */ #if defined(_WIN32) #if _WIN32_WINNT >= 0x0600 if (1) return (uint64_t) (GetTickCount64 () - tick_start); #else /* _WIN32_WINNT < 0x0600 */ if (0 != perf_freq) { LARGE_INTEGER perf_counter; uint64_t num_ticks; QueryPerformanceCounter (&perf_counter); /* never fail on XP and later */ num_ticks = (uint64_t) (perf_counter.QuadPart - perf_start); return ((num_ticks / perf_freq) * 1000) + ((num_ticks % perf_freq) / (perf_freq / 1000)); } #endif /* _WIN32_WINNT < 0x0600 */ #endif /* _WIN32 */ #ifdef HAVE_GETHRTIME if (1) return ((uint64_t) (gethrtime () - hrtime_start)) / 1000000; #endif /* HAVE_GETHRTIME */ /* Fallbacks, affected by system time change */ #ifdef HAVE_TIMESPEC_GET if (TIME_UTC == timespec_get (&ts, TIME_UTC)) return (uint64_t) (((uint64_t) (ts.tv_sec - gettime_start)) * 1000 + (uint64_t) (ts.tv_nsec / 1000000)); #elif defined(HAVE_GETTIMEOFDAY) if (1) { struct timeval tv; if (0 == gettimeofday (&tv, NULL)) return (uint64_t) (((uint64_t) (tv.tv_sec - gettime_start)) * 1000 + (uint64_t) (tv.tv_usec / 1000)); } #endif /* HAVE_GETTIMEOFDAY */ /* The last resort fallback with very low resolution */ return (uint64_t) (time (NULL) - sys_clock_start) * 1000; }