From 17aa21279965f5d088606639c17aa60208a34b7d Mon Sep 17 00:00:00 2001 From: "Bryan C. Mills" Date: Mon, 22 Nov 2021 14:30:40 -0500 Subject: [PATCH] runtime: in TestSpuriousWakeupsNeverHangSemasleep, wait for the runtime to register handlers According to https://man7.org/linux/man-pages/man7/signal.7.html, the default behavior of SIGIO is to terminate the program. The Go runtime changes that behavior with its own signal handler, so the program will terminate if we send the signal before the runtime has finished setting up. Fixes #49727 Change-Id: I65db66f5176831c8d7454eebc0138d825c68e100 Reviewed-on: https://go-review.googlesource.com/c/go/+/366255 Trust: Bryan C. Mills Run-TryBot: Bryan C. Mills Reviewed-by: Ian Lance Taylor --- src/runtime/semasleep_test.go | 41 +++++++++++++++++++------- src/runtime/testdata/testprog/sleep.go | 7 ++++- 2 files changed, 37 insertions(+), 11 deletions(-) diff --git a/src/runtime/semasleep_test.go b/src/runtime/semasleep_test.go index cf4ef18208..bc73140a2a 100644 --- a/src/runtime/semasleep_test.go +++ b/src/runtime/semasleep_test.go @@ -7,6 +7,7 @@ package runtime_test import ( + "io" "os/exec" "syscall" "testing" @@ -28,6 +29,10 @@ func TestSpuriousWakeupsNeverHangSemasleep(t *testing.T) { start := time.Now() cmd := exec.Command(exe, "After1") + stdout, err := cmd.StdoutPipe() + if err != nil { + t.Fatalf("StdoutPipe: %v", err) + } if err := cmd.Start(); err != nil { t.Fatalf("Failed to start command: %v", err) } @@ -36,27 +41,43 @@ func TestSpuriousWakeupsNeverHangSemasleep(t *testing.T) { doneCh <- cmd.Wait() }() + // Wait for After1 to close its stdout so that we know the runtime's SIGIO + // handler is registered. + b, err := io.ReadAll(stdout) + if len(b) > 0 { + t.Logf("read from testprog stdout: %s", b) + } + if err != nil { + t.Fatalf("error reading from testprog: %v", err) + } + // With the repro running, we can continuously send to it - // a non-terminal signal such as SIGIO, to spuriously - // wakeup pthread_cond_timedwait_relative_np. - unfixedTimer := time.NewTimer(2 * time.Second) + // a signal that the runtime considers non-terminal, + // such as SIGIO, to spuriously wake up + // pthread_cond_timedwait_relative_np. + ticker := time.NewTicker(200 * time.Millisecond) + defer ticker.Stop() for { select { - case <-time.After(200 * time.Millisecond): + case now := <-ticker.C: + if now.Sub(start) > 2*time.Second { + t.Error("Program failed to return on time and has to be killed, issue #27520 still exists") + cmd.Process.Signal(syscall.SIGKILL) + <-doneCh + return + } + // Send the pesky signal that toggles spinning // indefinitely if #27520 is not fixed. cmd.Process.Signal(syscall.SIGIO) - case <-unfixedTimer.C: - t.Error("Program failed to return on time and has to be killed, issue #27520 still exists") - cmd.Process.Signal(syscall.SIGKILL) - return - case err := <-doneCh: if err != nil { t.Fatalf("The program returned but unfortunately with an error: %v", err) } - if time.Since(start) < 100*time.Millisecond { + if time.Since(start) < 1*time.Second { + // The program was supposed to sleep for a full (monotonic) second; + // it should not return before that has elapsed. t.Fatalf("The program stopped too quickly.") } return diff --git a/src/runtime/testdata/testprog/sleep.go b/src/runtime/testdata/testprog/sleep.go index 86e2f6cfe6..b230e60181 100644 --- a/src/runtime/testdata/testprog/sleep.go +++ b/src/runtime/testdata/testprog/sleep.go @@ -4,7 +4,10 @@ package main -import "time" +import ( + "os" + "time" +) // for golang.org/issue/27250 @@ -13,5 +16,7 @@ func init() { } func After1() { + os.Stdout.WriteString("ready\n") + os.Stdout.Close() <-time.After(1 * time.Second) } -- 2.50.0