diff options
| author | Bryan C. Mills <bcmills@google.com> | 2024-01-19 17:10:21 -0500 |
|---|---|---|
| committer | Gopher Robot <gobot@golang.org> | 2024-01-22 16:16:08 +0000 |
| commit | d3f713bbd11ce9da507b21968179012b62d1a52c (patch) | |
| tree | 3c1df15339b0b5e3f1d07193c9f49e90d9885f95 | |
| parent | b17bf6dde52446a3eebbf667735367566288b97f (diff) | |
| download | go-d3f713bbd11ce9da507b21968179012b62d1a52c.tar.xz | |
time: add a regression test for time.AfterFunc goroutine starvation
The test is skipped on wasm platforms for now, because it
successfully detects a starvation bug on those platforms.
For #65178.
Change-Id: I05d28f1c7be99fcab67ec4dfaa38f412e11fd3cb
Reviewed-on: https://go-review.googlesource.com/c/go/+/557038
Auto-Submit: Bryan Mills <bcmills@google.com>
LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com>
Reviewed-by: Damien Neil <dneil@google.com>
| -rw-r--r-- | src/time/sleep_test.go | 44 |
1 files changed, 44 insertions, 0 deletions
diff --git a/src/time/sleep_test.go b/src/time/sleep_test.go index 2f791240f9..b25606dfed 100644 --- a/src/time/sleep_test.go +++ b/src/time/sleep_test.go @@ -83,6 +83,50 @@ func TestAfterStress(t *testing.T) { stop.Store(true) } +func TestAfterFuncStarvation(t *testing.T) { + // Start two goroutines ping-ponging on a channel send. + // At any given time, at least one of these goroutines is runnable: + // if the channel buffer is full, the receiver is runnable, + // and if it is not full, the sender is runnable. + // + // In addition, the AfterFunc callback should become runnable after + // the indicated delay. + // + // Even if GOMAXPROCS=1, we expect the runtime to eventually schedule + // the AfterFunc goroutine instead of the runnable channel goroutine. + // However, in https://go.dev/issue/65178 this was observed to live-lock + // on wasip1/wasm and js/wasm after <10000 runs. + + if runtime.GOARCH == "wasm" { + testenv.SkipFlaky(t, 65178) + } + + defer runtime.GOMAXPROCS(runtime.GOMAXPROCS(1)) + + var ( + wg sync.WaitGroup + stop atomic.Bool + c = make(chan bool, 1) + ) + + wg.Add(2) + go func() { + for !stop.Load() { + c <- true + } + close(c) + wg.Done() + }() + go func() { + for range c { + } + wg.Done() + }() + + AfterFunc(1*Microsecond, func() { stop.Store(true) }) + wg.Wait() +} + func benchmark(b *testing.B, bench func(n int)) { // Create equal number of garbage timers on each P before starting |
