]> Cypherpunks repositories - gostls13.git/commitdiff
time: add a regression test for time.AfterFunc goroutine starvation
authorBryan C. Mills <bcmills@google.com>
Fri, 19 Jan 2024 22:10:21 +0000 (17:10 -0500)
committerGopher Robot <gobot@golang.org>
Mon, 22 Jan 2024 16:16:08 +0000 (16:16 +0000)
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>
src/time/sleep_test.go

index 2f791240f927810a2904a041b9e985ef9d89a88d..b25606dfed762353b23fa82d02d797cef9acc009 100644 (file)
@@ -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