github.com/pf-qiu/concourse/v6@v6.7.3-0.20201207032516-1f455d73275f/worker/runtime/process_killer.go (about) 1 package runtime 2 3 import ( 4 "context" 5 "fmt" 6 "syscall" 7 "time" 8 9 "github.com/containerd/containerd" 10 ) 11 12 //go:generate go run github.com/maxbrunsfeld/counterfeiter/v6 . ProcessKiller 13 14 type ProcessKiller interface { 15 16 // Kill terminates a single process. 17 // 18 Kill( 19 ctx context.Context, 20 proc containerd.Process, 21 signal syscall.Signal, 22 waitPeriod time.Duration, 23 ) error 24 } 25 26 type processKiller struct{} 27 28 func NewProcessKiller() *processKiller { 29 return &processKiller{} 30 } 31 32 // Kill delivers a signal to a process, waiting for a maximum of `waitPeriod` 33 // for a status to be reported back. 34 // 35 // In case no status is reported within the grace period time span, 36 // ErrGracePeriodTimeout is returned. 37 // 38 // ps.: even in the case of a SIGKILL being used as the signal, `Kill` will 39 // wait for a `waitPeriod` for a status to be reported back. This way one 40 // can detect cases where not even a SIGKILL changes the status of a 41 // process (e.g., if the process is frozen due to a blocking syscall that 42 // never returns, or because it's suspended - see [1]). 43 // 44 // [1]: https://github.com/pf-qiu/concourse/v6/issues/4477 45 // 46 func (p processKiller) Kill( 47 ctx context.Context, 48 proc containerd.Process, 49 signal syscall.Signal, 50 waitPeriod time.Duration, 51 ) error { 52 waitCtx, cancel := context.WithTimeout(ctx, waitPeriod) 53 defer cancel() 54 55 statusC, err := proc.Wait(waitCtx) 56 if err != nil { 57 return fmt.Errorf("proc wait: %w", err) 58 } 59 60 err = proc.Kill(ctx, signal) 61 if err != nil { 62 return fmt.Errorf("proc kill w/ signal %d: %w", signal, err) 63 } 64 65 select { 66 case <-waitCtx.Done(): 67 err = waitCtx.Err() 68 if err == context.DeadlineExceeded { 69 return ErrGracePeriodTimeout 70 } 71 72 return fmt.Errorf("waitctx done: %w", err) 73 case status := <-statusC: 74 err = status.Error() 75 if err != nil { 76 return fmt.Errorf("waiting for exit status: %w", err) 77 } 78 } 79 80 return nil 81 }