aboutsummaryrefslogtreecommitdiff
path: root/src/runtime/testdata
diff options
context:
space:
mode:
authorBryan C. Mills <bcmills@google.com>2020-05-13 12:43:46 -0400
committerBryan C. Mills <bcmills@google.com>2020-05-13 17:48:20 +0000
commitee0d40cba454c32876a8730d7029bfa6db073735 (patch)
tree348bfd67f2b488030d2fa442389e764a7c39326f /src/runtime/testdata
parent810c27e9be647ce4da8930ff3625a856041ae5b2 (diff)
downloadgo-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.go70
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
}