-
Notifications
You must be signed in to change notification settings - Fork 18k
time: macOS monotonic time paused during sleep #66870
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
Comments
related / dup of #35012 ? |
The time value comes from a call to This sounds a bit like libuv/libuv#2891. What version of Darwin are you running? |
14.4.1 I've tested with my friend and found that using simple code as this playground can't reproduce the issue. But this issue can be reproduced using our service code. I'm going to write a simple gin service to try to reproduce this, and print |
Seems to be related to this issue: libuv/libuv#2891 as @ianlancetaylor mentioned I write another simple code for reproducing this: package main
/*
#cgo LDFLAGS: -framework CoreFoundation -framework IOKit
#include <mach/mach_time.h>
uint64_t mach_absolute_time_wrapper() {
return mach_absolute_time();
}
*/
import "C"
import (
"time"
"github.com/gin-gonic/gin"
)
var expiresAt = time.Now().Add(time.Hour * 3)
func machAbsoluteNano() uint64 {
return uint64(C.mach_absolute_time_wrapper())
}
func toDur(nano uint64) time.Duration {
return time.Duration(nano) * time.Nanosecond
}
func main() {
r := gin.Default()
r.GET("/time", handler)
r.Run(":8080")
}
func handler(c *gin.Context) {
now := time.Now()
isBefore := now.Before(expiresAt)
uptime := machAbsoluteNano()
c.JSON(200, gin.H{
"isBefore": isBefore,
"mach_absolute_time()": uptime,
"now": now.String(),
"expiresAt": expiresAt.String(),
"uptimeDuration": toDur(uptime).String(),
})
} output: $ curl localhost:8080/time -s | jq
{
"expiresAt": "2024-04-19 17:14:28.654548 +0800 CST m=+10800.003901671",
"isBefore": true,
"mach_absolute_time()": 76197193801457,
"now": "2024-04-19 14:15:36.137978 +0800 CST m=+67.486527117",
"uptimeDuration": "21h9m57.193801457s"
} I'll check this next morning (about 18~20hrs later) |
Maybe there is no such Although the stopping of a monotonic clock does not destroy its monotonicity, it may introduce bugs in this case. So in macos(or other OS that may stop the monotonic clock), we may using raw To make this reproducing more clearly, I'll modify expiresAt to tomorrow 6 a.m UTC+8 (about 15hrs later), and some data: $ curl localhost:8080/time -s | jq
{
"expiresAt": "2024-04-20 06:00:23.307705 +0800 CST m=+51720.003813063",
"isBefore": true,
"mach_absolute_time()": 80009874232411,
"now": "2024-04-19 15:38:26.38051 +0800 CST m=+3.076554055",
"uptimeDuration": "22h13m29.874232411s"
} Next morning after 8:00 am, I'll fire another curl, and expect that due to monotonic clock stopping during sleep, new |
The key point is, if you are going to sleep 4hrs, the value This issue may impact App written in go, like created by https://github.com/wailsapp/wails |
As mentioned in this issue: #23178, this is an expected behavior.
For my case, I can use Maybe I could add some comment to |
Added some notes. PR here: #66922 |
Change https://go.dev/cl/580515 mentions this issue: |
See #66870 Change-Id: I781265355a3dbd0d9538bc9dcafaa83b482ec3f8 GitHub-Last-Rev: 9d92f11 GitHub-Pull-Request: #66922 Reviewed-on: https://go-review.googlesource.com/c/go/+/580515 Reviewed-by: Ian Lance Taylor <[email protected]> Auto-Submit: Ian Lance Taylor <[email protected]> Reviewed-by: Joedian Reid <[email protected]> LUCI-TryBot-Result: Go LUCI <[email protected]>
Go version
go version go1.22.1 darwin/amd64
Output of
go env
in your module/workspace:What did you do?
The issue happens in both
go run main.go
and vscode debug mode, on my macbook.The code is running as expected on a Linux server.
Maybe this is an OS bug
No
unsafe
code.Two variables:
expiresAt
: atime.Time
variable that stores the token when to expire, created liketime.Now().After(expiresIn)
now
: atime.Time
variable that stores token checking time.Then I use
now.Before(expiresAt)
to decide if I should renew the token.What did you see happen?
now
is afterexpiresAt
, means token already expired, butnow.Before(expiresAt)
returns true. Below is a screenshot in vscode debug console:Both
now
andexpiresAt
are created in the same process, andhasMonotonic
bit is set to 1. So they just compare byext
:time.Time
defines here:go/src/time/time.go
Lines 135 to 156 in d6c972a
Time.Before
defines here:go/src/time/time.go
Lines 265 to 273 in d6c972a
From the screenshot we can see there is a monotonic clock rollback issue, the
m
printed is not as expected.now
is afterexpiresAt
, but itsm
is smaller. This is a breakpoint hit in debug mode(after I send an API call), two variables surely created in the same process.The issue is not always happen, unless I close the screen and a night passed.
Seems that while the computer sleep and awake, it will reset the monotonic clock to zero.
I wrote some code to reproduce quickly, but failed. The monotonic clock only stopped, but not reset, while the computer sleep for two hours when I go out for a launch.
"Monotonic clock may stop" is mentioned here in doc monotonic clock, but seems like it won't rollback (if it can, it's not monotonic)
The failed reproduce code is written as here in playground
And the output:

We can see the monotonic clock is only just stopped, but not reset (no rollback). I'll keep testing to let it pass a whole night to see if it could be reproduced.
Besides, more info:
The value of monotonic clock (stored as
Time.ext
) eventually comes from here [nanotime1
] on mac os:go/src/runtime/sys_darwin.go
Lines 332 to 350 in d6c972a
What did you expect to see?
I expect to see
now.Before(expiresAt)
should return false, then token can renew as expected.The text was updated successfully, but these errors were encountered: