diff --git a/src/common/timer.cpp b/src/common/timer.cpp index fd668492c..88ffdb6e5 100644 --- a/src/common/timer.cpp +++ b/src/common/timer.cpp @@ -17,6 +17,23 @@ namespace Common { static double s_counter_frequency; static bool s_counter_initialized = false; +// This gets leaked... oh well. +static thread_local HANDLE s_sleep_timer; +static thread_local bool s_sleep_timer_created = false; + +static HANDLE GetSleepTimer() +{ + if (s_sleep_timer_created) + return s_sleep_timer; + + s_sleep_timer_created = true; + s_sleep_timer = CreateWaitableTimer(nullptr, TRUE, nullptr); + if (!s_sleep_timer) + std::fprintf(stderr, "CreateWaitableTimer() failed, falling back to Sleep()\n"); + + return s_sleep_timer; +} + Timer::Value Timer::GetValue() { // even if this races, it should still result in the same value.. @@ -63,9 +80,40 @@ Timer::Value Timer::ConvertNanosecondsToValue(double ns) return static_cast(ns * s_counter_frequency); } -#else +void Timer::SleepUntil(Value value, bool exact) +{ + if (exact) + { + while (GetValue() < value) + SleepUntil(value, false); + } + else + { + const std::int64_t diff = static_cast(value - GetValue()); + if (diff <= 0) + return; -#if 1 // using clock_gettime() + HANDLE timer = GetSleepTimer(); + if (timer) + { + FILETIME ft; + GetSystemTimeAsFileTime(&ft); + + LARGE_INTEGER fti; + fti.LowPart = ft.dwLowDateTime; + fti.HighPart = ft.dwHighDateTime; + fti.QuadPart += diff; + + if (SetWaitableTimer(timer, &fti, 0, nullptr, nullptr, FALSE)) + WaitForSingleObject(timer, INFINITE); + } + + // falling back to sleep... bad. + Sleep(static_cast(static_cast(diff) / 1000000)); + } +} + +#else Timer::Value Timer::GetValue() { @@ -104,46 +152,34 @@ Timer::Value Timer::ConvertNanosecondsToValue(double ns) return static_cast(ns); } -#else // using gettimeofday() - -Timer::Value Timer::GetValue() +void Timer::SleepUntil(Value value, bool exact) { - struct timeval tv; - gettimeofday(&tv, NULL); - return ((Value)tv.tv_usec) + ((Value)tv.tv_sec * (Value)1000000); -} - -double Timer::ConvertValueToNanoseconds(Timer::Value value) -{ - return ((double)value * 1000.0); -} - -double Timer::ConvertValueToMilliseconds(Timer::Value value) -{ - return ((double)value / 1000.0); -} - -double Timer::ConvertValueToSeconds(Timer::Value value) -{ - return ((double)value / 1000000.0); -} - -Timer::Value Timer::ConvertSecondsToValue(double s) -{ - return static_cast(ms * 1000000.0); -} - -Timer::Value Timer::ConvertMillisecondsToValue(double ms) -{ - return static_cast(ms * 1000.0); -} - -Timer::Value Timer::ConvertNanosecondsToValue(double ns) -{ - return static_cast(ns / 1000.0); -} + if (exact) + { + while (GetValue() < value) + SleepUntil(value, false); + } + else + { + // Apple doesn't have TIMER_ABSTIME, so fall back to nanosleep in such a case. +#ifdef __APPLE__ + const Value current_time = GetValue(); + if (value <= current_time) + return; + const Value diff = value - current_time; + struct timespec ts; + ts.tv_sec = diff / UINT64_C(1000000000); + ts.tv_nsec = diff % UINT64_C(1000000000); + nanosleep(&ts, nullptr); +#else + struct timespec ts; + ts.tv_sec = value / UINT64_C(1000000000); + ts.tv_nsec = value % UINT64_C(1000000000); + clock_nanosleep(CLOCK_MONOTONIC, TIMER_ABSTIME, &ts, nullptr); #endif + } +} #endif @@ -212,24 +248,13 @@ void Timer::HybridSleep(std::uint64_t ns, std::uint64_t min_sleep_time) void Timer::NanoSleep(std::uint64_t ns) { #if defined(WIN32) - static HANDLE throttle_timer; - static bool throttle_timer_created = false; - if (!throttle_timer_created) - { - throttle_timer_created = true; - throttle_timer = CreateWaitableTimer(nullptr, TRUE, nullptr); - if (throttle_timer) - std::atexit([]() { CloseHandle(throttle_timer); }); - else - std::fprintf(stderr, "CreateWaitableTimer() failed, falling back to Sleep()\n"); - } - - if (throttle_timer) + HANDLE timer = GetSleepTimer(); + if (timer) { LARGE_INTEGER due_time; due_time.QuadPart = -static_cast(static_cast(ns) / 100u); - if (SetWaitableTimer(throttle_timer, &due_time, 0, nullptr, nullptr, FALSE)) - WaitForSingleObject(throttle_timer, INFINITE); + if (SetWaitableTimer(timer, &due_time, 0, nullptr, nullptr, FALSE)) + WaitForSingleObject(timer, INFINITE); else std::fprintf(stderr, "SetWaitableTimer() failed: %08X\n", GetLastError()); } diff --git a/src/common/timer.h b/src/common/timer.h index 3afa1e2ce..c105f3d50 100644 --- a/src/common/timer.h +++ b/src/common/timer.h @@ -20,6 +20,7 @@ public: static void BusyWait(std::uint64_t ns); static void HybridSleep(std::uint64_t ns, std::uint64_t min_sleep_time = UINT64_C(2000000)); static void NanoSleep(std::uint64_t ns); + static void SleepUntil(Value value, bool exact); void Reset();