Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why won't Go kill a child process correctly?

Tags:

go

exec

The following works just fine when cmd finishes in the allotted time. However, the timeout is not working. While it does print "It's dead Jim", not only does it fail to print "Done waiting", but the process is not actually killed. It continues to run, and "Done waiting" never prints.

func() {
    var output bytes.Buffer
    cmd := exec.Command("Command", args...)
    cmd.Dir = filepath.Dir(srcFile)
    cmd.Stdout, cmd.Stderr = &output, &output
    if err := cmd.Start(); err != nil {
        return err
    }
    defer time.AfterFunc(time.Second*2, func() {
        fmt.Printf("Nobody got time fo that\n")
        if err := cmd.Process.Signal(syscall.SIGKILL); err != nil {
            fmt.Printf("Error:%s\n", err)
        }
        fmt.Printf("It's dead Jim\n")
    }).Stop()
    err := cmd.Wait()
    fmt.Printf("Done waiting\n")
}()

I don't think it should make a difference, but for what it's worth the command is go test html. The reason it's timing out is because I'm injecting an error that causes an infinite loop before running it. To add to the confusion, I tried running it with go test net. There was a timeout, and it worked correctly.

like image 422
Floegipoky Avatar asked Mar 18 '14 04:03

Floegipoky


1 Answers

I'm not sure when it was added, but as of Go 1.11 you can set the Pdeathsig on a subprocess to syscall.SIGKILL. This will kill the child when the parent exits.

cmd, _ := exec.Command("long-running command")
cmd.SysProcAttr = &syscall.SysProcAttr{
    Pdeathsig: syscall.SIGKILL,
}
cmd.Start()

os.Exit(1)

The cmd should be killed on exit.

like image 153
c2knaps Avatar answered Sep 18 '22 17:09

c2knaps