You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Add build-time check for QueryUnbiasedInterruptTime availability #126551
Several places in the runtime call QueryUnbiasedInterruptTime as a higher-fidelity replacement for GetTickCount64. This API is only available for desktop and UWP apps per the documentation — it is not available in all Windows partitions (e.g. the GAMES partition).
Affected callsites
File
Function
src/coreclr/gc/windows/gcenv.windows.cpp
GCToOSInterface::GetLowPrecisionTimeStamp
src/native/minipal/time.c
minipal_lowres_ticks
src/libraries/.../System/Environment.Windows.cs
Environment.TickCount64
Proposal
Add a CMake feature check (e.g. HAVE_QUERYUNBIASEDINTERRUPTTIME) that detects whether the API is available, and use it consistently across all callsites. For native code this would flow through minipalconfig.h; for managed code it could be an MSBuild property driving a conditional compilation symbol.
When the API is unavailable, GetTickCount64 is an appropriate fallback — it was the prior implementation at all three sites.
Note
This issue was drafted with the assistance of GitHub Copilot.
This may need additional consideration as GetTickCount64 is explicitly not high enough precision for certain domains, such as games, and the underlying OS wait APIs were all changed to use a different timer source in the Windows 7-8 timeframe.
What of the other common timer APIs are available in the GAMES partition and can we opt to use those instead if compiling for that target?
This API is only use for low resolution timing. For high resolution timing there is QueryPerformanceCounter. minpal have two versions, minipal_lowres_ticks and minipal_hires_ticks, all native code should go through these two functions for timing needs.
For managed code, TickCount64 is documented to have low resolution, 10-16ms, so not something useful in any high-performance timing scenarios anyways.
I think its ok to fallback to older GetTickCount64 on systems where the never API is not available and documentation have clearly described this is a low resolution timer for a long time. For high resolution scenarios, use stopwatch or if specific platforms have propriety timing API's, p/invoke into platform specific API's.
I think we should see if gcenv.windows.cpp could use minipal minipal_lowres_ticks, removing the direct call to the Windows API from that source. That would eliminate the first call site in above table.
Sorry, not sure what exactly you're saying here @lateralusX.
We explicitly switched to QueryUnbiasedInterruptTime to ensure we are matching the Windows recommendations and properly respecting thread timer resolution changes made by an application to ensure that it maintains an appropriate level of responsiveness.
GetTickCount64 is the historical API and is a fixed resolution (typically 15.5ms) regardless of thread priority or timer resolution
This API includes the time spent in sleep, hibernation, or other low-power states
QueryUnbiasedInterruptTime defaults to the same resolution as GetTickCount64 but updates with the thread prioritization/timer resolution
This API is "unbiased" and so does not include the time spent in sleep, hibernation, or other low-power states
Windows took an explicit breaking change in the Win8/Server2012 timeframe so that its wait APIs no longer include time spent in sleep, hibernation, or other low power states
This includes APIs like Sleep, SleepEx, WaitForMultipleObjectsEx, etc. Basically anything that underpins the timers, waits, delays, or other functionality we support
QueryPerformanceCounter is the highest precision timer in the system and is usable for microsecond accuracy
This comes with significant cost/overhead in querying the result and handling various edges. It can, however, be used to build the others with the appropriate caching or limit in how regularly it is queried
So we want to prefer the existing APIs (i.e. QueryUnbiasedInterruptTime) that specifically have low latency and precision/throughput that matches the thread scheduling resolution when available. If not available, then we may need to consider what guarantees that particular system provides/expects and may need to roll a custom solution based on QueryPerformanceCounter instead. I would expect that the GAMES partition for Windows provides an appropriate API, possibly even one that isn't part of the regular Windows SDK, for use in such scenarios and that it documents in the GDK/XDK.
Sorry, not sure what exactly you're saying here @lateralusX.
We explicitly switched to QueryUnbiasedInterruptTime to ensure we are matching the Windows recommendations and properly respecting thread timer resolution changes made by an application to ensure that it maintains an appropriate level of responsiveness.
GetTickCount64 is the historical API and is a fixed resolution (typically 15.5ms) regardless of thread priority or timer resolution
This API includes the time spent in sleep, hibernation, or other low-power states
QueryUnbiasedInterruptTime defaults to the same resolution as GetTickCount64 but updates with the thread prioritization/timer resolution
This API is "unbiased" and so does not include the time spent in sleep, hibernation, or other low-power states
Windows took an explicit breaking change in the Win8/Server2012 timeframe so that its wait APIs no longer include time spent in sleep, hibernation, or other low power states
This includes APIs like Sleep, SleepEx, WaitForMultipleObjectsEx, etc. Basically anything that underpins the timers, waits, delays, or other functionality we support
QueryPerformanceCounter is the highest precision timer in the system and is usable for microsecond accuracy
This comes with significant cost/overhead in querying the result and handling various edges. It can, however, be used to build the others with the appropriate caching or limit in how regularly it is queried
So we want to prefer the existing APIs (i.e. QueryUnbiasedInterruptTime) that specifically have low latency and precision/throughput that matches the thread scheduling resolution when available. If not available, then we may need to consider what guarantees that particular system provides/expects and may need to roll a custom solution based on QueryPerformanceCounter instead. I would expect that the GAMES partition for Windows provides an appropriate API, possibly even one that isn't part of the regular Windows SDK, for use in such scenarios and that it documents in the GDK/XDK.
Sorry for being unclear, without going into specific platform details, I think this issue mainly aims at making it simpler to plug in an alternative implementation downstream when the current API is not present. If we believe there is no good implementation to fallback in upstream when QueryUnbiasedInterruptTime is not available, then downstream implementation would need a way to replace the existing implementation with a custom lowres timer implementation. If that is not acceptable, then downstream will just fix up the needed sources. As far as I'm concerned, no big deal.
It might still have upstream value to re-wire GCToOSInterface::GetLowPrecisionTimeStamp to use minipal lowres implementation.
I think it would be fine to delete GCToOSInterface::GetLowPrecisionTimeStamp/QueryPerformanceFrequency/QueryPerformanceCounter from the GCToOSInterface, and use the equivalent minipal methods. The GC has a direct dependency on minipal on other places, so we would not be introducing anything fundamentally new.
src/libraries/.../System/Environment.Windows.cs
I do not think we want to pay for the extra minipal layer on Windows here. I think we should ifdef it downstream in CoreLib and minipal.
Activity
steveisok commentedon Apr 4, 2026
/cc @lateralusX @jbevain
dotnet-policy-service commentedon Apr 4, 2026
Tagging subscribers to this area: @dotnet/area-system-runtime
See info in area-owners.md if you want to be subscribed.
steveisok commentedon Apr 4, 2026
If this has a place in main, do we treat these as one-offs as they come up? Or do we want to capture all of these around a common check?
tannergooding commentedon Apr 4, 2026
This may need additional consideration as GetTickCount64 is explicitly not high enough precision for certain domains, such as games, and the underlying OS wait APIs were all changed to use a different timer source in the Windows 7-8 timeframe.
What of the other common timer APIs are available in the GAMES partition and can we opt to use those instead if compiling for that target?
lateralusX commentedon Apr 7, 2026
This API is only use for low resolution timing. For high resolution timing there is QueryPerformanceCounter. minpal have two versions, minipal_lowres_ticks and minipal_hires_ticks, all native code should go through these two functions for timing needs.
For managed code, TickCount64 is documented to have low resolution, 10-16ms, so not something useful in any high-performance timing scenarios anyways.
I think its ok to fallback to older GetTickCount64 on systems where the never API is not available and documentation have clearly described this is a low resolution timer for a long time. For high resolution scenarios, use stopwatch or if specific platforms have propriety timing API's, p/invoke into platform specific API's.
I think we should see if gcenv.windows.cpp could use minipal minipal_lowres_ticks, removing the direct call to the Windows API from that source. That would eliminate the first call site in above table.
tannergooding commentedon Apr 7, 2026
Sorry, not sure what exactly you're saying here @lateralusX.
We explicitly switched to
QueryUnbiasedInterruptTimeto ensure we are matching the Windows recommendations and properly respecting thread timer resolution changes made by an application to ensure that it maintains an appropriate level of responsiveness.This is generally covered under https://learn.microsoft.com/en-us/windows/win32/sysinfo/acquiring-high-resolution-time-stamps#guidance-for-acquiring-time-stamps but a few other docs as well. The issues and PRs around the change cover things in more detail.
It can generally be summed as
GetTickCount64is the historical API and is a fixed resolution (typically 15.5ms) regardless of thread priority or timer resolutionQueryUnbiasedInterruptTimedefaults to the same resolution asGetTickCount64but updates with the thread prioritization/timer resolutionSleep,SleepEx,WaitForMultipleObjectsEx, etc. Basically anything that underpins the timers, waits, delays, or other functionality we supportQueryPerformanceCounteris the highest precision timer in the system and is usable for microsecond accuracySo we want to prefer the existing APIs (i.e.
QueryUnbiasedInterruptTime) that specifically have low latency and precision/throughput that matches the thread scheduling resolution when available. If not available, then we may need to consider what guarantees that particular system provides/expects and may need to roll a custom solution based onQueryPerformanceCounterinstead. I would expect that theGAMESpartition for Windows provides an appropriate API, possibly even one that isn't part of the regular Windows SDK, for use in such scenarios and that it documents in the GDK/XDK.lateralusX commentedon Apr 7, 2026
Sorry for being unclear, without going into specific platform details, I think this issue mainly aims at making it simpler to plug in an alternative implementation downstream when the current API is not present. If we believe there is no good implementation to fallback in upstream when
QueryUnbiasedInterruptTimeis not available, then downstream implementation would need a way to replace the existing implementation with a custom lowres timer implementation. If that is not acceptable, then downstream will just fix up the needed sources. As far as I'm concerned, no big deal.It might still have upstream value to re-wire
GCToOSInterface::GetLowPrecisionTimeStampto use minipal lowres implementation.jkotas commentedon Apr 7, 2026
I think it would be fine to delete GCToOSInterface::GetLowPrecisionTimeStamp/QueryPerformanceFrequency/QueryPerformanceCounter from the GCToOSInterface, and use the equivalent minipal methods. The GC has a direct dependency on minipal on other places, so we would not be introducing anything fundamentally new.
I do not think we want to pay for the extra minipal layer on Windows here. I think we should ifdef it downstream in CoreLib and minipal.
Use minipal timers directly in GC (#132197)
steveisok commentedon Aug 14, 2026
@jkotas thanks!