Какую версию Go вы используете (go version
)?
$ go version 1.13.1
Воспроизводится ли эта проблема в последнем выпуске?
Я не уверен.
Какую операционную систему и архитектуру процессора вы используете (go env
)?
$ go env
GO111MODULE="auto"
GOARCH="amd64"
GOBIN="/usr/local/go/bin"
GOCACHE="/data/xieyixin/.cache/go-build"
GOENV="/data/xieyixin/.config/go/env"
GOEXE=""
GOFLAGS=""
GOHOSTARCH="amd64"
GOHOSTOS="linux"
GONOPROXY=""
GONOSUMDB=""
GOOS="linux"
GOPATH="/data/xieyixin/go"
GOPRIVATE=""
GOPROXY="http://10.0.12.201:8989/"
GOROOT="/usr/local/go"
GOSUMDB="sum.golang.org"
GOTMPDIR=""
GOTOOLDIR="/usr/local/go/pkg/tool/linux_amd64"
GCCGO="gccgo"
AR="ar"
CC="gcc"
CXX="g++"
CGO_ENABLED="1"
GOMOD="/data/xieyixin/hxagent/go.mod"
CGO_CFLAGS="-g -O2"
CGO_CPPFLAGS=""
CGO_CXXFLAGS="-g -O2"
CGO_FFLAGS="-g -O2"
CGO_LDFLAGS="-g -O2"
PKG_CONFIG="pkg-config"
GOGCCFLAGS="-fPIC -m64 -pthread -fmessage-length=0 -fdebug-prefix-map=/tmp/go-build474907248=/tmp/go-build"
Что вы делали?
Я пишу функцию для выполнения команды exec, иЯ самостоятельно проверяю время ожидания. И я проверяю это так
package utils
import (
"bytes"
"context"
"log"
"os/exec"
"syscall"
"time"
)
func ExecCommand(command string, timeout time.Duration) (string, error) {
log.Printf("command:%v, timeout:%v", command, timeout)
var (
cmd *exec.Cmd
stdout bytes.Buffer
stderr bytes.Buffer
result string
err error
//timeouterr error
)
ctx, cancelFn := context.WithTimeout(context.Background(), timeout)
defer cancelFn()
cmd = exec.Command("bash", "-c", "--", command)
cmd.Stdout = &stdout
cmd.Stderr = &stderr
cmd.SysProcAttr = &syscall.SysProcAttr{Setpgid: true}
var waitDone = make(chan struct{})
defer func() {
log.Printf("waitDone addr:%v\n", &waitDone)
log.Printf("close waitdone channel\n")
close(waitDone)
}()
go func() {
err = cmd.Run()
log.Printf("waitDone addr:%v\n", &waitDone)
waitDone <- struct{}{}
}()
select {
case <-ctx.Done():
log.Printf("timeout to kill process, %v", cmd.Process.Pid)
syscall.Kill(-cmd.Process.Pid, syscall.SIGKILL)
result = convertStr(stdout)
err = ctx.Err()
case <-waitDone:
if err != nil {
result = convertStr(stderr)
} else {
result = convertStr(stdout)
}
}
log.Printf("result:%v,err:%v", result, err)
return result, err
}
func convertStr(buffer bytes.Buffer) string {
data := buffer.String()
return data
}
package utils
import (
"context"
"testing"
"time"
)
func TestExecCommand(t *testing.T) {
tests := []struct {
command string
timeout time.Duration
wantErr string
want string
}{
{
command: "sleep 10",
timeout: time.Second * 5,
wantErr: context.DeadlineExceeded.Error(),
},
{
command: "watch -n 1 date +%s",
timeout: time.Second * 10,
wantErr: context.DeadlineExceeded.Error(),
want: "timeout, but still have result.",
},
{
command: "hostname",
timeout: time.Second * 5,
wantErr: "",
want: "anything result would be fine.",
},
}
for _, tt := range tests {
// got panic here.
// send on closed channel.
got, gotErr := ExecCommand(tt.command, tt.timeout)
if gotErr == nil {
if tt.wantErr == "" {
t.Logf("succeed")
} else {
t.Errorf("failed case: %+v, got:%v, gotErr:%v\n", tt, got, gotErr)
}
} else if gotErr.Error() == tt.wantErr {
t.Logf("succeed")
} else {
t.Errorf("failed case: %+v, got:%v, gotErr:%v\n", tt, got, gotErr)
}
}
}
Что вы ожидали увидеть?
тест в порядке.
Что вы видели вместо этого?
паника: отправить по закрытому каналу.
edit1: причина, по которой я сам управляю контекстом [https://medium.com/@felixge/killing-a-child-process-and-all-of-its-children-in-go-54079af94773]
edit2: здесь больше путаницы.
мне это кажется немного понятным. но у меня все еще есть вопрос. [https://golang.org/src/os/exec/exec.go?s=11462:11489#L440],
if c.ctx != nil {
c.waitDone = make(chan struct{}) // here
go func() {
select {
case <-c.ctx.Done():
c.Process.Kill()
case <-c.waitDone: // and here
}
}()
}
, как вы можете видеть, это похоже на код @Cerise Limón. почему мы должны писать этот канал. это необходимо?