diff --git a/private/pkg/app/app.go b/private/pkg/app/app.go index 0f9011de1d..866fe7d61c 100644 --- a/private/pkg/app/app.go +++ b/private/pkg/app/app.go @@ -326,12 +326,7 @@ func Main(ctx context.Context, f func(context.Context, Container) error) { // The run will be stopped on interrupt signal. // The exit code can be determined using GetExitCode. func Run(ctx context.Context, container Container, f func(context.Context, Container) error) error { - ctx, cancel := interrupt.NotifyContext(ctx) - go func() { - <-ctx.Done() - cancel() - }() - if err := f(ctx, container); err != nil { + if err := f(interrupt.Handle(ctx), container); err != nil { printError(container, err) return err } diff --git a/private/pkg/interrupt/interrupt.go b/private/pkg/interrupt/interrupt.go index 50f04e1496..446e5bc6f0 100644 --- a/private/pkg/interrupt/interrupt.go +++ b/private/pkg/interrupt/interrupt.go @@ -16,14 +16,38 @@ package interrupt import ( "context" - "os" "os/signal" ) -var interruptSignals = append([]os.Signal{os.Interrupt}, extraSignals...) - -// NotifyContext returns a new [context.Context] from [signal.NotifyContext] -// with the appropriate interrupt signals. -func NotifyContext(ctx context.Context) (context.Context, context.CancelFunc) { - return signal.NotifyContext(ctx, interruptSignals...) +// Handle returns a copy of the parent [context.Context] that is marked done +// when an interrupt signal arrives or when the parent Context's Done channel +// is closed, whichever happens first. +// +// Signal handling is unregistered automatically by this function when the +// first interrupt signal arrives, which will restore the default interrupt +// signal behabior of Go programs (to exit). +// +// In effect, this function is functionally equivalent to: +// +// ctx, cancel := signal.NotifyContext(ctx, interrupt.Signals...) +// defer func() { +// <-ctx.Done() +// cancel() +// }() +// +// Most programs should wrap their contexts using this function to enable interrupt +// signal handling. The first interrupt signal will result in the context's Done +// channel closing. The second interrupt signal will result in the program exiting. +// +// func main() { +// ctx := interrupt.Handle(context.Background()) +// ... +// } +func Handle(ctx context.Context) context.Context { + ctx, cancel := signal.NotifyContext(ctx, Signals...) + defer func() { + <-ctx.Done() + cancel() + }() + return ctx } diff --git a/private/pkg/interrupt/interrupt_other.go b/private/pkg/interrupt/interrupt_other.go index 5a5dd97924..8e3e48a846 100644 --- a/private/pkg/interrupt/interrupt_other.go +++ b/private/pkg/interrupt/interrupt_other.go @@ -18,9 +18,8 @@ package interrupt import "os" -// extraSignals are signals beyond os.Interrupt that we want to be handled -// as interrupts. +// Signals are all interrupt signals. // -// For unix-like platforms, this adds syscall.SIGTERM. No other signals -// are added for other platforms. -var extraSignals = []os.Signal{} +// As opposed to os.Interrupt, this adds syscall.SIGTERM for unix-like platforms. For +// other platforms, this is just os.Interrupt +var Signals = []os.Signal{os.Interrupt} diff --git a/private/pkg/interrupt/interrupt_unix.go b/private/pkg/interrupt/interrupt_unix.go index 903ca1ddc7..3b476ce5cf 100644 --- a/private/pkg/interrupt/interrupt_unix.go +++ b/private/pkg/interrupt/interrupt_unix.go @@ -21,9 +21,8 @@ import ( "syscall" ) -// extraSignals are signals beyond os.Interrupt that we want to be handled -// as interrupts. +// Signals are all interrupt signals. // -// For unix-like platforms, this adds syscall.SIGTERM. No other signals -// are added for other platforms. -var extraSignals = []os.Signal{syscall.SIGTERM} +// As opposed to os.Interrupt, this adds syscall.SIGTERM for unix-like platforms. For +// other platforms, this is just os.Interrupt +var Signals = []os.Signal{os.Interrupt, syscall.SIGTERM}