aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBryan C. Mills <bcmills@google.com>2024-01-19 17:10:21 -0500
committerGopher Robot <gobot@golang.org>2024-01-22 16:16:08 +0000
commitd3f713bbd11ce9da507b21968179012b62d1a52c (patch)
tree3c1df15339b0b5e3f1d07193c9f49e90d9885f95
parentb17bf6dde52446a3eebbf667735367566288b97f (diff)
downloadgo-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.go44
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