From dcd13a7566340e80c042da7f626f9747ac71b8a7 Mon Sep 17 00:00:00 2001
From: Morph <39850852+Morph1984@users.noreply.github.com>
Date: Sat, 4 Mar 2023 21:44:31 -0500
Subject: [PATCH] native_clock: Re-adjust the RDTSC frequency

The RDTSC frequency reported by CPUID is not accurate to its true frequency.
We will spawn a separate thread to calculate the true RDTSC frequency after a measurement period of 30 seconds has elapsed.
---
 src/common/x64/native_clock.cpp | 34 ++++++++++++++++++++++++++++-----
 src/common/x64/native_clock.h   |  5 +++++
 2 files changed, 34 insertions(+), 5 deletions(-)

diff --git a/src/common/x64/native_clock.cpp b/src/common/x64/native_clock.cpp
index bc1a973b06..c11590291b 100644
--- a/src/common/x64/native_clock.cpp
+++ b/src/common/x64/native_clock.cpp
@@ -72,13 +72,29 @@ NativeClock::NativeClock(u64 emulated_cpu_frequency_, u64 emulated_clock_frequen
                          u64 rtsc_frequency_)
     : WallClock(emulated_cpu_frequency_, emulated_clock_frequency_, true), rtsc_frequency{
                                                                                rtsc_frequency_} {
+    // Thread to re-adjust the RDTSC frequency after 30 seconds has elapsed.
+    time_sync_thread = std::jthread{[this](std::stop_token token) {
+        // Get the current time.
+        const auto start_time = Common::SteadyClock::Now();
+        const u64 tsc_start = FencedRDTSC();
+        // Wait for 30 seconds.
+        if (!Common::StoppableTimedWait(token, std::chrono::seconds{30})) {
+            return;
+        }
+        const auto end_time = Common::SteadyClock::Now();
+        const u64 tsc_end = FencedRDTSC();
+        // Calculate differences.
+        const u64 timer_diff = static_cast<u64>(
+            std::chrono::duration_cast<std::chrono::nanoseconds>(end_time - start_time).count());
+        const u64 tsc_diff = tsc_end - tsc_start;
+        const u64 tsc_freq = MultiplyAndDivide64(tsc_diff, 1000000000ULL, timer_diff);
+        rtsc_frequency = tsc_freq;
+        CalculateAndSetFactors();
+    }};
+
     time_point.inner.last_measure = FencedRDTSC();
     time_point.inner.accumulated_ticks = 0U;
-    ns_rtsc_factor = GetFixedPoint64Factor(NS_RATIO, rtsc_frequency);
-    us_rtsc_factor = GetFixedPoint64Factor(US_RATIO, rtsc_frequency);
-    ms_rtsc_factor = GetFixedPoint64Factor(MS_RATIO, rtsc_frequency);
-    clock_rtsc_factor = GetFixedPoint64Factor(emulated_clock_frequency, rtsc_frequency);
-    cpu_rtsc_factor = GetFixedPoint64Factor(emulated_cpu_frequency, rtsc_frequency);
+    CalculateAndSetFactors();
 }
 
 u64 NativeClock::GetRTSC() {
@@ -138,6 +154,14 @@ u64 NativeClock::GetCPUCycles() {
     return MultiplyHigh(rtsc_value, cpu_rtsc_factor);
 }
 
+void NativeClock::CalculateAndSetFactors() {
+    ns_rtsc_factor = GetFixedPoint64Factor(NS_RATIO, rtsc_frequency);
+    us_rtsc_factor = GetFixedPoint64Factor(US_RATIO, rtsc_frequency);
+    ms_rtsc_factor = GetFixedPoint64Factor(MS_RATIO, rtsc_frequency);
+    clock_rtsc_factor = GetFixedPoint64Factor(emulated_clock_frequency, rtsc_frequency);
+    cpu_rtsc_factor = GetFixedPoint64Factor(emulated_cpu_frequency, rtsc_frequency);
+}
+
 } // namespace X64
 
 } // namespace Common
diff --git a/src/common/x64/native_clock.h b/src/common/x64/native_clock.h
index 38ae7a4625..03ca291d80 100644
--- a/src/common/x64/native_clock.h
+++ b/src/common/x64/native_clock.h
@@ -3,6 +3,7 @@
 
 #pragma once
 
+#include "common/polyfill_thread.h"
 #include "common/wall_clock.h"
 
 namespace Common {
@@ -28,6 +29,8 @@ public:
 private:
     u64 GetRTSC();
 
+    void CalculateAndSetFactors();
+
     union alignas(16) TimePoint {
         TimePoint() : pack{} {}
         u128 pack{};
@@ -47,6 +50,8 @@ private:
     u64 ms_rtsc_factor{};
 
     u64 rtsc_frequency;
+
+    std::jthread time_sync_thread;
 };
 } // namespace X64