Skip to content

Commit 5682480

Browse files
authored
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 <[email protected]>
1 parent fd532a3 commit 5682480

File tree

1 file changed

+13
-13
lines changed

1 file changed

+13
-13
lines changed

pkg/compose/up.go

+13-13
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ import (
2121
"fmt"
2222
"os"
2323
"os/signal"
24+
"sync/atomic"
2425
"syscall"
2526

2627
"github.com/compose-spec/compose-go/v2/types"
@@ -65,9 +66,10 @@ func (s *composeService) Up(ctx context.Context, project *types.Project, options
6566
// we might miss a signal while setting up the second channel read
6667
// (this is also why signal.Notify is used vs signal.NotifyContext)
6768
signalChan := make(chan os.Signal, 2)
68-
signal.Notify(signalChan, syscall.SIGINT, syscall.SIGTERM)
6969
defer close(signalChan)
70-
var isTerminated bool
70+
signal.Notify(signalChan, syscall.SIGINT, syscall.SIGTERM)
71+
defer signal.Stop(signalChan)
72+
var isTerminated atomic.Bool
7173
printer := newLogPrinter(options.Start.Attach)
7274

7375
doneCh := make(chan bool)
@@ -78,12 +80,11 @@ func (s *composeService) Up(ctx context.Context, project *types.Project, options
7880
formatter.ClearLine()
7981
fmt.Fprintln(s.stdinfo(), "Gracefully stopping... (press Ctrl+C again to force)")
8082
eg.Go(func() error {
81-
err := s.Stop(context.Background(), project.Name, api.StopOptions{
83+
err := s.Stop(context.WithoutCancel(ctx), project.Name, api.StopOptions{
8284
Services: options.Create.Services,
8385
Project: project,
8486
})
85-
isTerminated = true
86-
close(doneCh)
87+
isTerminated.Store(true)
8788
return err
8889
})
8990
first = false
@@ -120,7 +121,7 @@ func (s *composeService) Up(ctx context.Context, project *types.Project, options
120121
break
121122
}
122123
eg.Go(func() error {
123-
err := s.kill(context.Background(), project.Name, api.KillOptions{
124+
err := s.kill(context.WithoutCancel(ctx), project.Name, api.KillOptions{
124125
Services: options.Create.Services,
125126
Project: project,
126127
All: true,
@@ -165,18 +166,17 @@ func (s *composeService) Up(ctx context.Context, project *types.Project, options
165166
})
166167
}
167168

168-
// We don't use parent (cancelable) context as we manage sigterm to stop the stack
169-
err = s.start(context.Background(), project.Name, options.Start, printer.HandleEvent)
170-
if err != nil && !isTerminated { // Ignore error if the process is terminated
169+
// We use the parent context without cancelation as we manage sigterm to stop the stack
170+
err = s.start(context.WithoutCancel(ctx), project.Name, options.Start, printer.HandleEvent)
171+
if err != nil && !isTerminated.Load() { // Ignore error if the process is terminated
171172
return err
172173
}
173174

175+
// Signal for the signal-handler goroutines to stop
176+
close(doneCh)
177+
174178
printer.Stop()
175179

176-
if !isTerminated {
177-
// signal for the signal-handler goroutines to stop
178-
close(doneCh)
179-
}
180180
err = eg.Wait().ErrorOrNil()
181181
if exitCode != 0 {
182182
errMsg := ""

0 commit comments

Comments
 (0)