Не существует простого или элегантного способа выяснить, был ли процесс уничтожен из-за отмены контекста. Самое близкое, что вы можете сделать, это:
func run() error {
ctx, cancel := context.WithTimeout(context.Background(), 1*time.Second)
defer cancel()
cmd := exec.CommandContext(ctx, "bash", "-c", "exit 1")
// Start() returns an error if the process can't be started. It will return
// ctx.Err() if the context is expired before starting the process.
if err := cmd.Start(); err != nil {
return err
}
if err := cmd.Wait(); err != nil {
if e, ok := err.(*exec.ExitError); ok {
// If the process exited by itself, just return the error to the
// caller.
if e.Exited() {
return e
}
// We know now that the process could be started, but didn't exit
// by itself. Something must have killed it. If the context is done,
// we can *assume* that it has been killed by the exec.Command.
// Let's return ctx.Err() so our user knows that this *might* be
// the case.
select {
case <-ctx.Done():
return ctx.Err()
default:
return e
}
}
return err
}
return nil
}
Проблема здесь в том, что может быть условие гонки, поэтому возвращение ctx.Err()
может ввести в заблуждение. Например, представьте следующий сценарий:
- Процесс запускается.
- Процесс прерван внешним субъектом.
- Контекст отменен.
- Вы проверяете контекст.
На этом этапе вышеприведенная функция вернет ctx.Err()
, но это может вводить в заблуждение, поскольку причина, по которой процесс был убит, заключается не в том, что контекст был отменен , Если вы решили использовать код, аналогичный функции, описанной выше, помните об этом приближении.