in runner/execer/os/process.go [108:227]
func (p *process) Abort() scootexecer.ProcessStatus {
p.mutex.Lock()
defer p.mutex.Unlock()
if p.result != nil {
return *p.result
} else {
p.result = &scootexecer.ProcessStatus{}
}
p.result.State = scootexecer.FAILED
p.result.ExitCode = -1
p.result.Error = "Aborted"
if err := p.cmd.Process.Signal(syscall.SIGTERM); err != nil {
msg := fmt.Sprintf("Error aborting command via SIGTERM: %s.", err)
log.WithFields(
log.Fields{
"pid": p.cmd.Process.Pid,
"tag": p.Tag,
"jobID": p.JobID,
"taskID": p.TaskID,
}).Errorf(msg)
p.KillAndWait(msg)
} else {
log.WithFields(
log.Fields{
"pid": p.cmd.Process.Pid,
"tag": p.Tag,
"jobID": p.JobID,
"taskID": p.TaskID,
}).Info("Aborting process via SIGTERM")
}
// Add buffer in case of race condition where both <-cmdDoneCh returns an error & timeout is exceeded at same time
errCh := make(chan error, 1)
go func() {
select {
case <-time.After(time.Second * time.Duration(p.ats)):
errCh <- errors.New(fmt.Sprintf("%d second timeout exceeded.", p.ats))
}
}()
cmdDoneCh := make(chan error)
// Wait in the process if nothing already has claimed it;
// if cmd.Wait() was already called, calling it again is an immediate error.
// If already called, just poll periodically if the process has exited
if !p.waiting {
go func() {
// p.wg ignored
cmdDoneCh <- p.cmd.Wait()
}()
} else {
go func() {
timeout := time.Now().Add(time.Second * time.Duration(p.ats))
for time.Now().Before(timeout) {
// note that we can't rely on ProcessState.Exited() - not true when p is signaled
if p.cmd.ProcessState != nil {
cmdDoneCh <- nil
return
}
time.Sleep(10 * time.Millisecond)
}
}()
}
for {
select {
case err := <-cmdDoneCh:
sigtermed := false
if err == nil {
sigtermed = true
}
if err != nil {
if err, ok := err.(*exec.ExitError); ok {
if status, ok := err.Sys().(syscall.WaitStatus); ok {
if status.Signaled() {
sigtermed = true
}
}
}
}
if sigtermed {
log.WithFields(
log.Fields{
"pid": p.cmd.Process.Pid,
"tag": p.Tag,
"jobID": p.JobID,
"taskID": p.TaskID,
}).Info("Command finished via SIGTERM")
p.result.Error += " (SIGTERM)"
return *p.result
} else {
// We weren't able to infer the task exited either normally or due to sigterm
msg := fmt.Sprintf("Command failed to terminate successfully: %v", err)
log.WithFields(
log.Fields{
"pid": p.cmd.Process.Pid,
"tag": p.Tag,
"jobID": p.JobID,
"taskID": p.TaskID,
}).Error(msg)
errCh <- errors.New(msg)
// Loop back and pull from errCh to force cleanup
}
case msg := <-errCh:
log.WithFields(
log.Fields{
"pid": p.cmd.Process.Pid,
"tag": p.Tag,
"jobID": p.JobID,
"taskID": p.TaskID,
}).Error(msg)
p.KillAndWait(fmt.Sprintf("%s. Killing command.", msg))
p.result.Error += " (SIGKILL)"
return *p.result
default:
}
}
}