blob: e6f8267cecf69636c163601ba070618c0e039525 [file] [log] [blame]
/*
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 <time.h>
#endif /* HAVE_TIME_H */
#ifdef HAVE_SYS_TIME_H
#include <sys/time.h>
#endif /* HAVE_SYS_TIME_H */
#ifdef HAVE_CLOCK_GET_TIME
#include <mach/mach.h>
/* for host_get_clock_service(), mach_host_self(), mach_task_self() */
#include <mach/clock.h>
/* 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 <windows.h>
#include <stdint.h>
#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;
}