Skip to content

Add build-time check for QueryUnbiasedInterruptTime availability #126551

Closed

Description

@steveisok

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.

Activity

  1. steveisok commented on Apr 4, 2026

    @steveisok
    MemberAuthor
  2. dotnet-policy-service commented on Apr 4, 2026

    @dotnet-policy-service
    Contributor

    Tagging subscribers to this area: @dotnet/area-system-runtime
    See info in area-owners.md if you want to be subscribed.

  3. steveisok commented on Apr 4, 2026

    @steveisok
    MemberAuthor

    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?

  4. tannergooding commented on Apr 4, 2026

    @tannergooding
    Member

    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?

  5. lateralusX commented on Apr 7, 2026

    @lateralusX
    Member

    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.

  6. tannergooding commented on Apr 7, 2026

    @tannergooding
    Member

    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.

    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

    • 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.

  7. lateralusX commented on Apr 7, 2026

    @lateralusX
    Member

    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.

    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

    • 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.

  8. jkotas commented on Apr 7, 2026

    @jkotas
    Member

    src/coreclr/gc/windows/gcenv.windows.cpp

    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.

  9. removed
    untriagedNew issue has not been triaged by the area owner
    on Aug 11, 2026
  10. added this to the Future milestone on Aug 11, 2026
  11. linked a pull request that will close this issueUse minipal timers directly in GC #132197on Aug 12, 2026
  12. steveisok commented on Aug 14, 2026

    @steveisok
    MemberAuthor

    @jkotas thanks!

  13. locked and limited conversation to collaborators on Sep 14, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Type

No type

Projects

No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions