From 62c773ac7517feb9e12b4edb7fda5145b41a34d4 Mon Sep 17 00:00:00 2001 From: Jordan Woyak Date: Wed, 23 Jul 2025 23:51:45 -0500 Subject: [PATCH] Common/Timer: Add a SteadyAwakeClock class which counts non-suspended system running time. --- Source/Core/Common/Timer.cpp | 34 ++++++++++++++++++++++++++++++++++ Source/Core/Common/Timer.h | 15 +++++++++++++++ 2 files changed, 49 insertions(+) diff --git a/Source/Core/Common/Timer.cpp b/Source/Core/Common/Timer.cpp index 0a23080c6f..0e260b833c 100644 --- a/Source/Core/Common/Timer.cpp +++ b/Source/Core/Common/Timer.cpp @@ -5,6 +5,7 @@ #include #include +#include "Common/CommonFuncs.h" #ifdef _WIN32 #include @@ -194,4 +195,37 @@ void PrecisionTimer::SleepUntil(Clock::time_point target) } } +// Results are appropriately slewed on Linux, but not on Windows, macOS, or FreeBSD. +// Clocks with that functionality seem to not be available there. +auto SteadyAwakeClock::now() -> time_point +{ +#if defined(_WIN32) + // The count is system time "in units of 100 nanoseconds". + using InterruptDuration = std::chrono::duration::type>; + + ULONGLONG interrupt_time{}; + if (!QueryUnbiasedInterruptTime(&interrupt_time)) + ERROR_LOG_FMT(COMMON, "QueryUnbiasedInterruptTime"); + + return time_point{InterruptDuration{interrupt_time}}; +#else + // Note that Linux's CLOCK_MONOTONIC "does not count time that the system is suspended". + // This is in contrast to the behavior on macOS and FreeBSD. + static constexpr auto clock_id = +#if defined(__linux__) + CLOCK_MONOTONIC; +#elif defined(__APPLE__) + CLOCK_UPTIME_RAW; +#else + CLOCK_UPTIME; +#endif + + timespec ts{}; + if (clock_gettime(clock_id, &ts) != 0) + ERROR_LOG_FMT(COMMON, "clock_gettime: {}", LastStrerrorString()); + + return time_point{std::chrono::seconds{ts.tv_sec} + std::chrono::nanoseconds{ts.tv_nsec}}; +#endif +} + } // Namespace Common diff --git a/Source/Core/Common/Timer.h b/Source/Core/Common/Timer.h index d49a599015..3f5a51ad7f 100644 --- a/Source/Core/Common/Timer.h +++ b/Source/Core/Common/Timer.h @@ -50,4 +50,19 @@ private: #endif }; +// Similar to std::chrono::steady_clock except this clock +// specifically does *not* count time while the system is suspended. +class SteadyAwakeClock +{ +public: + using rep = s64; + using period = std::nano; + using duration = std::chrono::duration; + using time_point = std::chrono::time_point; + + static constexpr bool is_steady = true; + + static time_point now(); +}; + } // Namespace Common