Workaround GetSystemTimePreciseAsFileTime inaccuracies (#14283)
authorStephen Toub <stoub@microsoft.com>
Mon, 2 Oct 2017 19:17:58 +0000 (15:17 -0400)
committerGitHub <noreply@github.com>
Mon, 2 Oct 2017 19:17:58 +0000 (15:17 -0400)
* Workaround GetSystemTimePreciseAsFileTime inaccuracies

On misconfigured systems, GetSystemTimePreciseAsFileTime may drift quickly from GetSystemTimeAsFileTime.  We want to use the former, though.  As a workaround/heuristic, when checking whether we have GetSystemTimePreciseAsFileTime, invoke both and ensure they're "close", falling back to GetSystemTimeAsFileTime if it seems like the precise variant has run away.

* Update comment

src/classlibnative/bcltype/system.cpp

index 4e13bd1876a337e365e10044d95791e3af4dce01..11b7107a65d227d82d6f2aef54854391a2646c62 100644 (file)
@@ -53,6 +53,33 @@ void WINAPI InitializeGetSystemTimeAsFileTime(LPFILETIME lpSystemTimeAsFileTime)
     if (hKernel32 != NULL)
     {
         func = (pfnGetSystemTimeAsFileTime)GetProcAddress(hKernel32, "GetSystemTimePreciseAsFileTime");
+        if (func != NULL)
+        {
+            // GetSystemTimePreciseAsFileTime exists and we'd like to use it.  However, on
+            // misconfigured systems, it's possible for the "precise" time to be inaccurate:
+            //     https://github.com/dotnet/coreclr/issues/14187
+            // If it's inaccurate, though, we expect it to be wildly inaccurate, so as a
+            // workaround/heuristic, we get both the "normal" and "precise" times, and as
+            // long as they're close, we use the precise one. This workaround can be removed
+            // when we better understand what's causing the drift and the issue is no longer
+            // a problem or can be better worked around on all targeted OSes.
+
+            FILETIME systemTimeResult;
+            ::GetSystemTimeAsFileTime(&systemTimeResult);
+
+            FILETIME preciseSystemTimeResult;
+            func(&preciseSystemTimeResult);
+
+            LONG64 systemTimeLong100ns = (LONG64)((((ULONG64)systemTimeResult.dwHighDateTime) << 32) | (ULONG64)systemTimeResult.dwLowDateTime);
+            LONG64 preciseSystemTimeLong100ns = (LONG64)((((ULONG64)preciseSystemTimeResult.dwHighDateTime) << 32) | (ULONG64)preciseSystemTimeResult.dwLowDateTime);
+
+            const INT32 THRESHOLD_100NS = 1000000; // 100ms
+            if (abs(preciseSystemTimeLong100ns - systemTimeLong100ns) > THRESHOLD_100NS)
+            {
+                // Too much difference.  Don't use GetSystemTimePreciseAsFileTime.
+                func = NULL;
+            }
+        }
     }
     if (func == NULL)
 #endif