I need to run a long-running child process and kill it if I quit (for any reason) out of parent application.
Here is the code:
cmd := exec.Command("./long-process")
defer cmd.Process.Kill()
if err != nil {
log.Fatal(err)
}
var fail io.ReadCloser
fail.Close()
The fail
here produces obvious
panic: runtime error: invalid memory address or nil pointer dereference
It works as expected - the child process is killed.
But this happens in a goroutine:
cmd := exec.Command("./long-process")
defer cmd.Process.Kill()
if err != nil {
log.Fatal(err)
}
go func() {
var fail io.ReadCloser
fail.Close()
}()
The panic still happens, but then it seems defer
is not called and the child process is not killed.
Any way to go around this?
UPDATE I need a cross-platform solution (at least for Linux and FreeBSD)
Minimal example:
infinite-loop.sh
#!/bin/bash
while true; do
sleep 1
done
Don't forget to
chmod +x infinite-loop.sh
test1.go
(error checking left out for brevity):
package main
import (
"time"
"io"
"os/exec"
"runtime"
)
func main() {
cmd := exec.Command("./infinite-loop.sh")
cmd.Start()
defer cmd.Process.Kill()
go func() {
time.Sleep(100 * time.Millisecond)
var fail io.ReadCloser
fail.Close()
}()
for {
runtime.Gosched()
}
}
Let's run
ps aux | grep infinite-loop.sh | grep -v grep | wc -l; \
go run test1.go; \
ps aux | grep infinite-loop.sh | grep -v grep | wc -l
0 <--- !!
panic: runtime error: invalid memory address or nil pointer dereference
[signal 0xb code=0x1 addr=0x20 pc=0x2130]
goroutine 5 [running]:
main.main.func1()
.../multiline/test1.go:19 +0x30
created by main.main
.../multiline/test1.go:20 +0x9a
goroutine 1 [runnable]:
runtime.Gosched()
/usr/local/Cellar/go/1.5.1/libexec/src/runtime/proc.go:166 +0x14
main.main()
.../multiline/test1.go:23 +0x9f
exit status 2
1 <--- !!
0 processes before and 1 after exit.
If you comment out goroutine code - it works fine.
Now we can kill it:
kill $(ps aux | grep infinite-loop.sh | grep -v grep | awk {'print $2'})