#include "config.h"
#include "CurrentTime.h"
#include "MonotonicTime.h"
#include "Condition.h"
#include "Lock.h"
#if OS(DARWIN)
#include <mach/mach.h>
#include <mach/mach_time.h>
#include <mutex>
#include <sys/time.h>
#elif OS(WINDOWS)
#undef WIN32_LEAN_AND_MEAN
#include <windows.h>
#include <math.h>
#include <stdint.h>
#include <time.h>
#else
#include <sys/time.h>
#endif
#if USE(GLIB)
#include <glib.h>
#endif
namespace WTF {
#if OS(WINDOWS)
static const ULONGLONG epochBias = 116444736000000000ULL;
static const double hundredsOfNanosecondsPerMillisecond = 10000;
static double lowResUTCTime()
{
FILETIME fileTime;
GetSystemTimeAsFileTime(&fileTime);
ULARGE_INTEGER dateTime;
memcpy(&dateTime, &fileTime, sizeof(dateTime));
return (dateTime.QuadPart - epochBias) / hundredsOfNanosecondsPerMillisecond;
}
#if USE(QUERY_PERFORMANCE_COUNTER)
static LARGE_INTEGER qpcFrequency;
static bool syncedTime;
static double highResUpTime()
{
static LARGE_INTEGER qpcLast;
static DWORD tickCountLast;
static bool inited;
LARGE_INTEGER qpc;
QueryPerformanceCounter(&qpc);
#if defined(_M_IX86) || defined(__i386__)
DWORD tickCount = GetTickCount();
#else
ULONGLONG tickCount = GetTickCount64();
#endif
if (inited) {
__int64 qpcElapsed = ((qpc.QuadPart - qpcLast.QuadPart) * 1000) / qpcFrequency.QuadPart;
__int64 tickCountElapsed;
if (tickCount >= tickCountLast)
tickCountElapsed = (tickCount - tickCountLast);
else {
#if COMPILER(MINGW)
__int64 tickCountLarge = tickCount + 0x100000000ULL;
#else
__int64 tickCountLarge = tickCount + 0x100000000I64;
#endif
tickCountElapsed = tickCountLarge - tickCountLast;
}
__int64 diff = tickCountElapsed - qpcElapsed;
if (diff > 500 || diff < -500)
syncedTime = false;
} else
inited = true;
qpcLast = qpc;
tickCountLast = tickCount;
return (1000.0 * qpc.QuadPart) / static_cast<double>(qpcFrequency.QuadPart);
}
static bool qpcAvailable()
{
static bool available;
static bool checked;
if (checked)
return available;
available = QueryPerformanceFrequency(&qpcFrequency);
checked = true;
return available;
}
double currentTime()
{
static double syncLowResUTCTime;
static double syncHighResUpTime;
static double lastUTCTime;
double lowResTime = lowResUTCTime();
if (!qpcAvailable())
return lowResTime / 1000.0;
double highResTime = highResUpTime();
if (!syncedTime) {
timeBeginPeriod(1); syncLowResUTCTime = lowResTime = lowResUTCTime();
timeEndPeriod(1); syncHighResUpTime = highResTime;
syncedTime = true;
}
double highResElapsed = highResTime - syncHighResUpTime;
double utc = syncLowResUTCTime + highResElapsed;
double lowResElapsed = lowResTime - syncLowResUTCTime;
const double maximumAllowedDriftMsec = 15.625 * 2.0; if (fabs(highResElapsed - lowResElapsed) > maximumAllowedDriftMsec)
syncedTime = false;
const double backwardTimeLimit = 2000.0;
if (utc < lastUTCTime && (lastUTCTime - utc) < backwardTimeLimit)
return lastUTCTime / 1000.0;
lastUTCTime = utc;
return utc / 1000.0;
}
#else
double currentTime()
{
static bool init = false;
static double lastTime;
static DWORD lastTickCount;
if (!init) {
lastTime = lowResUTCTime();
lastTickCount = GetTickCount();
init = true;
return lastTime;
}
DWORD tickCountNow = GetTickCount();
DWORD elapsed = tickCountNow - lastTickCount;
double timeNow = lastTime + (double)elapsed / 1000.;
if (elapsed >= 0x7FFFFFFF) {
lastTime = timeNow;
lastTickCount = tickCountNow;
}
return timeNow;
}
#endif // USE(QUERY_PERFORMANCE_COUNTER)
#elif USE(GLIB)
double currentTime()
{
GTimeVal now;
g_get_current_time(&now);
return static_cast<double>(now.tv_sec) + static_cast<double>(now.tv_usec / 1000000.0);
}
#else
double currentTime()
{
struct timeval now;
gettimeofday(&now, 0);
return now.tv_sec + now.tv_usec / 1000000.0;
}
#endif
#if USE(GLIB)
double monotonicallyIncreasingTime()
{
return static_cast<double>(g_get_monotonic_time() / 1000000.0);
}
#elif OS(DARWIN)
double monotonicallyIncreasingTime()
{
static mach_timebase_info_data_t timebaseInfo;
static std::once_flag initializeTimerOnceFlag;
std::call_once(initializeTimerOnceFlag, [] {
kern_return_t kr = mach_timebase_info(&timebaseInfo);
ASSERT_UNUSED(kr, kr == KERN_SUCCESS);
ASSERT(timebaseInfo.denom);
});
return (mach_absolute_time() * timebaseInfo.numer) / (1.0e9 * timebaseInfo.denom);
}
#elif OS(LINUX) || OS(FREEBSD) || OS(OPENBSD) || OS(NETBSD)
double monotonicallyIncreasingTime()
{
struct timespec ts { };
clock_gettime(CLOCK_MONOTONIC, &ts);
return static_cast<double>(ts.tv_sec) + ts.tv_nsec / 1.0e9;
}
#else
double monotonicallyIncreasingTime()
{
static double lastTime = 0;
double currentTimeNow = currentTime();
if (currentTimeNow < lastTime)
return lastTime;
lastTime = currentTimeNow;
return currentTimeNow;
}
#endif
Seconds currentCPUTime()
{
#if OS(DARWIN)
mach_msg_type_number_t infoCount = THREAD_BASIC_INFO_COUNT;
thread_basic_info_data_t info;
mach_port_t threadPort = mach_thread_self();
thread_info(threadPort, THREAD_BASIC_INFO, reinterpret_cast<thread_info_t>(&info), &infoCount);
mach_port_deallocate(mach_task_self(), threadPort);
return Seconds(info.user_time.seconds + info.system_time.seconds) + Seconds::fromMicroseconds(info.user_time.microseconds + info.system_time.microseconds);
#elif OS(WINDOWS)
union {
FILETIME fileTime;
unsigned long long fileTimeAsLong;
} userTime, kernelTime;
FILETIME creationTime, exitTime;
GetThreadTimes(GetCurrentThread(), &creationTime, &exitTime, &kernelTime.fileTime, &userTime.fileTime);
return Seconds::fromMicroseconds((userTime.fileTimeAsLong + kernelTime.fileTimeAsLong) / 10);
#elif OS(LINUX) || OS(FREEBSD) || OS(OPENBSD) || OS(NETBSD)
struct timespec ts { };
clock_gettime(CLOCK_THREAD_CPUTIME_ID, &ts);
return Seconds(ts.tv_sec) + Seconds::fromNanoseconds(ts.tv_nsec);
#else
static MonotonicTime firstTime = MonotonicTime::now();
return MonotonicTime::now() - firstTime;
#endif
}
void sleep(double value)
{
Lock fakeLock;
Condition fakeCondition;
LockHolder fakeLocker(fakeLock);
fakeCondition.waitFor(fakeLock, Seconds(value));
}
}