-
Notifications
You must be signed in to change notification settings - Fork 821
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Fix data race #162
Fix data race #162
Conversation
Thanks! How about something like this to avoid the mutex's? cmd := exec.Command(input.Command, input.Args...)
cmd.Env = env
cmd.Stdin = os.Stdin
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
if err = cmd.Start(); err != nil {
app.Fatalf("%v", err)
return
}
// wait for the command to finish
waitCh := make(chan error)
go func() {
waitCh <- cmd.Wait()
}()
for {
select {
case sig := <-input.Signals:
if err = cmd.Process.Signal(sig); err != nil {
app.Errorf("%v", err)
return
}
case err := <-waitCh:
var waitStatus syscall.WaitStatus
if exitError, ok := err.(*exec.ExitError); ok {
waitStatus = exitError.Sys().(syscall.WaitStatus)
os.Exit(waitStatus.ExitStatus())
}
if err != nil {
app.Errorf("%v", err)
return
}
}
} |
2ffc675
to
761159b
Compare
Done - I think we don't want the for loop, since every branch of the |
Hmmmm, I suspect we will need the for loop, otherwise receiving any signal before the process finishes will cause it to terminate. Theoretically it should support multiple signals too, although the current implementation doesn't. Which example will run forever? |
This one:
|
Hmmm. Ah right, it needs a break at the end of the <-waitCh case even if the error is nil. cmd := exec.Command(input.Command, input.Args...)
cmd.Env = env
cmd.Stdin = os.Stdin
cmd.Stdout = os.Stdout
cmd.Stderr = os.Stderr
if err = cmd.Start(); err != nil {
app.Fatalf("%v", err)
return
}
// wait for the command to finish
waitCh := make(chan error)
go func() {
waitCh <- cmd.Wait()
}()
for {
select {
// listen for signals and send them to the process
case sig := <-input.Signals:
if err = cmd.Process.Signal(sig); err != nil {
app.Errorf("%v", err)
break
}
// wait for return to exit
case err := <-waitCh:
var waitStatus syscall.WaitStatus
if exitError, ok := err.(*exec.ExitError); ok {
waitStatus = exitError.Sys().(syscall.WaitStatus)
os.Exit(waitStatus.ExitStatus())
}
if err != nil {
app.Errorf("%v", err)
}
break
}
} |
Apologies; if we try the code you pasted, I still get a hang in the test. I think what we need is to break/continue processing after we send a signal, but to always return if the underlying program exits/ we receive a value on |
We try to read cmd.Process in the thread that handles incoming signals, but there's no synchronization between that goroutine and the one that sets cmd.Process to a non-nil value. This might be overkill, e.g. there may be a way to write this with less code, but I confirmed that this patch addresses the race when shutting down the program. Fixes 99designs#158.
761159b
to
e7a769f
Compare
Looks good, thanks! |
We try to read cmd.Process in the thread that handles incoming
signals, but there's no synchronization between that goroutine and the
one that sets cmd.Process to a non-nil value.
This might be overkill, e.g. there may be a way to write this with
less code, but I confirmed that this patch addresses the
race when shutting down the program.
Fixes #158.