Skip to content

Commit

Permalink
[Android] Update DateTimeOffset.Now fast path routine
Browse files Browse the repository at this point in the history
dotnet#74965 contained improvements for the routine. This change brings them back to main.
  • Loading branch information
Steve Pfister committed Sep 12, 2022
1 parent 4486805 commit c9fbbad
Showing 1 changed file with 25 additions and 41 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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);
Expand Down

0 comments on commit c9fbbad

Please sign in to comment.