Skip to content
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
16 changes: 15 additions & 1 deletion src/coreclr/gc/windows/gcenv.windows.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1091,7 +1091,21 @@ int64_t GCToOSInterface::QueryPerformanceFrequency()
// Time stamp in milliseconds
uint64_t GCToOSInterface::GetLowPrecisionTimeStamp()
{
return ::GetTickCount64();
// GetTickCount64 uses fixed resolution of 10-16ms for backward compatibility. Use
// QueryUnbiasedInterruptTime instead which becomes more accurate if the underlying system
// resolution is improved. This helps responsiveness in the case an app is trying to opt
// into things like multimedia scenarios and additionally does not include "bias" from time
// the system is spent asleep or in hibernation.

const ULONGLONG TicksPerMillisecond = 10000;

ULONGLONG unbiasedTime;
if (!::QueryUnbiasedInterruptTime(&unbiasedTime))
{
assert(false && "Failed to query unbiased interrupt time");
}

return (uint64_t)(unbiasedTime / TicksPerMillisecond);
}

// Gets the total number of processors on the machine, not taking
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,16 @@ internal static partial class Interop
{
internal static partial class Kernel32
{
// The actual native signature is:
// BOOL WINAPI QueryUnbiasedInterruptTime(
// _Out_ PULONGLONG UnbiasedTime
// );
//
// We take a ulong* (rather than a out ulong) to avoid the pinning overhead.
// We don't set last error since we don't need the extended error info.

[LibraryImport(Libraries.Kernel32)]
[return: MarshalAs(UnmanagedType.Bool)]
internal static partial bool QueryUnbiasedInterruptTime(out ulong UnbiasedTime);
[SuppressGCTransition]
internal static unsafe partial BOOL QueryUnbiasedInterruptTime(ulong* unbiasedTime);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -377,6 +377,27 @@ public static ProcessCpuUsage CpuUsage

/// <summary>Gets the number of milliseconds elapsed since the system started.</summary>
/// <value>A 64-bit signed integer containing the amount of time in milliseconds that has passed since the last time the computer was started.</value>
public static long TickCount64 => (long)Interop.Kernel32.GetTickCount64();
public static long TickCount64
{
get
{
unsafe
{
// GetTickCount64 uses fixed resolution of 10-16ms for backward compatibility. Use
// QueryUnbiasedInterruptTime instead which becomes more accurate if the underlying system
// resolution is improved. This helps responsiveness in the case an app is trying to opt
// into things like multimedia scenarios and additionally does not include "bias" from time
// the system is spent asleep or in hibernation.

ulong unbiasedTime;

Interop.BOOL result = Interop.Kernel32.QueryUnbiasedInterruptTime(&unbiasedTime);
// The P/Invoke is documented to only fail if a null-ptr is passed in
Debug.Assert(result != Interop.BOOL.FALSE);

return (long)(unbiasedTime / TimeSpan.TicksPerMillisecond);
}
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ internal sealed partial class TimerQueue
{
#region Shared TimerQueue instances
/// <summary>Mapping from a tick count to a time to use when debugging to translate tick count values.</summary>
internal static readonly (long TickCount, DateTime Time) s_tickCountToTimeMap = (TickCount64, DateTime.UtcNow);
internal static readonly (long TickCount, DateTime Time) s_tickCountToTimeMap = (Environment.TickCount64, DateTime.UtcNow);

public static TimerQueue[] Instances { get; } = CreateTimerQueues();

Expand Down Expand Up @@ -126,7 +126,7 @@ private bool EnsureTimerFiresBy(uint requestedDuration)

if (_isTimerScheduled)
{
long elapsed = TickCount64 - _currentTimerStartTicks;
long elapsed = Environment.TickCount64 - _currentTimerStartTicks;
if (elapsed >= _currentTimerDuration)
return true; // the timer's about to fire

Expand All @@ -138,7 +138,7 @@ private bool EnsureTimerFiresBy(uint requestedDuration)
if (SetTimer(actualDuration))
{
_isTimerScheduled = true;
_currentTimerStartTicks = TickCount64;
_currentTimerStartTicks = Environment.TickCount64;
_currentTimerDuration = actualDuration;
return true;
}
Expand All @@ -161,7 +161,7 @@ private bool EnsureTimerFiresBy(uint requestedDuration)

// The current threshold, an absolute time where any timers scheduled to go off at or
// before this time must be queued to the short list.
private long _currentAbsoluteThreshold = TickCount64 + ShortTimersThresholdMilliseconds;
private long _currentAbsoluteThreshold = Environment.TickCount64 + ShortTimersThresholdMilliseconds;

// Default threshold that separates which timers target _shortTimers vs _longTimers. The threshold
// is chosen to balance the number of timers in the small list against the frequency with which
Expand Down Expand Up @@ -191,7 +191,7 @@ private void FireNextTimers()
bool haveTimerToSchedule = false;
uint nextTimerDuration = uint.MaxValue;

long nowTicks = TickCount64;
long nowTicks = Environment.TickCount64;

// Sweep through the "short" timers. If the current tick count is greater than
// the current threshold, also sweep through the "long" timers. Finally, as part
Expand Down Expand Up @@ -342,7 +342,7 @@ private void FireNextTimers()

public bool UpdateTimer(TimerQueueTimer timer, uint dueTime, uint period)
{
long nowTicks = TickCount64;
long nowTicks = Environment.TickCount64;

// The timer can be put onto the short list if it's next absolute firing time
// is <= the current absolute threshold.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@ namespace System.Threading
//
internal partial class TimerQueue
{
private static long TickCount64 => Environment.TickCount64;
private static List<TimerQueue>? s_scheduledTimers;
private static List<TimerQueue>? s_scheduledTimersToFire;
private static long s_shortestDueTimeMs = long.MaxValue;
Expand Down Expand Up @@ -49,7 +48,7 @@ private static void TimerHandler()
// always only have one scheduled at a time
s_shortestDueTimeMs = long.MaxValue;

long currentTimeMs = TickCount64;
long currentTimeMs = Environment.TickCount64;
ReplaceNextTimer(PumpTimerQueue(currentTimeMs), currentTimeMs);
}
catch (Exception e)
Expand All @@ -62,7 +61,7 @@ private static void TimerHandler()
private bool SetTimer(uint actualDuration)
{
Debug.Assert((int)actualDuration >= 0);
long currentTimeMs = TickCount64;
long currentTimeMs = Environment.TickCount64;
if (!_isScheduled)
{
s_scheduledTimers ??= new List<TimerQueue>(Instances.Length);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ private static List<TimerQueue> InitializeScheduledTimerManager_Locked()
private bool SetTimerPortable(uint actualDuration)
{
Debug.Assert((int)actualDuration >= 0);
long dueTimeMs = TickCount64 + (int)actualDuration;
long dueTimeMs = Environment.TickCount64 + (int)actualDuration;
AutoResetEvent timerEvent = s_timerEvent;
Lock timerEventLock = s_timerEventLock;

Expand Down Expand Up @@ -92,7 +92,7 @@ private static void TimerThread()
{
timerEvent.WaitOne(shortestWaitDurationMs);

long currentTimeMs = TickCount64;
long currentTimeMs = Environment.TickCount64;
shortestWaitDurationMs = int.MaxValue;
lock (timerEventLock)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,6 @@ namespace System.Threading
{
internal sealed partial class TimerQueue
{
public static long TickCount64 => Environment.TickCount64;

#pragma warning disable IDE0060
private TimerQueue(int id)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@ namespace System.Threading
//
internal sealed partial class TimerQueue
{
private static long TickCount64 => Environment.TickCount64;
private static List<TimerQueue>? s_scheduledTimers;
private static List<TimerQueue>? s_scheduledTimersToFire;
private static long s_shortestDueTimeMs = long.MaxValue;
Expand All @@ -36,7 +35,7 @@ private static void TimerHandler(object _)
{
s_shortestDueTimeMs = long.MaxValue;

long currentTimeMs = TickCount64;
long currentTimeMs = Environment.TickCount64;
SetNextTimer(PumpTimerQueue(currentTimeMs), currentTimeMs);
}
catch (Exception e)
Expand All @@ -49,7 +48,7 @@ private static void TimerHandler(object _)
private bool SetTimer(uint actualDuration)
{
Debug.Assert((int)actualDuration >= 0);
long currentTimeMs = TickCount64;
long currentTimeMs = Environment.TickCount64;
if (!_isScheduled)
{
s_scheduledTimers ??= new List<TimerQueue>(Instances.Length);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,33 +12,6 @@ private TimerQueue(int id)
_id = id;
}

public static long TickCount64
{
get
{
// We need to keep our notion of time synchronized with the calls to SleepEx that drive
// the underlying native timer. In Win8, SleepEx does not count the time the machine spends
// sleeping/hibernating. Environment.TickCount (GetTickCount) *does* count that time,
// so we will get out of sync with SleepEx if we use that method.
//
// So, on Win8, we use QueryUnbiasedInterruptTime instead; this does not count time spent
// in sleep/hibernate mode.
if (Environment.IsWindows8OrAbove)
{
// Based on its documentation the QueryUnbiasedInterruptTime() function validates
// the argument is non-null. In this case we are always supplying an argument,
// so will skip return value validation.
bool success = Interop.Kernel32.QueryUnbiasedInterruptTime(out ulong time100ns);
Debug.Assert(success);
return (long)(time100ns / 10_000); // convert from 100ns to milliseconds
}
else
{
return Environment.TickCount64;
}
}
}

private bool SetTimer(uint actualDuration) =>
ThreadPool.UseWindowsThreadPool ?
SetTimerWindowsThreadPool(actualDuration) :
Expand Down
14 changes: 13 additions & 1 deletion src/native/minipal/time.c
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,19 @@ int64_t minipal_hires_tick_frequency()

int64_t minipal_lowres_ticks()
{
return GetTickCount64();
// GetTickCount64 uses fixed resolution of 10-16ms for backward compatibility. Use
// QueryUnbiasedInterruptTime instead which becomes more accurate if the underlying system
// resolution is improved. This helps responsiveness in the case an app is trying to opt
// into things like multimedia scenarios and additionally does not include "bias" from time
// the system is spent asleep or in hibernation.

const ULONGLONG TicksPerMillisecond = 10000;

ULONGLONG unbiasedTime;
BOOL ret;
ret = QueryUnbiasedInterruptTime(&unbiasedTime);
assert(ret); // The function is documented to only fail if a null-ptr is passed in
return (int64_t)(unbiasedTime / TicksPerMillisecond);
}

uint64_t minipal_get_system_time()
Expand Down
Loading