From 5682480726b05ab9ae2c72c6fbecf0880a2c9e3d Mon Sep 17 00:00:00 2001 From: Jaime Soriano Pastor Date: Sat, 20 Apr 2024 08:09:26 +0200 Subject: [PATCH] Fix #11710: Avoid to try to close channel twice after hitting Ctrl-C on compose up (#11719) Ensure done channel is closed only once Signed-off-by: Jaime Soriano Pastor --- pkg/compose/up.go | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/pkg/compose/up.go b/pkg/compose/up.go index 1a3e928957..a0ebdcbc24 100644 --- a/pkg/compose/up.go +++ b/pkg/compose/up.go @@ -21,6 +21,7 @@ import ( "fmt" "os" "os/signal" + "sync/atomic" "syscall" "github.com/compose-spec/compose-go/v2/types" @@ -65,9 +66,10 @@ func (s *composeService) Up(ctx context.Context, project *types.Project, options // we might miss a signal while setting up the second channel read // (this is also why signal.Notify is used vs signal.NotifyContext) signalChan := make(chan os.Signal, 2) - signal.Notify(signalChan, syscall.SIGINT, syscall.SIGTERM) defer close(signalChan) - var isTerminated bool + signal.Notify(signalChan, syscall.SIGINT, syscall.SIGTERM) + defer signal.Stop(signalChan) + var isTerminated atomic.Bool printer := newLogPrinter(options.Start.Attach) doneCh := make(chan bool) @@ -78,12 +80,11 @@ func (s *composeService) Up(ctx context.Context, project *types.Project, options formatter.ClearLine() fmt.Fprintln(s.stdinfo(), "Gracefully stopping... (press Ctrl+C again to force)") eg.Go(func() error { - err := s.Stop(context.Background(), project.Name, api.StopOptions{ + err := s.Stop(context.WithoutCancel(ctx), project.Name, api.StopOptions{ Services: options.Create.Services, Project: project, }) - isTerminated = true - close(doneCh) + isTerminated.Store(true) return err }) first = false @@ -120,7 +121,7 @@ func (s *composeService) Up(ctx context.Context, project *types.Project, options break } eg.Go(func() error { - err := s.kill(context.Background(), project.Name, api.KillOptions{ + err := s.kill(context.WithoutCancel(ctx), project.Name, api.KillOptions{ Services: options.Create.Services, Project: project, All: true, @@ -165,18 +166,17 @@ func (s *composeService) Up(ctx context.Context, project *types.Project, options }) } - // We don't use parent (cancelable) context as we manage sigterm to stop the stack - err = s.start(context.Background(), project.Name, options.Start, printer.HandleEvent) - if err != nil && !isTerminated { // Ignore error if the process is terminated + // We use the parent context without cancelation as we manage sigterm to stop the stack + err = s.start(context.WithoutCancel(ctx), project.Name, options.Start, printer.HandleEvent) + if err != nil && !isTerminated.Load() { // Ignore error if the process is terminated return err } + // Signal for the signal-handler goroutines to stop + close(doneCh) + printer.Stop() - if !isTerminated { - // signal for the signal-handler goroutines to stop - close(doneCh) - } err = eg.Wait().ErrorOrNil() if exitCode != 0 { errMsg := ""