Move leap seconds DateTime configuration statics into LeapSecondCache class (#77163)
authorJan Kotas <jkotas@microsoft.com>
Tue, 18 Oct 2022 19:48:09 +0000 (12:48 -0700)
committerGitHub <noreply@github.com>
Tue, 18 Oct 2022 19:48:09 +0000 (12:48 -0700)
This enables AOT initialization of public readonly DateTime statics like DateTime.Min/MaxValue.

src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems
src/libraries/System.Private.CoreLib/src/System/DateTime.Unix.cs
src/libraries/System.Private.CoreLib/src/System/DateTime.Win32.cs [deleted file]
src/libraries/System.Private.CoreLib/src/System/DateTime.Windows.cs
src/libraries/System.Private.CoreLib/src/System/DateTime.cs
src/libraries/System.Private.CoreLib/src/System/DateTimeOffset.cs

index 2cdc6ee9731c4f7d16e671bdcc9ed4b7c2099da0..9d02fee3906858c830023e00c0ba356935333198 100644 (file)
     <Compile Include="$(MSBuildThisFileDirectory)Microsoft\Win32\SafeHandles\SafeRegistryHandle.Windows.cs" />
     <Compile Include="$(MSBuildThisFileDirectory)System\AppDomain.Windows.cs" />
     <Compile Include="$(MSBuildThisFileDirectory)System\Buffer.Windows.cs" />
-    <Compile Include="$(MSBuildThisFileDirectory)System\DateTime.Win32.cs" />
     <Compile Include="$(MSBuildThisFileDirectory)System\DateTime.Windows.cs" />
     <Compile Include="$(MSBuildThisFileDirectory)System\Environment.Win32.cs" />
     <Compile Include="$(MSBuildThisFileDirectory)System\Environment.Windows.cs" />
index 0c35b864ff4c6c33b39a93c8964061aa74c9e702..8dd87e3105d0b69f6e6e8d9ac32389ec9c7ea57c 100644 (file)
@@ -5,7 +5,7 @@ namespace System
 {
     public readonly partial struct DateTime
     {
-        internal const bool s_systemSupportsLeapSeconds = false;
+        internal static bool SystemSupportsLeapSeconds => false;
 
         public static DateTime UtcNow
         {
diff --git a/src/libraries/System.Private.CoreLib/src/System/DateTime.Win32.cs b/src/libraries/System.Private.CoreLib/src/System/DateTime.Win32.cs
deleted file mode 100644 (file)
index 15aca35..0000000
+++ /dev/null
@@ -1,19 +0,0 @@
-// Licensed to the .NET Foundation under one or more agreements.
-// The .NET Foundation licenses this file to you under the MIT license.
-
-namespace System
-{
-    public readonly partial struct DateTime
-    {
-        private static unsafe bool SystemSupportsLeapSeconds()
-        {
-            Interop.NtDll.SYSTEM_LEAP_SECOND_INFORMATION slsi;
-
-            return Interop.NtDll.NtQuerySystemInformation(
-                Interop.NtDll.SystemLeapSecondInformation,
-                &slsi,
-                (uint)sizeof(Interop.NtDll.SYSTEM_LEAP_SECOND_INFORMATION),
-                null) == 0 && slsi.Enabled != Interop.BOOLEAN.FALSE;
-        }
-    }
-}
index 5ec5c7d0548a46023c126b67ae21f275a7495180..1b72fccb3f4ec7b11022e66679b6c7ed7224c1f6 100644 (file)
@@ -10,21 +10,21 @@ namespace System
 {
     public readonly partial struct DateTime
     {
-        internal static readonly bool s_systemSupportsLeapSeconds = SystemSupportsLeapSeconds();
+        internal static bool SystemSupportsLeapSeconds => LeapSecondCache.s_systemSupportsLeapSeconds;
 
         public static unsafe DateTime UtcNow
         {
             get
             {
                 ulong fileTimeTmp; // mark only the temp local as address-taken
-                s_pfnGetSystemTimeAsFileTime(&fileTimeTmp);
+                LeapSecondCache.s_pfnGetSystemTimeAsFileTime(&fileTimeTmp);
                 ulong fileTime = fileTimeTmp;
 
-                if (s_systemSupportsLeapSeconds)
+                if (LeapSecondCache.s_systemSupportsLeapSeconds)
                 {
                     // Query the leap second cache first, which avoids expensive calls to GetFileTimeAsSystemTime.
 
-                    LeapSecondCache cacheValue = s_leapSecondCache;
+                    LeapSecondCache cacheValue = LeapSecondCache.s_leapSecondCache;
                     ulong ticksSinceStartOfCacheValidityWindow = fileTime - cacheValue.OSFileTimeTicksAtStartOfValidityWindow;
                     if (ticksSinceStartOfCacheValidityWindow < LeapSecondCache.ValidityPeriodInTicks)
                     {
@@ -129,7 +129,16 @@ namespace System
             return new DateTime(ticks);
         }
 
-        private static readonly unsafe delegate* unmanaged[SuppressGCTransition]<ulong*, void> s_pfnGetSystemTimeAsFileTime = GetGetSystemTimeAsFileTimeFnPtr();
+        private static unsafe bool GetSystemSupportsLeapSeconds()
+        {
+            Interop.NtDll.SYSTEM_LEAP_SECOND_INFORMATION slsi;
+
+            return Interop.NtDll.NtQuerySystemInformation(
+                Interop.NtDll.SystemLeapSecondInformation,
+                &slsi,
+                (uint)sizeof(Interop.NtDll.SYSTEM_LEAP_SECOND_INFORMATION),
+                null) == 0 && slsi.Enabled != Interop.BOOLEAN.FALSE;
+        }
 
         private static unsafe delegate* unmanaged[SuppressGCTransition]<ulong*, void> GetGetSystemTimeAsFileTimeFnPtr()
         {
@@ -184,11 +193,11 @@ namespace System
             // OS update occurs and a past leap second is added, this limits the window in which our
             // cache will return incorrect values.
 
-            Debug.Assert(s_systemSupportsLeapSeconds);
+            Debug.Assert(SystemSupportsLeapSeconds);
             Debug.Assert(LeapSecondCache.ValidityPeriodInTicks < TicksPerDay - TicksPerSecond, "Leap second cache validity window should be less than 23:59:59.");
 
             ulong fileTimeNow;
-            s_pfnGetSystemTimeAsFileTime(&fileTimeNow);
+            LeapSecondCache.s_pfnGetSystemTimeAsFileTime(&fileTimeNow);
 
             // If we reached this point, our leap second cache is stale, and we need to update it.
             // First, convert the FILETIME to a SYSTEMTIME.
@@ -292,7 +301,7 @@ namespace System
             // Finally, update the cache and return UtcNow.
 
             Debug.Assert(fileTimeNow - fileTimeAtStartOfValidityWindow < LeapSecondCache.ValidityPeriodInTicks, "We should be within the validity window.");
-            Volatile.Write(ref s_leapSecondCache, new LeapSecondCache()
+            Volatile.Write(ref LeapSecondCache.s_leapSecondCache, new LeapSecondCache()
             {
                 OSFileTimeTicksAtStartOfValidityWindow = fileTimeAtStartOfValidityWindow,
                 DotnetDateDataAtStartOfValidityWindow = dotnetDateDataAtStartOfValidityWindow
@@ -318,11 +327,6 @@ namespace System
             }
         }
 
-        // The leap second cache. May be accessed by multiple threads simultaneously.
-        // Writers must not mutate the object's fields after the reference is published.
-        // Readers are not required to use volatile semantics.
-        private static LeapSecondCache s_leapSecondCache = new LeapSecondCache();
-
         private sealed class LeapSecondCache
         {
             // The length of the validity window. Must be less than 23:59:59.
@@ -333,6 +337,16 @@ namespace System
 
             // The DateTime._dateData value at the beginning of the validity window.
             internal ulong DotnetDateDataAtStartOfValidityWindow;
+
+            // The leap second cache. May be accessed by multiple threads simultaneously.
+            // Writers must not mutate the object's fields after the reference is published.
+            // Readers are not required to use volatile semantics.
+            internal static LeapSecondCache s_leapSecondCache = new LeapSecondCache();
+
+            // The configuration of system leap seconds support is intentionally here to avoid blocking
+            // AOT pre-initialization of public readonly DateTime statics.
+            internal static readonly bool s_systemSupportsLeapSeconds = GetSystemSupportsLeapSeconds();
+            internal static readonly unsafe delegate* unmanaged[SuppressGCTransition]<ulong*, void> s_pfnGetSystemTimeAsFileTime = GetGetSystemTimeAsFileTimeFnPtr();
         }
     }
 }
index bcf0d2dd8e0dfa58df64634d04e4400177f2f607..11c566195daf8bebcfe7e93cb7a15de2a187a211 100644 (file)
@@ -273,7 +273,7 @@ namespace System
             if ((uint)millisecond >= MillisPerSecond) ThrowMillisecondOutOfRange();
             if ((uint)kind > (uint)DateTimeKind.Local) ThrowInvalidKind();
 
-            if (second != 60 || !s_systemSupportsLeapSeconds)
+            if (second != 60 || !SystemSupportsLeapSeconds)
             {
                 ulong ticks = calendar.ToDateTime(year, month, day, hour, minute, second, millisecond).UTicks;
                 _dateData = ticks | ((ulong)kind << KindShift);
@@ -291,7 +291,7 @@ namespace System
         //
         public DateTime(int year, int month, int day, int hour, int minute, int second)
         {
-            if (second != 60 || !s_systemSupportsLeapSeconds)
+            if (second != 60 || !SystemSupportsLeapSeconds)
             {
                 _dateData = DateToTicks(year, month, day) + TimeToTicks(hour, minute, second);
             }
@@ -307,7 +307,7 @@ namespace System
         {
             if ((uint)kind > (uint)DateTimeKind.Local) ThrowInvalidKind();
 
-            if (second != 60 || !s_systemSupportsLeapSeconds)
+            if (second != 60 || !SystemSupportsLeapSeconds)
             {
                 ulong ticks = DateToTicks(year, month, day) + TimeToTicks(hour, minute, second);
                 _dateData = ticks | ((ulong)kind << KindShift);
@@ -327,7 +327,7 @@ namespace System
         {
             ArgumentNullException.ThrowIfNull(calendar);
 
-            if (second != 60 || !s_systemSupportsLeapSeconds)
+            if (second != 60 || !SystemSupportsLeapSeconds)
             {
                 _dateData = calendar.ToDateTime(year, month, day, hour, minute, second, 0).UTicks;
             }
@@ -500,7 +500,7 @@ namespace System
         {
             ArgumentNullException.ThrowIfNull(calendar);
 
-            if (second != 60 || !s_systemSupportsLeapSeconds)
+            if (second != 60 || !SystemSupportsLeapSeconds)
             {
                 _dateData = calendar.ToDateTime(year, month, day, hour, minute, second, millisecond).UTicks;
             }
@@ -777,7 +777,7 @@ namespace System
             if ((uint)millisecond >= MillisPerSecond) ThrowMillisecondOutOfRange();
             if ((uint)kind > (uint)DateTimeKind.Local) ThrowInvalidKind();
 
-            if (second != 60 || !s_systemSupportsLeapSeconds)
+            if (second != 60 || !SystemSupportsLeapSeconds)
             {
                 ulong ticks = DateToTicks(year, month, day) + TimeToTicks(hour, minute, second);
                 ticks += (uint)millisecond * (uint)TicksPerMillisecond;
@@ -1257,12 +1257,10 @@ namespace System
                 throw new ArgumentOutOfRangeException(nameof(fileTime), SR.ArgumentOutOfRange_FileTimeInvalid);
             }
 
-#pragma warning disable 162 // Unrechable code on Unix
-            if (s_systemSupportsLeapSeconds)
+            if (SystemSupportsLeapSeconds)
             {
                 return FromFileTimeLeapSecondsAware((ulong)fileTime);
             }
-#pragma warning restore 162
 
             // This is the ticks in Universal time for this fileTime.
             ulong universalTicks = (ulong)fileTime + FileTimeOffset;
@@ -1696,12 +1694,10 @@ namespace System
             // Treats the input as universal if it is not specified
             long ticks = ((_dateData & KindLocal) != 0) ? ToUniversalTime().Ticks : Ticks;
 
-#pragma warning disable 162 // Unrechable code on Unix
-            if (s_systemSupportsLeapSeconds)
+            if (SystemSupportsLeapSeconds)
             {
                 return (long)ToFileTimeLeapSecondsAware(ticks);
             }
-#pragma warning restore 162
 
             ticks -= FileTimeOffset;
             if (ticks < 0)
@@ -1967,7 +1963,7 @@ namespace System
             {
                 ticks += TimeToTicks(hour, minute, second) + (uint)millisecond * (uint)TicksPerMillisecond;
             }
-            else if (second == 60 && s_systemSupportsLeapSeconds && IsValidTimeWithLeapSeconds(year, month, day, hour, minute, DateTimeKind.Unspecified))
+            else if (second == 60 && SystemSupportsLeapSeconds && IsValidTimeWithLeapSeconds(year, month, day, hour, minute, DateTimeKind.Unspecified))
             {
                 // if we have leap second (second = 60) then we'll need to check if it is valid time.
                 // if it is valid, then we adjust the second to 59 so DateTime will consider this second is last second
index b2289d1377acebc94e413994822c07c7aff0be2b..7c05f4ef5216b1aea53be53b7684ffeb72eb5716 100644 (file)
@@ -123,7 +123,7 @@ namespace System
             _offsetMinutes = ValidateOffset(offset);
 
             int originalSecond = second;
-            if (second == 60 && DateTime.s_systemSupportsLeapSeconds)
+            if (second == 60 && DateTime.SystemSupportsLeapSeconds)
             {
                 // Reset the leap second to 59 for now and then we'll validate it after getting the final UTC time.
                 second = 59;
@@ -145,7 +145,7 @@ namespace System
             _offsetMinutes = ValidateOffset(offset);
 
             int originalSecond = second;
-            if (second == 60 && DateTime.s_systemSupportsLeapSeconds)
+            if (second == 60 && DateTime.SystemSupportsLeapSeconds)
             {
                 // Reset the leap second to 59 for now and then we'll validate it after getting the final UTC time.
                 second = 59;
@@ -167,7 +167,7 @@ namespace System
             _offsetMinutes = ValidateOffset(offset);
 
             int originalSecond = second;
-            if (second == 60 && DateTime.s_systemSupportsLeapSeconds)
+            if (second == 60 && DateTime.SystemSupportsLeapSeconds)
             {
                 // Reset the leap second to 59 for now and then we'll validate it after getting the final UTC time.
                 second = 59;