dotnet / runtime

.NET is a cross-platform runtime for cloud, mobile, desktop, and IoT apps.
https://docs.microsoft.com/dotnet/core/
MIT License
14.62k stars 4.56k forks source link

Task.Delay sometimes returns sooner than delay #45585

Open shravan2x opened 3 years ago

shravan2x commented 3 years ago

Description

I have a program that runs a set task every x seconds. After the delay, a verification check is performed that at least x seconds have passed. However, it appears that sometimes the Task.Delay method returns sooner than x. I tested this using code similar to the snippet below. The amount it's off by is usually small, around 1-4ms rarely 5ms.

Stopwatch sw = Stopwatch.StartNew();
await Task.Delay(delay, cancellationToken);
if (sw.ElapsedMilliseconds < delay)
    Console.WriteLine("delay wrong " + (delay - sw.ElapsedMilliseconds));

To be clear, I have no issue with the method taking more time than I schedule it for, as I know there will always be overhead due to context switching etc. However, I don't think the right behavior for this method is to ever return sooner than the set time.

Configuration

ghost commented 3 years ago

Tagging subscribers to this area: @tarekgh See info in area-owners.md if you want to be subscribed.

Issue Details
### Description I have a program that runs a set task every x seconds. After the delay, a verification check is performed that _at least_ x seconds have passed. However, it appears that sometimes the `Task.Delay` method returns sooner than x. I tested this using code similar to the snippet below. The amount it's off by is usually small, around 1-4ms rarely 5ms. ``` Stopwatch sw = Stopwatch.StartNew(); await Task.Delay(delay, cancellationToken); if (sw.ElapsedMilliseconds < delay) Console.WriteLine("delay wrong " + (delay - sw.ElapsedMilliseconds)); ``` To be clear, I have no issue with the method taking _more_ time than I schedule it for, as I know there will always be overhead due to context switching etc. However, I don't think the right behavior for this method is to ever return _sooner_ than the set time. ### Configuration * Which version of .NET is the code running on? .NET Core 3.1 * What OS and version, and what distro if applicable? Ubuntu 18.04 * What is the architecture (x64, x86, ARM, ARM64)? x64 * Do you know whether it is specific to that configuration? No
Author: shravan2x
Assignees: -
Labels: `area-System.Threading.Tasks`, `untriaged`
Milestone: -
tarekgh commented 3 years ago

CC @stephentoub

stephentoub commented 3 years ago

This is extremely unlikely to be specific to Task.Delay, rather Timer. And Timer uses Environment.TickCount64 rather than Stopwatch, with the former not having the same precision as the latter (TickCount's precision is ~15ms on Windows). I expect if you tested with TickCount instead you wouldn't see such a discrepancy.

shravan2x commented 3 years ago

@stephentoub Oh interesting, though this was tested on Linux. Do you know what the precision there is?

And if we know such a precision loss is possible, could our use of Timer be updated to always round up to the next 15ms mark?