mirror of
https://github.com/golang/go.git
synced 2025-10-20 03:23:18 +00:00
runtime: run TestSignalDuringExec in its own process group
TestSignalDuringExec sends a SIGWINCH to the whole process group. However, it may execute concurrently with other copies of the runtime tests, especially through `go tool dist`, and gdb version <12.1 has a bug in non-interactive mode where recieving a SIGWINCH causes a crash. This change modifies SignalDuringExec in the testprog to first fork itself into a new process group. To avoid issues with Ctrl+C and the new process group hanging, the new process blocks on a pipe that is passed down to it. This pipe is automatically closed when its parent exits, which should ensure that the subprocess also exits. Fixes #58932. Change-Id: I3906afa28cf8b15d22ae612d071bce7f30fc3e6c Cq-Include-Trybots: luci.golang.try:gotip-linux-amd64-longtest-noswissmap,gotip-linux-amd64-longtest-aliastypeparams,gotip-linux-amd64-longtest,gotip-linux-386-longtest Reviewed-on: https://go-review.googlesource.com/c/go/+/686875 LUCI-TryBot-Result: Go LUCI <golang-scoped@luci-project-accounts.iam.gserviceaccount.com> Reviewed-by: Michael Pratt <mpratt@google.com>
This commit is contained in:
parent
67c1704444
commit
8131635e5a
2 changed files with 46 additions and 0 deletions
|
@ -78,6 +78,9 @@ func checkGdbVersion(t *testing.T) {
|
||||||
if major < 10 {
|
if major < 10 {
|
||||||
t.Skipf("skipping: gdb version %d.%d too old", major, minor)
|
t.Skipf("skipping: gdb version %d.%d too old", major, minor)
|
||||||
}
|
}
|
||||||
|
if major < 12 || (major == 12 && minor < 1) {
|
||||||
|
t.Logf("gdb version <12.1 is known to crash due to a SIGWINCH recieved in non-interactive mode; if you see a crash, some test may be sending SIGWINCH to the whole process group. See go.dev/issue/58932.")
|
||||||
|
}
|
||||||
t.Logf("gdb version %d.%d", major, minor)
|
t.Logf("gdb version %d.%d", major, minor)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
43
src/runtime/testdata/testprognet/signalexec.go
vendored
43
src/runtime/testdata/testprognet/signalexec.go
vendored
|
@ -13,9 +13,11 @@ package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"io"
|
||||||
"os"
|
"os"
|
||||||
"os/exec"
|
"os/exec"
|
||||||
"os/signal"
|
"os/signal"
|
||||||
|
"runtime"
|
||||||
"sync"
|
"sync"
|
||||||
"syscall"
|
"syscall"
|
||||||
"time"
|
"time"
|
||||||
|
@ -23,10 +25,51 @@ import (
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
register("SignalDuringExec", SignalDuringExec)
|
register("SignalDuringExec", SignalDuringExec)
|
||||||
|
register("SignalDuringExecPgrp", SignalDuringExecPgrp)
|
||||||
register("Nop", Nop)
|
register("Nop", Nop)
|
||||||
}
|
}
|
||||||
|
|
||||||
func SignalDuringExec() {
|
func SignalDuringExec() {
|
||||||
|
// Re-launch ourselves in a new process group.
|
||||||
|
cmd := exec.Command(os.Args[0], "SignalDuringExecPgrp")
|
||||||
|
cmd.Stdout = os.Stdout
|
||||||
|
cmd.Stderr = os.Stderr
|
||||||
|
cmd.SysProcAttr = &syscall.SysProcAttr{
|
||||||
|
Setpgid: true,
|
||||||
|
}
|
||||||
|
|
||||||
|
// Start the new process with an extra pipe. It will
|
||||||
|
// exit if the pipe is closed.
|
||||||
|
rp, wp, err := os.Pipe()
|
||||||
|
if err != nil {
|
||||||
|
fmt.Printf("Failed to create pipe: %v", err)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
cmd.ExtraFiles = []*os.File{rp}
|
||||||
|
|
||||||
|
// Run the command.
|
||||||
|
if err := cmd.Run(); err != nil {
|
||||||
|
fmt.Printf("Run failed: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// We don't actually need to write to the pipe, it just
|
||||||
|
// needs to get closed, which will happen on process
|
||||||
|
// exit.
|
||||||
|
runtime.KeepAlive(wp)
|
||||||
|
}
|
||||||
|
|
||||||
|
func SignalDuringExecPgrp() {
|
||||||
|
// Grab fd 3 which is a pipe we need to read on.
|
||||||
|
f := os.NewFile(3, "pipe")
|
||||||
|
go func() {
|
||||||
|
// Nothing will ever get written to the pipe, so we'll
|
||||||
|
// just block on it. If it closes, ReadAll will return
|
||||||
|
// one way or another, at which point we'll exit.
|
||||||
|
io.ReadAll(f)
|
||||||
|
os.Exit(1)
|
||||||
|
}()
|
||||||
|
|
||||||
|
// This is just for SignalDuringExec.
|
||||||
pgrp := syscall.Getpgrp()
|
pgrp := syscall.Getpgrp()
|
||||||
|
|
||||||
const tries = 10
|
const tries = 10
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue