-
Notifications
You must be signed in to change notification settings - Fork 4.7k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add a monotonic clock to the framework #15207
Comments
Just responding to this one point, Stopwatch provides the static GetTimestamp() method, so if the timing support provided by Stopwatch is what you want, you can get at the same mechanism without instantiating a Stopwatch instance. |
How would these work? If these function existed, it'd mean that |
I can imagine this implementation (minus synchronization): struct MonotonicTime { |
@CodesInChaos true, I have edited those away. |
@KrzysztofCwalina that's not good enough because it can cause monotonic time to stand still for arbitrary periods of wall-clock time. Monotonic time must advance with the wall clock. That's the point of timeouts. Make users and other systems wait x seconds on the clock. I think |
@GSPP, I thought monotonic means never decreasing. Also, I am not sure what the semantics/implementation could be if you wanted to always increasing. Given the system clock is lower resolution than processor clock, it's not clear how it would be implemented. |
@KrzysztofCwalina The way I understand it, monotonic does mean never decreasing, but that's not the only requirement. It's okay for the value to not increase for, say, 366 ns (that's what the resolution of Also, your implementation is not really monotonic (at least not with this interface). Consider something like: var dt1 = DateTime.UtcNow;
Thread.Sleep(aWhile);
var dt2 = DateTime.UtcNow;
Thread.Sleep(someMore);
var dt3 = DateTime.UtcNow;
var monotonic2 = MonotonicTime.ForDateTime(dt2);
var monotonic3 = MonotonicTime.ForDateTime(dt3);
var monotonic1 = MonotonicTime.ForDateTime(dt1); Here, |
@svick correct, it's supposed to be like |
@KrzysztofCwalina I think Stopwatch timestamps should work. According to search engines they even continue advancing when the system is hibernated. |
@GSPP, ah, ok. I got confused. I thought you did not want to use Stopwatch/QueryPerformanceCounter for so reason. |
One note is that |
@scalablecory good point but maybe there are no supported platforms anymore where that fallback is necessary. If this is rare enough one could even make the monotonic time struct throw in case good stopwatch support is not there. The kernel team should know some stats about high frequency counter support. |
Just stumbled across this issue and I'll add:
WRT the original scenarios mentioned, many are indeed just fine with |
But The idea of a monotonic clock does not include providing "legible" time as This ticket is not about high precision so much. It's about a clock source that cannot jump arbitrarily like the system clock. |
Re: .NET does already expose all the monotonic clocks most would ever need, between |
The API is new to Windows 8. It did not exist back then. I have created a ticket to split off that discussion. It has nothing to do with monotonic time.
|
A stricly increasing (e.g. non-decreasing) clock I know of (and that is, 'as a bonus'?, high precision) is QueryPerformanceCounter.
It can 'leap' forward* (in some circumstances, AFAIK, correct me if I'm wrong) but never back. I'm not sure I would rely on the * There used to be a KB article (KB274323) about this (see archive.org) but it, currently, redirects to Acquiring high-resolution time stamps |
@GSPP proper long-term use of Of course a I think ideally |
Right, Stopwatch should just fall back to the 64 bit counter. This is another change request.
To nitpick, your code is not guaranteed to be scheduled once every 30 days. It could be a suspended VM or a sleeping physical machine. Since the system clock can change solutions based on detecting wrapping that way also fail. To summarize:
|
It is worth noting that I submitted a bug to MS ~6 years ago and was denied with "wont fix" regarding |
@RobThree - My understanding is that the issues described in KB274323, and in this blog post relating to QPC being unreliable are no longer accurate because 1) The hardware that is known to exhibit this bug is very old and no longer in common usage, and 2) There were some workarounds added to detect this hardware bug and use an alternate timing source. See the notes under TSC Register at the bottom of the Acquiring hi-res timestamps article. |
@GSPP - Agreed with 1, 2 & 4. Not sure what else is necessary for 3 that Stopwatch doesn't provide. 5 we'll talk about in the other item. 😄 |
An additional benefit of a monotonic clock could be correct handling of leap seconds, which |
Computers/Programs have a bad time with leap seconds in general. A monotonic clock doesn't actually handle leap seconds so much as it wouldn't notice there was anything different. If you tried to translate it back to a readable date/time value, you'd still get translation errors (unless there was some sort of mapping). |
Exactly! The proposed API by @GSPP doesn't include a conversion to/from DateTime and so the type system will prevent you from doing just that. So comparing two instances of |
Heh.
Not quite sure you're going with this. Ignoring NTP syncing problems, OS/runtime clocks do any or all of the following:
|
@KrzysztofCwalina what are the next steps here? Are we waiting for a formal API proposal? |
@mj1856 in my mind there is no relation to the wall clock at all. This class would never be exposed to the user. Any "debug" formatting would suffice. Strawman: "12345678 ticks (likely 2017-01-23 18:32:12.345 local time)". This does have a relationship to the wallclock but it's only for the developers convenience. I don't see the need to convert to wallclock time except as a debugging aid. This is conceptually impossible. It is possible to approximately convert to wallclock time by storing the current tick count and current DateTime.UtcNow in a static field before the first instance is created (in the cctor). That way an UTC date can be obtained relative to the time the cctor ran. Again, this is a debugging aid. The method could be called I think this would indeed just be a value-type wrapper around Stopwatch ticks, yes. This is a convenience API for a few very common scenarios outlined in the opening post. Regarding |
@GSPP - Oh, so you're just proposing a more strongly typed tick? I suppose that would help with people forgetting to take frequency into account with StopWatch ticks, but other than that, is it just an ease of use issue? I mean, we do have |
@mj1856 yes, strongly typed ticks to improve usability to the point where people actually do this instead of hacking it with |
I agree with an earlier post that said I don't think we need to worry about the case where a misconfiguration sends the system clock forward or backward by years. That would represent a system-wide misconfiguration and would hopefully be caught very quickly. And the recovery procedure is simple enough: fix the clock and reboot the OS. IMO this API is niche enough where it doesn't need to be in-box. Anybody who consumes it probably has their own opinionated beliefs regarding how it should behave. |
Not necessarily arguing against the niche, but reducing this to the system administrator and a "just reboot" attitude is a bit short sighted. I'm coming over from dotnet/aspnetcore#13628 where an embedded hardware has an unreliable system clock due to design limitations. Changing it manually or automatically on the order of O(months) is not uncommon and results in unexpected error conditions due to the usage of If the monotonic clock API is too niche, I would argue that having a dedicated API for timeouts (based on a monotonic time source) is a valuable addition to the framework. Instead of pointing users of |
Here are two alternative designs:
I'm personally not convinced that these should be added to the framework. I'm logging the ideas here to collect them. |
Strictly speaking, a date/time instant can't be elapsed, because it's not a duration. You can have a "target" instant (the estimated instant the event will fire), and you can have the instant it actually fired; these are, however, not guaranteed to be the same value. Which of them did you mean? Things get even crazier in the (admittedly rare) case of the computer clock being manually adjusted for whatever reason (kernels tend to be better behaved about the adjustments they perform on the clocks). For this reason, low-level timeout functionality should almost never make reference to the "current" ("real world") time, but should instead be based and report solely on elapsed process/system time. |
I meant the elapse target. The elapse target could change measured in the currently configured PC clock time. It's meant as a supplementary property. Not sure about the use case, I just added it as an idea. This proposal was done rather quickly... The purpose of a timeout usually is to elapse after a certain absolute wall-clock duration has elapsed. "We have 5 seconds to make this HTTP call or else the user is going to be delayed for too long.". This type should encapsulate such semantics. |
Right, then I recommend removing it. Referencing wall-clock time is more the domain of a scheduling process, which is a way higher level concern. |
How often do your production systems adjust their system clocks so significantly that apps require special handling of this condition? Edit: this question is in relation to exposing new API. Let's assume for now that we can make Timer and other types more resilient to system clock changes without exposing any new API surface. |
Strictly speaking, every piece of hardware will have the clock significantly updated at least once - usually during install, when the clock is first set. In most cases this is going to be the only time such an update happens (barring failure of the clock battery when powered off). Updates from NTP and similar services are usually better behaved, specifically to avoid some of these problems. Changing the time zone of the system does not update the clock (or should not, in a modern OS). QPC and similar system (or process) time clocks should be immune to time changes, because they measure the relative ticks since a start, and aren't based around an external absolute instant (ie, UTC). Outside of some oddball situations for fallbacks, then, The question, then, is more whether we want a system/process class Environment
{
public static TimeSpan TimeSinceProcessStart { get; }
} |
Just to note on the frequency. That is actually happening quite often. It used to be that we would see clock drift that exceeded 15 minutes over a period of a few months (which caused sync issues with APIs). If you go to https://time.gov/, you can see your system's offset from the official time. My laptop is off by +0.040 s, for example. |
Adjusting the clock is a rare event. But when it happens we don't want random malfunctions. OK, I don't want to overstate the significance of this problem. But it's the kind of totally unexpected bug that takes down, say, Azure in the middle of the night. It would be nice to have a correct-by-default solution that is clean and convenient. (I updated the |
These are all instances of absolute time differences. Although normally I would have expected things like host migration to be "invisible", because I would have expected the host clock to be correct due to management requirements. Clock drift over months tells me there's a configuration issue, because NTP updates way more often than that by default. Also, that's a spectacularly bad accuracy - your common wristwatch is something like +-30 seconds a month (and at 15 minutes off, most OSs will start squawking at you for SSL connections).
@GSPP - in what way is the existing static Timeout CreateFromDeadline(DateTime elapseTargetDateTimeUtc); ... because it's going to be based on a lie (that the timeout is actually based on absolute time). |
Closing this as there isn't currently anything actionable. .NET provides multiple times and time sources. Dealing with time, is and always will be complicated and there is no "foolproof" solution. If you want a monotonic clock then |
Problem: Many BCL components use
DateTime.UtcNow
to compute timeouts and time deltas. When you run a "Find Usages" onDateTime.UtcNow
in .NET Framework assemblies a lot of usage sites come to light. For example, in ADO.NET, transactions, caches, WebRequest, remoting, WCF, SignalR, security.All of these usages are bugs because the current system time can change significantly forwards and backwards at any time. This means that in case the system clock is changed many .NET components will spuriously fail.
Common symptoms would be timeouts firing to early or too late (possibly never). Perf counters also might show too high or negative values.
Sample scenario:
Then, the cache item will essentially stay around forever if eviction is based on
DateTime.UtcNow
. I believe this bug exists in the BCL.User code also has the same problems.
Solution: Add some kind of monotonic clock to the BCL. The main property of such a clock would be that its time advances linearly. It should never jump when the system clock changes. It should not exhibit split second jumps. It should behave reasonably in case the system sleeps or hibernates or in case the VM is paused.
This monotonic clock class should be fast and convenient to use so that it is a no-brainer to switch all
DateTime.UtcNow
usages over to the new model.Maybe we can add
Environment.TickCount64
as well.Environment.TickCount64
alone would not be good enough because it is awkward to use.I'm not sure if
Stopwatch
would cover all these requirements. It is a reference type which might be too heavy for hot code path in the BCL.Maybe we can add a new value type that is made exactly for this purpose. A sketch:
To summarize, I request:
Environment.TickCount64
.The text was updated successfully, but these errors were encountered: