diff options
author | Bryan C. Mills <bcmills@google.com> | 2020-05-13 12:43:46 -0400 |
---|---|---|
committer | Bryan C. Mills <bcmills@google.com> | 2020-05-13 17:48:20 +0000 |
commit | ee0d40cba454c32876a8730d7029bfa6db073735 (patch) | |
tree | 348bfd67f2b488030d2fa442389e764a7c39326f /src/runtime/testdata | |
parent | 810c27e9be647ce4da8930ff3625a856041ae5b2 (diff) | |
download | go-ee0d40cba454c32876a8730d7029bfa6db073735.tar.gz go-ee0d40cba454c32876a8730d7029bfa6db073735.zip |
runtime: reduce timing sensitivity in TestEINTR
- Don't assume that a process interrupted at 100μs intervals will have
enough remaining time to make progress. (Stop sending signals
in between signal storms to allow the process to quiesce.)
- Don't assume that a child process that spins for 1ms will block long
enough for the parent process to receive signals or make meaningful
progress. (Instead, have the child block indefinitely, and unblock
it explicitly after the signal storm.)
For #39043
Updates #22838
Updates #20400
Change-Id: I85cba23498c346a637e6cfe8684ca0c478562a93
Reviewed-on: https://go-review.googlesource.com/c/go/+/233877
Run-TryBot: Bryan C. Mills <bcmills@google.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Ian Lance Taylor <iant@golang.org>
Diffstat (limited to 'src/runtime/testdata')
-rw-r--r-- | src/runtime/testdata/testprogcgo/eintr.go | 70 |
1 files changed, 48 insertions, 22 deletions
diff --git a/src/runtime/testdata/testprogcgo/eintr.go b/src/runtime/testdata/testprogcgo/eintr.go index cd88c15c37..58f0dd2ca3 100644 --- a/src/runtime/testdata/testprogcgo/eintr.go +++ b/src/runtime/testdata/testprogcgo/eintr.go @@ -36,6 +36,7 @@ import ( "net" "os" "os/exec" + "os/signal" "sync" "syscall" "time" @@ -43,7 +44,7 @@ import ( func init() { register("EINTR", EINTR) - register("Nop", Nop) + register("Block", Block) } // Test various operations when a signal handler is installed without @@ -59,13 +60,6 @@ func EINTR() { log.Fatal(syscall.Errno(errno)) } - // Send ourselves SIGWINCH regularly. - go func() { - for range time.Tick(100 * time.Microsecond) { - syscall.Kill(0, syscall.SIGWINCH) - } - }() - var wg sync.WaitGroup testPipe(&wg) testNet(&wg) @@ -90,6 +84,22 @@ func spin() (float64, [][]byte) { return r1, r2 } +// winch sends a few SIGWINCH signals to the process. +func winch() { + ticker := time.NewTicker(100 * time.Microsecond) + defer ticker.Stop() + for n := 10; n > 0; n-- { + syscall.Kill(0, syscall.SIGWINCH) + <-ticker.C + } +} + +// sendSomeSignals triggers a few SIGURG and SIGWINCH signals. +func sendSomeSignals() { + spin() + winch() +} + // testPipe tests pipe operations. func testPipe(wg *sync.WaitGroup) { r, w, err := os.Pipe() @@ -109,19 +119,19 @@ func testPipe(wg *sync.WaitGroup) { // Spin before calling Write so that the first ReadFull // in the other goroutine will likely be interrupted // by a signal. - spin() + sendSomeSignals() // This Write will likely be interrupted by a signal // as the other goroutine spins in the middle of reading. // We write enough data that we should always fill the // pipe buffer and need multiple write system calls. - if _, err := w.Write(bytes.Repeat([]byte{0}, 2 << 20)); err != nil { + if _, err := w.Write(bytes.Repeat([]byte{0}, 2<<20)); err != nil { log.Fatal(err) } }() go func() { defer wg.Done() defer r.Close() - b := make([]byte, 1 << 20) + b := make([]byte, 1<<20) // This ReadFull will likely be interrupted by a signal, // as the other goroutine spins before writing anything. if _, err := io.ReadFull(r, b); err != nil { @@ -130,7 +140,7 @@ func testPipe(wg *sync.WaitGroup) { // Spin after reading half the data so that the Write // in the other goroutine will likely be interrupted // before it completes. - spin() + sendSomeSignals() if _, err := io.ReadFull(r, b); err != nil { log.Fatal(err) } @@ -164,14 +174,14 @@ func testNet(wg *sync.WaitGroup) { log.Fatal(err) } // See comments in testPipe. - spin() - if _, err := cf.Write(bytes.Repeat([]byte{0}, 2 << 20)); err != nil { + sendSomeSignals() + if _, err := cf.Write(bytes.Repeat([]byte{0}, 2<<20)); err != nil { log.Fatal(err) } }() go func() { defer wg.Done() - spin() + sendSomeSignals() c, err := net.Dial("tcp", ln.Addr().String()) if err != nil { log.Fatal(err) @@ -186,11 +196,11 @@ func testNet(wg *sync.WaitGroup) { log.Fatal(err) } // See comments in testPipe. - b := make([]byte, 1 << 20) + b := make([]byte, 1<<20) if _, err := io.ReadFull(cf, b); err != nil { log.Fatal(err) } - spin() + sendSomeSignals() if _, err := io.ReadFull(cf, b); err != nil { log.Fatal(err) } @@ -201,14 +211,30 @@ func testExec(wg *sync.WaitGroup) { wg.Add(1) go func() { defer wg.Done() - if err := exec.Command(os.Args[0], "Nop").Run(); err != nil { + cmd := exec.Command(os.Args[0], "Block") + cmd.Stderr = new(bytes.Buffer) + cmd.Stdout = cmd.Stderr + if err := cmd.Start(); err != nil { log.Fatal(err) } + + go func() { + sendSomeSignals() + if err := cmd.Process.Signal(os.Interrupt); err != nil { + panic(err) + } + }() + + if err := cmd.Wait(); err != nil { + log.Fatalf("%v:\n%s", err, cmd.Stdout) + } }() } -// Nop just sleeps for a bit. This is used to test interrupts while waiting -// for a child. -func Nop() { - time.Sleep(time.Millisecond) +// Block blocks until the process receives os.Interrupt. +func Block() { + c := make(chan os.Signal, 1) + signal.Notify(c, os.Interrupt) + defer signal.Stop(c) + <-c } |