-
Notifications
You must be signed in to change notification settings - Fork 17.8k
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
runtime: use CLOCK_BOOTIME, not CLOCK_MONOTONIC, when possible #24595
Comments
Define "correct". I am not convinced. |
Sorry. That was too short. I think it's plausible that some people will want to exclude the time when the computer is asleep. I will also note that - I believe - the monotonic time always matches runtime.nanotime, and I'm not sure the runtime timers necessarily want to include the time when the computer is asleep. Or imagine calling time.Ticker(1*time.Hour) to do some kind of cleanup once per hour. If the computer was off for 50 minutes, do you want the tick after 10 minutes of program execution or not? I'm not sure you do. I can also imagine programs depending on one or the other behavior. If all current systems agree that monotonic time does not include time the computer is asleep, then I'm not sure we should introduce a Linux-specific variation. (I'm assuming - maybe incorrectly - that other systems typically don't support this.) My point is only that I don't think it's a slam dunk we should definitely do this. I'd like to know more about what other systems do. For example, do Python and Java's monotonic time measurements include time spent asleep? How many operating systems provide access to time spent asleep? |
There is an interesting comparison to be made with suspending a single Go process ( Processes can't control whether they experience some form of cpu starvation, either via system suspend, process suspend, or overload. They probably need to handle events occuring without much processing time to be reliable. Eg, a process doing an hourly cleanup needs to handle the cleanup occurring "too soon" otherwise it should track against amount processed, not real time. It is surprising that |
CLOCK_MONOTONIC will behave the same as CLOCK_BOOTTIME as of Linux 4.17 (advancing time while suspended). Details here. This may be a good argument to use CLOCK_BOOTTIME for consistency between kernels. |
If I understand correctly, this is also a change in behaviour compared to the previous implementation when |
Oh, I just opened a duplicate of this. Yes, please make this change. Without it, implementing certain network protocols reliably for Android devices is really miserable. |
Change https://golang.org/cl/111356 mentions this issue: |
https://github.com/golang/go/wiki/MinimumRequirements#linux says we currently require Kernel version 2.6.23. Would this require bumping our minimum kernel version? Or would we use this conditionally at runtime? |
BOOTTIME requires 2.6.39 indeed. I just checked old kernels, and fortunately clock_gettime returns -EINVAL if you pass it too new of a value, which means you could easily try BOOTTIME and fall back to MONOTONIC, if you're on a kernel that doesn't support it. Or you could just raise the minimum kernel requirement. Either way is fine with me. (It's worth noting that the oldest kernel supported by kernel.org is 3.2.) |
Note that the change to make CLOCK_MONOTONIC behave like BOOTTIME in Linux 4.17 got reverted quickly due to causing compatibility issues. It won't happen, at least not for a very long time, if ever. https://www.spinics.net/lists/linux-tip-commits/msg43709.html |
Bummer, though still not a reason for us to inherit Linux's legacy problems. |
Agreed. It's just that the same kind of issue (like the systemd watchdog timeouts triggering on resume) may very well lurk in Go programs, too. It's probably worth a try, reverting the change before release if things blow up. |
Welp, this is unfortunate, but I'm doing this now downstream: https://git.zx2c4.com/wireguard-android/commit/?id=7f8799a4d44058d2fe0981841b8b6d825f97aee7 (The good news is that it actually works pretty well.) |
FWIW, macOS added mach_continuous_time in macOS 10.12. It is like mach_absolute_time but advances during sleep. |
Tagging this NeedsDecision as the CL is blocked on @rsc approving. While I see the risk of having the same timeout issues the Linux kernel had, I think our case is different because it’s exposed through an interface that normally refers to real time. The best argument I’ve seen for this change is that without it time.Since can return very surprising results. |
It also makes stateful network protocols extremely difficult to implement without this, particularly cryptographic ones, where cleaning up old keys is important. |
OK, feel free to try this early in Go 1.12. |
Okay. I'll set a reminder for myself to do this when the 1.12 tree opens in August. |
This has now been submitted at https://go-review.googlesource.com/c/go/+/111356 for merging. |
It's too late in the cycle for this to land in 1.14 (trusting the |
Indeed it's too late, and this actually requires some other changes too: we have to use futex and semaphores with sleep that corresponds with BOOTTIME. |
Thanks for the update, @zx2c4 :) |
Maybe this calls for a new stdlib API, since changing the way time.Timer works isn't workable for all callers. How about |
I believe that most systems are moving toward incrementing monotonic time while the system is asleep. As @mpx notes above, the Linux kernel is making I'm not convinced that we need two different kinds of timers. |
I'm not convinced either. One is fine. And we should simply make them all function as CLOCK_BOOTTIME. Windows is already there. The other platforms will require a bit of poking and prodding. I'll see if I can find some time to have another try at it soon, since I'm shipping a patched Go runtime anyway for this. |
The Linux kernel change was apparently abandoned; #24595 (comment) I don't understand why you'd eliminate the absolute timer. I use timers in If Go timers don't work the same on Windows as other platforms, that's a likely source of porting problems, and it's not documented, yikes. |
This is just one use case, there are plenty with different behavior needed. Personally, I think it's not intuitive that we use a clock that's not advanced when the computer sleeps. IMHO, the computer sleeping throughout execution is a detail that should be transparent to you, otherwise you can't trust your timers. Given this code:
if after The end result is that timers are no longer predictable. If I set it to fire in 10 mins, will it? who knows, maybe, maybe it'll fire in 15 mins, maybe 20. Using CLOCK_MONOTIC means time stops (when computer sleeps), I think it's safer to say that the more intuitive use case is one in which time doesn't stop. |
@gabzim you missed my suggestion for |
Program necessarily receive control some time after the delay specified, ideally not too long afterwards. However, there are many reasons why a program may not get control "quickly":
The best we can hope for is that the OS/runtime doesn't unnecessarily delay providing control to the program. Programs need to be written to cope with this uncertainly and can't assume some amount of processing has occurred in-between timers firing, otherwise they are likely buggy. In this case, I don't think we need a new timer, we just need the runtime to provide control as close to wallclock duration as is practical. At least, that allows the program to delay further if it is more appropriate. |
@as has suggested the API A variation of that is Either would provide the functionality requested by this issue, and both are preferable to my previous suggestion of |
The Windows runtime was changed in 1.13.3 to the boot-time model, without reference to this discussion. Microsoft has asked us to revert that. See #35482 @rsc @ianlancetaylor there is clearly a need for a boot-time timer; would the Go team look favorably on a proposal for |
The arguments behind selecting
If This isn't perfect in all situations, there are advantages/disadvantages in both directions. With With |
Quoting @jstarks from #35447 (comment): The Windows kernel team changed timer behavior in Windows 8 to stop advancing relative timeouts on wake. Otherwise when you open your laptop lid, every timer in the system goes off all at once and you get a bunch of unpredictable errors. Software is generally written to assume that local processes will make forward progress over reasonable time periods, and if they don't then something is wrong. When the machine is asleep, this assumption is violated. By making relative timers behave like threads, so that they both run together or they both don't, the illusion is maintained. You can claim these programs are buggy, but they obviously exist. Watchdog timers are well-known constructs. This was a conscious design decision in Windows, and so it's disappointing to see the Go runtime second guess this several years later in a bug fix. [see #34130] As far as behavior on Linux, there is clearly no consensus in issue #24595, which discusses this same problem. And indeed you can see that the CLOCK_MONOTONIC/CLOCK_BOOTTIME convergence was reverted in the kernel exactly because of the reason we stopped advancing time in Windows: random code has random failures due to timeouts. See https://lkml.org/lkml/2018/4/26/929 for a brief justification. |
Re NewTimerAt() and related ideas, see #35482 (comment) |
Change https://golang.org/cl/211307 mentions this issue: |
On GNU/Linux since 2.6.39
clock_gettime
supportsCLOCK_BOOTTIME
, which is equivalent toCLOCK_MONOTONIC
except that it also accounts for time when the computer is asleep. We should preferCLOCK_BOOTTIME
toCLOCK_MONOTONIC
when it is available. That will permittime.Time.Sub
to return the correct value across periods when the computer has gone to sleep.See #23178.
The text was updated successfully, but these errors were encountered: