Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Move leap seconds DateTime configuration statics into LeapSecondCache #77163

Merged
merged 1 commit into from
Oct 18, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -1900,7 +1900,6 @@
<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" />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
{
Expand Down
19 changes: 0 additions & 19 deletions src/libraries/System.Private.CoreLib/src/System/DateTime.Win32.cs

This file was deleted.

40 changes: 27 additions & 13 deletions src/libraries/System.Private.CoreLib/src/System/DateTime.Windows.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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)
{
Expand Down Expand Up @@ -129,7 +129,16 @@ private static DateTime CreateDateTimeFromSystemTime(in Interop.Kernel32.SYSTEMT
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()
{
Expand Down Expand Up @@ -184,11 +193,11 @@ private static unsafe DateTime UpdateLeapSecondCacheAndReturnUtcNow()
// 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.
Expand Down Expand Up @@ -292,7 +301,7 @@ private static unsafe DateTime UpdateLeapSecondCacheAndReturnUtcNow()
// 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
Expand All @@ -318,11 +327,6 @@ static DateTime LowGranularityNonCachedFallback()
}
}

// 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.
Expand All @@ -333,6 +337,16 @@ private sealed class LeapSecondCache

// 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();
}
}
}
22 changes: 9 additions & 13 deletions src/libraries/System.Private.CoreLib/src/System/DateTime.cs
Original file line number Diff line number Diff line change
Expand Up @@ -273,7 +273,7 @@ public DateTime(int year, int month, int day, int hour, int minute, int second,
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);
Expand All @@ -291,7 +291,7 @@ public DateTime(int year, int month, int day, int hour, int minute, int second,
//
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);
}
Expand All @@ -307,7 +307,7 @@ public DateTime(int year, int month, int day, int hour, int minute, int second,
{
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);
Expand All @@ -327,7 +327,7 @@ public DateTime(int year, int month, int day, int hour, int minute, int second,
{
ArgumentNullException.ThrowIfNull(calendar);

if (second != 60 || !s_systemSupportsLeapSeconds)
if (second != 60 || !SystemSupportsLeapSeconds)
{
_dateData = calendar.ToDateTime(year, month, day, hour, minute, second, 0).UTicks;
}
Expand Down Expand Up @@ -500,7 +500,7 @@ public DateTime(int year, int month, int day, int hour, int minute, int second,
{
ArgumentNullException.ThrowIfNull(calendar);

if (second != 60 || !s_systemSupportsLeapSeconds)
if (second != 60 || !SystemSupportsLeapSeconds)
{
_dateData = calendar.ToDateTime(year, month, day, hour, minute, second, millisecond).UTicks;
}
Expand Down Expand Up @@ -777,7 +777,7 @@ private static ulong Init(int year, int month, int day, int hour, int minute, in
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;
Expand Down Expand Up @@ -1257,12 +1257,10 @@ public static DateTime FromFileTimeUtc(long fileTime)
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;
Expand Down Expand Up @@ -1696,12 +1694,10 @@ public long ToFileTimeUtc()
// 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)
Expand Down Expand Up @@ -1967,7 +1963,7 @@ internal static bool TryCreate(int year, int month, int day, int hour, int minut
{
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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -123,7 +123,7 @@ public DateTimeOffset(int year, int month, int day, int hour, int minute, int se
_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;
Expand All @@ -145,7 +145,7 @@ public DateTimeOffset(int year, int month, int day, int hour, int minute, int se
_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;
Expand All @@ -167,7 +167,7 @@ public DateTimeOffset(int year, int month, int day, int hour, int minute, int se
_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;
Expand Down