mirror of https://github.com/golang/go.git
time: gracefully handle ts.zombies underflow
The current implementation sets t.ts before adding t to ts; that can cause inconsistencies with temporarily negative ts.zombies values. Handle them gracefully, since we only care about detecting very positive values. Pending CL 564977 removes the race that sets t.ts early, and then CL 569996 builds on top of that to make the count precise. This CL just gets examples like the new test working sooner. Change-Id: Ibe1aecc2554f83436f761f48e4050bd962982e4f Reviewed-on: https://go-review.googlesource.com/c/go/+/569995 Reviewed-by: Austin Clements <austin@google.com> LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
This commit is contained in:
parent
e03e06d560
commit
e38be310a4
|
|
@ -66,7 +66,7 @@ type timers struct {
|
|||
len atomic.Uint32
|
||||
|
||||
// zombies is the number of deleted timers left in heap.
|
||||
zombies atomic.Uint32
|
||||
zombies atomic.Int32
|
||||
|
||||
// raceCtx is the race context used while executing timer functions.
|
||||
raceCtx uintptr
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ package time_test
|
|||
import (
|
||||
"fmt"
|
||||
"runtime"
|
||||
"sync"
|
||||
"testing"
|
||||
. "time"
|
||||
)
|
||||
|
|
@ -145,6 +146,83 @@ func TestTickerResetLtZeroDuration(t *testing.T) {
|
|||
tk.Reset(0)
|
||||
}
|
||||
|
||||
func TestLongAdjustTimers(t *testing.T) {
|
||||
t.Parallel()
|
||||
var wg sync.WaitGroup
|
||||
defer wg.Wait()
|
||||
|
||||
// Build up the timer heap.
|
||||
const count = 5000
|
||||
wg.Add(count)
|
||||
for range count {
|
||||
go func() {
|
||||
defer wg.Done()
|
||||
Sleep(10 * Microsecond)
|
||||
}()
|
||||
}
|
||||
for range count {
|
||||
Sleep(1 * Microsecond)
|
||||
}
|
||||
|
||||
// Give ourselves 60 seconds to complete.
|
||||
// This used to reliably fail on a Mac M3 laptop,
|
||||
// which needed 77 seconds.
|
||||
// Trybots are slower, so it will fail even more reliably there.
|
||||
// With the fix, the code runs in under a second.
|
||||
done := make(chan bool)
|
||||
AfterFunc(60*Second, func() { close(done) })
|
||||
|
||||
// Set up a queing goroutine to ping pong through the scheduler.
|
||||
inQ := make(chan func())
|
||||
outQ := make(chan func())
|
||||
|
||||
defer close(inQ)
|
||||
|
||||
wg.Add(1)
|
||||
go func() {
|
||||
defer wg.Done()
|
||||
defer close(outQ)
|
||||
var q []func()
|
||||
for {
|
||||
var sendTo chan func()
|
||||
var send func()
|
||||
if len(q) > 0 {
|
||||
sendTo = outQ
|
||||
send = q[0]
|
||||
}
|
||||
select {
|
||||
case sendTo <- send:
|
||||
q = q[1:]
|
||||
case f, ok := <-inQ:
|
||||
if !ok {
|
||||
return
|
||||
}
|
||||
q = append(q, f)
|
||||
case <-done:
|
||||
return
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
for i := range 50000 {
|
||||
const try = 20
|
||||
for range try {
|
||||
inQ <- func() {}
|
||||
}
|
||||
for range try {
|
||||
select {
|
||||
case _, ok := <-outQ:
|
||||
if !ok {
|
||||
t.Fatal("output channel is closed")
|
||||
}
|
||||
case <-After(5 * Second):
|
||||
t.Fatalf("failed to read work, iteration %d", i)
|
||||
case <-done:
|
||||
t.Fatal("timer expired")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
func BenchmarkTicker(b *testing.B) {
|
||||
benchmark(b, func(n int) {
|
||||
ticker := NewTicker(Nanosecond)
|
||||
|
|
|
|||
Loading…
Reference in New Issue