From 4652370fdf410c3c5952d7c1657d3f4446463f46 Mon Sep 17 00:00:00 2001 From: Jan Kotas Date: Thu, 7 Jan 2021 04:20:34 -0800 Subject: [PATCH] Delete DateTime FCalls and switch to fully managed implementation --- .../System.Private.CoreLib.csproj | 2 - .../src/System/DateTime.Unix.CoreCLR.cs | 21 --- .../src/System/DateTime.Windows.CoreCLR.cs | 25 --- src/coreclr/classlibnative/bcltype/system.cpp | 150 --------------- src/coreclr/classlibnative/bcltype/system.h | 7 - src/coreclr/vm/ecalllist.h | 11 -- .../Interop.GetSystemTimeAsTicks.cs | 1 + .../Kernel32/Interop.FileTimeToSystemTime.cs | 1 + .../Windows/Kernel32/Interop.GetSystemTime.cs | 1 + .../Interop.GetSystemTimeAsFileTime.cs | 13 -- .../Interop.GetSystemTimePreciseAsFileTime.cs | 13 -- .../Kernel32/Interop.SystemTimeToFileTime.cs | 1 + .../System.Private.CoreLib.Shared.projitems | 6 - .../src/System/DateTime.Unix.cs | 2 - .../src/System/DateTime.Windows.cs | 173 ++++++++---------- 15 files changed, 81 insertions(+), 346 deletions(-) delete mode 100644 src/coreclr/System.Private.CoreLib/src/System/DateTime.Unix.CoreCLR.cs delete mode 100644 src/coreclr/System.Private.CoreLib/src/System/DateTime.Windows.CoreCLR.cs delete mode 100644 src/libraries/Common/src/Interop/Windows/Kernel32/Interop.GetSystemTimeAsFileTime.cs delete mode 100644 src/libraries/Common/src/Interop/Windows/Kernel32/Interop.GetSystemTimePreciseAsFileTime.cs diff --git a/src/coreclr/System.Private.CoreLib/System.Private.CoreLib.csproj b/src/coreclr/System.Private.CoreLib/System.Private.CoreLib.csproj index a2a5b776f4cbd..36b60ed6663a3 100644 --- a/src/coreclr/System.Private.CoreLib/System.Private.CoreLib.csproj +++ b/src/coreclr/System.Private.CoreLib/System.Private.CoreLib.csproj @@ -285,12 +285,10 @@ - - Common\Interop\Windows\OleAut32\Interop.VariantClear.cs diff --git a/src/coreclr/System.Private.CoreLib/src/System/DateTime.Unix.CoreCLR.cs b/src/coreclr/System.Private.CoreLib/src/System/DateTime.Unix.CoreCLR.cs deleted file mode 100644 index 3dc69ae32b1d0..0000000000000 --- a/src/coreclr/System.Private.CoreLib/src/System/DateTime.Unix.CoreCLR.cs +++ /dev/null @@ -1,21 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -using System.Runtime.CompilerServices; - -namespace System -{ - public readonly partial struct DateTime - { - public static DateTime UtcNow - { - get - { - return new DateTime(((ulong)(GetSystemTimeAsFileTime() + FileTimeOffset)) | KindUtc); - } - } - - [MethodImpl(MethodImplOptions.InternalCall)] - private static extern long GetSystemTimeAsFileTime(); - } -} diff --git a/src/coreclr/System.Private.CoreLib/src/System/DateTime.Windows.CoreCLR.cs b/src/coreclr/System.Private.CoreLib/src/System/DateTime.Windows.CoreCLR.cs deleted file mode 100644 index 1f77180f47870..0000000000000 --- a/src/coreclr/System.Private.CoreLib/src/System/DateTime.Windows.CoreCLR.cs +++ /dev/null @@ -1,25 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -using System.Runtime.CompilerServices; - -namespace System -{ - public readonly partial struct DateTime - { - [MethodImpl(MethodImplOptions.InternalCall)] - private static extern unsafe bool ValidateSystemTime(Interop.Kernel32.SYSTEMTIME* time, bool localTime); - - [MethodImpl(MethodImplOptions.InternalCall)] - private static extern unsafe bool FileTimeToSystemTime(long fileTime, FullSystemTime* time); - - [MethodImpl(MethodImplOptions.InternalCall)] - private static extern unsafe void GetSystemTimeWithLeapSecondsHandling(FullSystemTime* time); - - [MethodImpl(MethodImplOptions.InternalCall)] - private static extern unsafe bool SystemTimeToFileTime(Interop.Kernel32.SYSTEMTIME* time, long* fileTime); - - [MethodImpl(MethodImplOptions.InternalCall)] - private static extern unsafe long GetSystemTimeAsFileTime(); - } -} diff --git a/src/coreclr/classlibnative/bcltype/system.cpp b/src/coreclr/classlibnative/bcltype/system.cpp index 3660c14963df9..91a39ef9d0892 100644 --- a/src/coreclr/classlibnative/bcltype/system.cpp +++ b/src/coreclr/classlibnative/bcltype/system.cpp @@ -30,157 +30,7 @@ #include "array.h" #include "eepolicy.h" -#ifndef TARGET_UNIX -typedef void(WINAPI *pfnGetSystemTimeAsFileTime)(LPFILETIME lpSystemTimeAsFileTime); -extern pfnGetSystemTimeAsFileTime g_pfnGetSystemTimeAsFileTime; - -void WINAPI InitializeGetSystemTimeAsFileTime(LPFILETIME lpSystemTimeAsFileTime) -{ - pfnGetSystemTimeAsFileTime func = NULL; - - HMODULE hKernel32 = WszLoadLibrary(W("kernel32.dll")); - 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/runtime/issues/9014 - // 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) - { - func = &::GetSystemTimeAsFileTime; - } - - InterlockedCompareExchangeT(&g_pfnGetSystemTimeAsFileTime, func, &InitializeGetSystemTimeAsFileTime); - - g_pfnGetSystemTimeAsFileTime(lpSystemTimeAsFileTime); -} - -pfnGetSystemTimeAsFileTime g_pfnGetSystemTimeAsFileTime = &InitializeGetSystemTimeAsFileTime; -#endif // TARGET_UNIX - -FCIMPL0(INT64, SystemNative::__GetSystemTimeAsFileTime) -{ - FCALL_CONTRACT; - - INT64 timestamp; -#ifndef TARGET_UNIX - g_pfnGetSystemTimeAsFileTime((FILETIME*)×tamp); -#else - GetSystemTimeAsFileTime((FILETIME*)×tamp); -#endif - -#if BIGENDIAN - timestamp = (INT64)(((UINT64)timestamp >> 32) | ((UINT64)timestamp << 32)); -#endif - - return timestamp; -} -FCIMPLEND; - - -#ifndef TARGET_UNIX - -FCIMPL1(VOID, SystemNative::GetSystemTimeWithLeapSecondsHandling, FullSystemTime *time) -{ - FCALL_CONTRACT; - INT64 timestamp; - - g_pfnGetSystemTimeAsFileTime((FILETIME*)×tamp); - - if (::FileTimeToSystemTime((FILETIME*)×tamp, &(time->systemTime))) - { - // to keep the time precision - time->hundredNanoSecond = timestamp % 10000; // 10000 is the number of 100-nano seconds per Millisecond - } - else - { - ::GetSystemTime(&(time->systemTime)); - time->hundredNanoSecond = 0; - } - - if (time->systemTime.wSecond > 59) - { - // we have a leap second, force it to last second in the minute as DateTime doesn't account for leap seconds in its calculation. - // we use the maxvalue from the milliseconds and the 100-nano seconds to avoid reporting two out of order 59 seconds - time->systemTime.wSecond = 59; - time->systemTime.wMilliseconds = 999; - time->hundredNanoSecond = 9999; - } -} -FCIMPLEND; -FCIMPL2(FC_BOOL_RET, SystemNative::FileTimeToSystemTime, INT64 fileTime, FullSystemTime *time) -{ - FCALL_CONTRACT; - if (::FileTimeToSystemTime((FILETIME*)&fileTime, (LPSYSTEMTIME) time)) - { - // to keep the time precision - time->hundredNanoSecond = fileTime % 10000; // 10000 is the number of 100-nano seconds per Millisecond - if (time->systemTime.wSecond > 59) - { - // we have a leap second, force it to last second in the minute as DateTime doesn't account for leap seconds in its calculation. - // we use the maxvalue from the milliseconds and the 100-nano seconds to avoid reporting two out of order 59 seconds - time->systemTime.wSecond = 59; - time->systemTime.wMilliseconds = 999; - time->hundredNanoSecond = 9999; - } - FC_RETURN_BOOL(TRUE); - } - FC_RETURN_BOOL(FALSE); -} -FCIMPLEND; - -FCIMPL2(FC_BOOL_RET, SystemNative::ValidateSystemTime, SYSTEMTIME *time, CLR_BOOL localTime) -{ - FCALL_CONTRACT; - - if (localTime) - { - SYSTEMTIME st; - FC_RETURN_BOOL(::TzSpecificLocalTimeToSystemTime(NULL, time, &st)); - } - else - { - FILETIME timestamp; - FC_RETURN_BOOL(::SystemTimeToFileTime(time, ×tamp)); - } -} -FCIMPLEND; - -FCIMPL2(FC_BOOL_RET, SystemNative::SystemTimeToFileTime, SYSTEMTIME *time, INT64 *pFileTime) -{ - FCALL_CONTRACT; - - BOOL ret = ::SystemTimeToFileTime(time, (LPFILETIME) pFileTime); - FC_RETURN_BOOL(ret); -} -FCIMPLEND; -#endif // TARGET_UNIX FCIMPL0(UINT32, SystemNative::GetTickCount) diff --git a/src/coreclr/classlibnative/bcltype/system.h b/src/coreclr/classlibnative/bcltype/system.h index 5bbf73d4a1048..fe8b1e476a160 100644 --- a/src/coreclr/classlibnative/bcltype/system.h +++ b/src/coreclr/classlibnative/bcltype/system.h @@ -43,13 +43,6 @@ class SystemNative public: // Functions on the System.Environment class -#ifndef TARGET_UNIX - static FCDECL1(VOID, GetSystemTimeWithLeapSecondsHandling, FullSystemTime *time); - static FCDECL2(FC_BOOL_RET, ValidateSystemTime, SYSTEMTIME *time, CLR_BOOL localTime); - static FCDECL2(FC_BOOL_RET, FileTimeToSystemTime, INT64 fileTime, FullSystemTime *time); - static FCDECL2(FC_BOOL_RET, SystemTimeToFileTime, SYSTEMTIME *time, INT64 *pFileTime); -#endif // TARGET_UNIX - static FCDECL0(INT64, __GetSystemTimeAsFileTime); static FCDECL0(UINT32, GetTickCount); static FCDECL0(UINT64, GetTickCount64); diff --git a/src/coreclr/vm/ecalllist.h b/src/coreclr/vm/ecalllist.h index bdab73a937776..e34f84c6125c8 100644 --- a/src/coreclr/vm/ecalllist.h +++ b/src/coreclr/vm/ecalllist.h @@ -131,16 +131,6 @@ FCFuncStart(gDiagnosticsStackTrace) FCFuncElement("GetStackFramesInternal", DebugStackTrace::GetStackFramesInternal) FCFuncEnd() -FCFuncStart(gDateTimeFuncs) -#if !defined(TARGET_UNIX) - FCFuncElement("GetSystemTimeWithLeapSecondsHandling", SystemNative::GetSystemTimeWithLeapSecondsHandling) - FCFuncElement("ValidateSystemTime", SystemNative::ValidateSystemTime) - FCFuncElement("FileTimeToSystemTime", SystemNative::FileTimeToSystemTime) - FCFuncElement("SystemTimeToFileTime", SystemNative::SystemTimeToFileTime) -#endif // TARGET_UNIX - FCFuncElement("GetSystemTimeAsFileTime", SystemNative::__GetSystemTimeAsFileTime) -FCFuncEnd() - FCFuncStart(gEnvironmentFuncs) FCFuncElement("get_CurrentManagedThreadId", JIT_GetCurrentManagedThreadId) FCFuncElement("get_TickCount", SystemNative::GetTickCount) @@ -1121,7 +1111,6 @@ FCClassElement("ComWrappers", "System.Runtime.InteropServices", gComWrappersFunc FCClassElement("CompatibilitySwitch", "System.Runtime.Versioning", gCompatibilitySwitchFuncs) FCClassElement("CustomAttribute", "System.Reflection", gCOMCustomAttributeFuncs) FCClassElement("CustomAttributeEncodedArgument", "System.Reflection", gCustomAttributeEncodedArgument) -FCClassElement("DateTime", "System", gDateTimeFuncs) FCClassElement("Debugger", "System.Diagnostics", gDiagnosticsDebugger) FCClassElement("Delegate", "System", gDelegateFuncs) FCClassElement("DependentHandle", "System.Runtime.CompilerServices", gDependentHandleFuncs) diff --git a/src/libraries/Common/src/Interop/Unix/System.Native/Interop.GetSystemTimeAsTicks.cs b/src/libraries/Common/src/Interop/Unix/System.Native/Interop.GetSystemTimeAsTicks.cs index 6811f5b45bfc4..359d3775f02b1 100644 --- a/src/libraries/Common/src/Interop/Unix/System.Native/Interop.GetSystemTimeAsTicks.cs +++ b/src/libraries/Common/src/Interop/Unix/System.Native/Interop.GetSystemTimeAsTicks.cs @@ -8,6 +8,7 @@ internal static partial class Interop internal partial class Sys { [DllImport(Interop.Libraries.SystemNative, EntryPoint = "SystemNative_GetSystemTimeAsTicks")] + [SuppressGCTransition] internal static extern long GetSystemTimeAsTicks(); } } diff --git a/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.FileTimeToSystemTime.cs b/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.FileTimeToSystemTime.cs index 95b98e47d3b0c..3a219035f6197 100644 --- a/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.FileTimeToSystemTime.cs +++ b/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.FileTimeToSystemTime.cs @@ -8,6 +8,7 @@ internal static partial class Interop internal static partial class Kernel32 { [DllImport(Libraries.Kernel32)] + [SuppressGCTransition] internal static extern unsafe Interop.BOOL FileTimeToSystemTime(long* lpFileTime, Interop.Kernel32.SYSTEMTIME* lpSystemTime); } } diff --git a/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.GetSystemTime.cs b/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.GetSystemTime.cs index 78ab4e93c2fa2..8f49121906c02 100644 --- a/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.GetSystemTime.cs +++ b/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.GetSystemTime.cs @@ -8,6 +8,7 @@ internal static partial class Interop internal static partial class Kernel32 { [DllImport(Libraries.Kernel32)] + [SuppressGCTransition] internal static extern unsafe void GetSystemTime(Interop.Kernel32.SYSTEMTIME* lpSystemTime); } } diff --git a/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.GetSystemTimeAsFileTime.cs b/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.GetSystemTimeAsFileTime.cs deleted file mode 100644 index cae4594d9a353..0000000000000 --- a/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.GetSystemTimeAsFileTime.cs +++ /dev/null @@ -1,13 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -using System.Runtime.InteropServices; - -internal static partial class Interop -{ - internal static partial class Kernel32 - { - [DllImport(Libraries.Kernel32)] - internal static extern unsafe void GetSystemTimeAsFileTime(long* lpSystemTimeAsFileTime); - } -} diff --git a/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.GetSystemTimePreciseAsFileTime.cs b/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.GetSystemTimePreciseAsFileTime.cs deleted file mode 100644 index 0f79e01529024..0000000000000 --- a/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.GetSystemTimePreciseAsFileTime.cs +++ /dev/null @@ -1,13 +0,0 @@ -// Licensed to the .NET Foundation under one or more agreements. -// The .NET Foundation licenses this file to you under the MIT license. - -using System.Runtime.InteropServices; - -internal static partial class Interop -{ - internal static partial class Kernel32 - { - [DllImport(Libraries.Kernel32)] - internal static extern unsafe void GetSystemTimePreciseAsFileTime(long* lpSystemTimeAsFileTime); - } -} diff --git a/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.SystemTimeToFileTime.cs b/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.SystemTimeToFileTime.cs index 852053b6c5f31..69a4a4622d336 100644 --- a/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.SystemTimeToFileTime.cs +++ b/src/libraries/Common/src/Interop/Windows/Kernel32/Interop.SystemTimeToFileTime.cs @@ -8,6 +8,7 @@ internal static partial class Interop internal static partial class Kernel32 { [DllImport(Libraries.Kernel32)] + [SuppressGCTransition] internal static extern unsafe Interop.BOOL SystemTimeToFileTime(Interop.Kernel32.SYSTEMTIME* lpSystemTime, long* lpFileTime); } } diff --git a/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems b/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems index dafa471e67f9c..af8dac2c31374 100644 --- a/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems +++ b/src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems @@ -1401,12 +1401,6 @@ Common\Interop\Windows\Kernel32\Interop.GetSystemTime.cs - - Common\Interop\Windows\Kernel32\Interop.GetSystemTimeAsFileTime.cs - - - Common\Interop\Windows\Kernel32\Interop.GetSystemTimePreciseAsFileTime.cs - Common\Interop\Windows\Kernel32\Interop.GetSystemTimes.cs diff --git a/src/libraries/System.Private.CoreLib/src/System/DateTime.Unix.cs b/src/libraries/System.Private.CoreLib/src/System/DateTime.Unix.cs index 000c72db38c3b..7c1df12cbdf76 100644 --- a/src/libraries/System.Private.CoreLib/src/System/DateTime.Unix.cs +++ b/src/libraries/System.Private.CoreLib/src/System/DateTime.Unix.cs @@ -7,7 +7,6 @@ public readonly partial struct DateTime { internal const bool s_systemSupportsLeapSeconds = false; -#if !CORECLR public static DateTime UtcNow { get @@ -15,7 +14,6 @@ public static DateTime UtcNow return new DateTime(((ulong)(Interop.Sys.GetSystemTimeAsTicks() + UnixEpochTicks)) | KindUtc); } } -#endif private static DateTime FromFileTimeLeapSecondsAware(long fileTime) => default; private static long ToFileTimeLeapSecondsAware(long ticks) => default; diff --git a/src/libraries/System.Private.CoreLib/src/System/DateTime.Windows.cs b/src/libraries/System.Private.CoreLib/src/System/DateTime.Windows.cs index ac4e1fe6be116..a348414c4065a 100644 --- a/src/libraries/System.Private.CoreLib/src/System/DateTime.Windows.cs +++ b/src/libraries/System.Private.CoreLib/src/System/DateTime.Windows.cs @@ -14,14 +14,39 @@ public static unsafe DateTime UtcNow { get { + long fileTime; + s_pfnGetSystemTimeAsFileTime(&fileTime); + if (s_systemSupportsLeapSeconds) { FullSystemTime time; - GetSystemTimeWithLeapSecondsHandling(&time); + + if (Interop.Kernel32.FileTimeToSystemTime(&fileTime, &time.systemTime) != Interop.BOOL.FALSE) + { + // to keep the time precision + time.hundredNanoSecond = fileTime % 10000; // 10000 is the number of 100-nano seconds per Millisecond + } + else + { + Interop.Kernel32.GetSystemTime(&time.systemTime); + time.hundredNanoSecond = 0; + } + + if (time.systemTime.Second > 59) + { + // we have a leap second, force it to last second in the minute as DateTime doesn't account for leap seconds in its calculation. + // we use the maxvalue from the milliseconds and the 100-nano seconds to avoid reporting two out of order 59 seconds + time.systemTime.Second = 59; + time.systemTime.Milliseconds = 999; + time.hundredNanoSecond = 9999; + } + return CreateDateTimeFromSystemTime(in time); } - - return new DateTime(((ulong)(GetSystemTimeAsFileTime() + FileTimeOffset)) | KindUtc); + else + { + return new DateTime(((ulong)(fileTime + FileTimeOffset)) | KindUtc); + } } } @@ -30,23 +55,42 @@ internal static unsafe bool IsValidTimeWithLeapSeconds(int year, int month, int DateTime dt = new DateTime(year, month, day); FullSystemTime time = new FullSystemTime(year, month, dt.DayOfWeek, day, hour, minute, second); - return kind switch + if (kind != DateTimeKind.Utc) { - DateTimeKind.Local => ValidateSystemTime(&time.systemTime, localTime: true), - DateTimeKind.Utc => ValidateSystemTime(&time.systemTime, localTime: false), - _ => ValidateSystemTime(&time.systemTime, localTime: true) || ValidateSystemTime(&time.systemTime, localTime: false), - }; + Interop.Kernel32.SYSTEMTIME st; + if (Interop.Kernel32.TzSpecificLocalTimeToSystemTime(IntPtr.Zero, &time.systemTime, &st) != Interop.BOOL.FALSE) + return true; + } + + if (kind != DateTimeKind.Local) + { + long ft; + if (Interop.Kernel32.SystemTimeToFileTime(&time.systemTime, &ft) != Interop.BOOL.FALSE) + return true; + } + + return false; } private static unsafe DateTime FromFileTimeLeapSecondsAware(long fileTime) { FullSystemTime time; - if (FileTimeToSystemTime(fileTime, &time)) + if (Interop.Kernel32.FileTimeToSystemTime(&fileTime, &time.systemTime) == Interop.BOOL.FALSE) { - return CreateDateTimeFromSystemTime(in time); + throw new ArgumentOutOfRangeException(nameof(fileTime), SR.ArgumentOutOfRange_DateTimeBadTicks); } - throw new ArgumentOutOfRangeException(nameof(fileTime), SR.ArgumentOutOfRange_DateTimeBadTicks); + // to keep the time precision + time.hundredNanoSecond = fileTime % TicksPerMillisecond; + if (time.systemTime.Second > 59) + { + // we have a leap second, force it to last second in the minute as DateTime doesn't account for leap seconds in its calculation. + // we use the maxvalue from the milliseconds and the 100-nano seconds to avoid reporting two out of order 59 seconds + time.systemTime.Second = 59; + time.systemTime.Milliseconds = 999; + time.hundredNanoSecond = 9999; + } + return CreateDateTimeFromSystemTime(in time); } private static unsafe long ToFileTimeLeapSecondsAware(long ticks) @@ -54,12 +98,12 @@ private static unsafe long ToFileTimeLeapSecondsAware(long ticks) FullSystemTime time = new FullSystemTime(ticks); long fileTime; - if (SystemTimeToFileTime(&time.systemTime, &fileTime)) + if (Interop.Kernel32.SystemTimeToFileTime(&time.systemTime, &fileTime) == Interop.BOOL.FALSE) { - return fileTime + ticks % TicksPerMillisecond; + throw new ArgumentOutOfRangeException(null, SR.ArgumentOutOfRange_FileTimeInvalid); } - throw new ArgumentOutOfRangeException(null, SR.ArgumentOutOfRange_FileTimeInvalid); + return fileTime + ticks % TicksPerMillisecond; } [MethodImpl(MethodImplOptions.AggressiveInlining)] @@ -111,12 +155,14 @@ internal FullSystemTime(long ticks) } } -#if !CORECLR - internal static readonly bool s_systemSupportsPreciseSystemTime = SystemSupportsPreciseSystemTime(); + private static unsafe readonly delegate* unmanaged[SuppressGCTransition] s_pfnGetSystemTimeAsFileTime = GetGetSystemTimeAsFileTimeFnPtr(); - private static unsafe bool SystemSupportsPreciseSystemTime() + private static unsafe delegate* unmanaged[SuppressGCTransition] GetGetSystemTimeAsFileTimeFnPtr() { - if (Environment.IsWindows8OrAbove) + IntPtr kernel32Lib = NativeLibrary.Load("kernel32.dll", typeof(DateTime).Assembly, DllImportSearchPath.System32); + IntPtr pfnGetSystemTime = NativeLibrary.GetExport(kernel32Lib, "GetSystemTimeAsFileTime"); + + if (NativeLibrary.TryGetExport(kernel32Lib, "GetSystemTimePreciseAsFileTime", out IntPtr pfnGetSystemTimePrecise)) { // GetSystemTimePreciseAsFileTime exists and we'd like to use it. However, on // misconfigured systems, it's possible for the "precise" time to be inaccurate: @@ -127,88 +173,23 @@ private static unsafe bool SystemSupportsPreciseSystemTime() // 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. - long systemTimeResult; - Interop.Kernel32.GetSystemTimeAsFileTime(&systemTimeResult); - - long preciseSystemTimeResult; - Interop.Kernel32.GetSystemTimePreciseAsFileTime(&preciseSystemTimeResult); - - return Math.Abs(preciseSystemTimeResult - systemTimeResult) <= 100 * TicksPerMillisecond; - } - - return false; - } - - private static unsafe bool ValidateSystemTime(Interop.Kernel32.SYSTEMTIME* time, bool localTime) - { - if (localTime) - { - Interop.Kernel32.SYSTEMTIME st; - return Interop.Kernel32.TzSpecificLocalTimeToSystemTime(IntPtr.Zero, time, &st) != Interop.BOOL.FALSE; - } - else - { - long timestamp; - return Interop.Kernel32.SystemTimeToFileTime(time, ×tamp) != Interop.BOOL.FALSE; - } - } - - private static unsafe bool FileTimeToSystemTime(long fileTime, FullSystemTime* time) - { - if (Interop.Kernel32.FileTimeToSystemTime(&fileTime, &time->systemTime) != Interop.BOOL.FALSE) - { - // to keep the time precision - time->hundredNanoSecond = fileTime % TicksPerMillisecond; - if (time->systemTime.Second > 59) - { - // we have a leap second, force it to last second in the minute as DateTime doesn't account for leap seconds in its calculation. - // we use the maxvalue from the milliseconds and the 100-nano seconds to avoid reporting two out of order 59 seconds - time->systemTime.Second = 59; - time->systemTime.Milliseconds = 999; - time->hundredNanoSecond = 9999; - } - return true; - } - return false; - } - - private static unsafe void GetSystemTimeWithLeapSecondsHandling(FullSystemTime* time) - { - if (!FileTimeToSystemTime(GetSystemTimeAsFileTime(), time)) - { - Interop.Kernel32.GetSystemTime(&time->systemTime); - time->hundredNanoSecond = 0; - if (time->systemTime.Second > 59) + // Retry this check several times to reduce chance of false negatives due to thread being rescheduled + // at wrong time. + for (int i = 0; i < 10; i++) { - // we have a leap second, force it to last second in the minute as DateTime doesn't account for leap seconds in its calculation. - // we use the maxvalue from the milliseconds and the 100-nano seconds to avoid reporting two out of order 59 seconds - time->systemTime.Second = 59; - time->systemTime.Milliseconds = 999; - time->hundredNanoSecond = 9999; + long systemTimeResult, preciseSystemTimeResult; + ((delegate* unmanaged[SuppressGCTransition])pfnGetSystemTime)(&systemTimeResult); + ((delegate* unmanaged[SuppressGCTransition])pfnGetSystemTimePrecise)(&preciseSystemTimeResult); + + if (Math.Abs(preciseSystemTimeResult - systemTimeResult) <= 100 * TicksPerMillisecond) + { + pfnGetSystemTime = pfnGetSystemTimePrecise; // use the precise version + break; + } } } - } - - private static unsafe bool SystemTimeToFileTime(Interop.Kernel32.SYSTEMTIME* time, long* fileTime) - { - return Interop.Kernel32.SystemTimeToFileTime(time, fileTime) != Interop.BOOL.FALSE; - } - - private static unsafe long GetSystemTimeAsFileTime() - { - long timestamp; - - if (s_systemSupportsPreciseSystemTime) - { - Interop.Kernel32.GetSystemTimePreciseAsFileTime(×tamp); - } - else - { - Interop.Kernel32.GetSystemTimeAsFileTime(×tamp); - } - return timestamp; + return (delegate* unmanaged[SuppressGCTransition])pfnGetSystemTime; } -#endif } }