I have a test written in Go:
func TestThings(t *testing.T) {
tCh := make(chan int64, 10000)
ctx, cx := context.WithTimeout(context.Background(), 20*time.Second)
defer cx()
wg := sync.WaitGroup{}
wg.Add(2)
go func(c context.Context) {
defer wg.Done()
defer close(tCh)
for {
select {
case <-c.Done():
return
default:
nt := time.Now()
tCh <- nt.UnixNano()
}
}
}(ctx)
go func(c context.Context) {
defer wg.Done()
for {
select {
case <-c.Done():
return
default:
v := <-tCh
nt := time.Now()
res := nt.UnixNano() - v
if res < 0 {
t.Errorf("got less than 0; diff: %d now: %d then: %d", res, nt.UnixNano(), v)
}
}
}
}(ctx)
wg.Wait()
}
Running this test (sometimes) ends with the result:
go test -v -run TestThings ./test
=== RUN TestThings
test.go:48: got less than 0; diff: -33686100 now: 1639183246323013700 then: 1639183246356699800
test.go:48: got less than 0; diff: -33535000 now: 1639183246323171300 then: 1639183246356706300
test.go:48: got less than 0; diff: -33490200 now: 1639183246323222600 then: 1639183246356712800
test.go:48: got less than 0; diff: -33488300 now: 1639183246323231000 then: 1639183246356719300
test.go:48: got less than 0; diff: -33502600 now: 1639183246323241000 then: 1639183246356743600
test.go:48: got less than 0; diff: -33551600 now: 1639183246323249100 then: 1639183246356800700
...
--- FAIL: TestThings (20.01s)
FAIL
FAIL test 20.022s
FAIL
Why are the times in the receiving Goroutine sometimes before the times in the sending Goroutine?
I understand that clocks are generally hard and unreliable when it comes to computers, but I would expect since these Goroutines are (maybe?) in the same process, but definitely on the same host, that calls to time.Now()
would return ever increasing values. Or at least not "earlier" ones.
Update:
Something else possibly worth mentioning: I'm running this inside a VSCode devcontainer on a Macbook Pro.
CodePudding user response:
As commented by @Peter on the original question referencing the time package docs, serialization of time.Time
loses any monotonic information by design:
Because the monotonic clock reading has no meaning outside the current process, the serialized forms generated by
t.GobEncode
,t.MarshalBinary
,t.MarshalJSON
, andt.MarshalText
omit the monotonic clock reading, andt.Format
provides no format for it. Similarly, the constructorstime.Date
,time.Parse
,time.ParseInLocation
, andtime.Unix
, as well as the unmarshalerst.GobDecode
,t.UnmarshalBinary
.t.UnmarshalJSON
, andt.UnmarshalText
always create times with no monotonic clock reading.
Changing the code snippet from the original question to pass time.Time
values between the Goroutines works without issue.