Skip to content

[Breaking change]: Environment.TickCount and Environment.TickCount64 made consistent with underlying OS timeout behavior #50755

@tannergooding

Description

@tannergooding

Description

On Windows, Environment.TickCount and Environment.TickCount64 were brought inline with the behavior seen in the underlying wait APIs for the OS to no longer include sleep or hibernation time as part of the elapsed time measured. This also makes it consistent with the behavior seen on other platforms and ensures it updates at the same frequency as the underlying interrupt timer for the system allowing for higher responsiveness in apps that opted in to higher frequency updates.

Version

.NET 11 Preview 1

Previous behavior

On all platforms, Environment.TickCount simply returns the truncated result of Environment.TickCount64 and so sees identical behavior, but is subject to overflow approximately every 49 days.

On Windows, Environment.TickCount64 simply returned the result of the Win32 GetTickCount64 which updates at a fixed cadence of 10-16ms (typically 15.5ms) and included the time the system spent in sleep, hibernation, or other low-power states.

On other platforms (such as Linux and MacOS), Environment.TickCount64 updated at the same frequency as the underlying interrupt timer for the system and only includes the time the system is considered "awake".

New behavior

On all platforms, Environment.TickCount maintains its implementation and so mirrors the behavior of Environment.TickCount64.

On Windows, Environment.TickCount64 was updated to return the result of the Win32 QueryUnbiasedInterruptTime API. This brings it inline with the behavior used in the underlying wait APIs for the OS to no longer include non-awake time and update at the same frequency as the underlying interrupt timer for the system.

On other platforms, Environment.TickCount64 retains its behavior, which is inline with the new behavior on Windows.

Type of breaking change

  • Binary incompatible: Existing binaries might encounter a breaking change in behavior, such as failure to load or execute, and if so, require recompilation.
  • Source incompatible: When recompiled using the new SDK or component or to target the new runtime, existing source code might require source changes to compile successfully.
  • Behavioral change: Existing binaries might behave differently at run time.

Reason for change

Windows took a similar behavior breaking change in Win8/Server2012 and newer such that APIs which took a timeout (like SleepEx or WaitForMultipleObjectsEx) would no longer factor in non-awake time. This caused an inconsistency with .NET as such wait APIs are frequently used in conjunction with Environment.TickCount64 leading to hard to diagnose bugs such as timers firing unexpectedly.

Additionally, the underlying API used, GetTickCount64, was a less precise, only updating at a fixed resolution. This resolution was not adjusted if the underlying interrupt timer for the OS had its frequency changed which could lead to additional work being done for apps that had opted to run at a higher priority. The behavior was then also inconsistent with the behavior seen on other platforms such as MacOS and Linux.

The break therefore ensures consistency with the underlying OS and across platforms. It can also lead to higher responsiveness in apps that have opted into more frequent updates.

Recommended action

Most code should not experience any change in behavior as they will not have opted into higher frequency interrupt times, so they will continue seeing updates at the same frequency as before. However, if update frequency is relevant, users should ensure that their timeouts are passing in a correct value that meets the expectations of their code or ensure that the application is not opting into too high of a update frequency (this can only be done via P/Invoke APIs today).

Some code may see timers no longer fire immediately after a machine wakes from a sleeping or low-power state. If such time is relevant, the developer should utilize APIs such as DateTime.UtcNow to ensure such time can always be included. Such code may have to account for potential clock adjustments.

As a reminder, the behavior here is only changing on Windows and is now inline with the behavior seen on other platforms. Code that finds itself impacted by this on Windows is therefore likely already impacted by the same scenario on other platforms such as Linux and MacOS.

Feature area

Core .NET libraries

Affected APIs

System.Environment.TickCount
System.Environment.TickCount64

Metadata

Metadata

Assignees

Labels

Type

No type

Projects

Status

🔖 Ready

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions