From c9fbbad50f95ea89ae859e515247fe2d815d35f3 Mon Sep 17 00:00:00 2001 From: Steve Pfister Date: Mon, 12 Sep 2022 11:47:17 -0400 Subject: [PATCH] [Android] Update DateTimeOffset.Now fast path routine https://github.com/dotnet/runtime/pull/74965 contained improvements for the routine. This change brings them back to main. --- .../src/System/DateTimeOffset.Android.cs | 66 +++++++------------ 1 file changed, 25 insertions(+), 41 deletions(-) diff --git a/src/libraries/System.Private.CoreLib/src/System/DateTimeOffset.Android.cs b/src/libraries/System.Private.CoreLib/src/System/DateTimeOffset.Android.cs index c78dadcdfc3bd..c40e4708e47aa 100644 --- a/src/libraries/System.Private.CoreLib/src/System/DateTimeOffset.Android.cs +++ b/src/libraries/System.Private.CoreLib/src/System/DateTimeOffset.Android.cs @@ -7,10 +7,8 @@ namespace System { public readonly partial struct DateTimeOffset { - private static bool s_androidTZDataLoaded; - private static readonly object s_localUtcOffsetLock = new(); - private static Thread? s_loadAndroidTZData; - private static bool s_startNewBackgroundThread = true; + // 0 == in process of being loaded, 1 == loaded + private static volatile int s_androidTZDataLoaded = -1; // Now on Android does the following // 1) quickly returning a fast path result when first called if the right AppContext data element is set @@ -29,52 +27,38 @@ public static DateTimeOffset Now { DateTime utcDateTime = DateTime.UtcNow; - if (s_androidTZDataLoaded) // The background thread finished, the cache is loaded. + if (s_androidTZDataLoaded == 1) // The background thread finished, the cache is loaded. + { return ToLocalTime(utcDateTime, true); + } - if (s_startNewBackgroundThread) // The cache isn't loaded and no background thread has been created + object? localDateTimeOffset = AppContext.GetData("System.TimeZoneInfo.LocalDateTimeOffset"); + if (localDateTimeOffset == null) // If no offset property provided through monovm app context, default { - lock (s_localUtcOffsetLock) - { - // Now may be called multiple times before a cache is loaded and a background thread is running, - // once the lock is available, check for a cache and background thread. - if (s_androidTZDataLoaded) - return ToLocalTime(utcDateTime, true); + // no need to create the thread, load tzdata now + s_androidTZDataLoaded = 1; + return ToLocalTime(utcDateTime, true); + } - if (s_loadAndroidTZData == null) + // The cache isn't loaded yet. + if (Interlocked.CompareExchange(ref s_androidTZDataLoaded, 0, -1) == -1) + { + new Thread(() => + { + try { - s_loadAndroidTZData = new Thread(() => { - // Delay the background thread to avoid impacting startup, if it still coincides after 1s, startup is already perceived as slow - Thread.Sleep(1000); - - _ = TimeZoneInfo.Local; // Load AndroidTZData - s_androidTZDataLoaded = true; + // Delay the background thread to avoid impacting startup, if it still coincides after 1s, startup is already perceived as slow + Thread.Sleep(1000); - lock (s_localUtcOffsetLock) - { - s_loadAndroidTZData = null; // Ensure thread is cleared when cache is loaded - } - }); - s_loadAndroidTZData.IsBackground = true; + _ = TimeZoneInfo.Local; // Load AndroidTZData } - } - - if (s_startNewBackgroundThread) - { - // Because Start does not block the calling thread, - // setting the boolean flag to false immediately after should - // prevent two calls to DateTimeOffset.Now in quick succession - // from both reaching here. - s_loadAndroidTZData.Start(); - s_startNewBackgroundThread = false; - } + finally + { + s_androidTZDataLoaded = 1; + } + }) { IsBackground = true }.Start(); } - - object? localDateTimeOffset = AppContext.GetData("System.TimeZoneInfo.LocalDateTimeOffset"); - if (localDateTimeOffset == null) // If no offset property provided through monovm app context, default - return ToLocalTime(utcDateTime, true); - // Fast path obtained offset incorporated into ToLocalTime(DateTime.UtcNow, true) logic int localDateTimeOffsetSeconds = Convert.ToInt32(localDateTimeOffset); TimeSpan offset = TimeSpan.FromSeconds(localDateTimeOffsetSeconds);