Skip to content

Commit e49232f

Browse files
committed
Fix potential goroutine leak in monitorStdin
Add a done channel to ensure the background goroutine that closes stdin exits cleanly when monitorStdin returns, not only when the context is cancelled. This prevents the goroutine from leaking if stdin closes before context cancellation. Fixes #1664 Assisted-By: cagent
1 parent c9116c0 commit e49232f

File tree

1 file changed

+11
-3
lines changed

1 file changed

+11
-3
lines changed

cmd/root/api.go

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -56,18 +56,26 @@ func newAPICmd() *cobra.Command {
5656

5757
// monitorStdin monitors stdin for EOF, which indicates the parent process has died.
5858
// When spawned with piped stdio, stdin closes when the parent process dies.
59+
// The caller is responsible for cancelling the context (e.g. via defer cancel()).
5960
func monitorStdin(ctx context.Context, cancel context.CancelFunc, stdin *os.File) {
60-
// Close stdin when context is cancelled to unblock the read
61+
done := make(chan struct{})
62+
63+
// Close stdin when context is cancelled to unblock the read.
64+
// Also exits cleanly when monitorStdin returns.
6165
go func() {
62-
<-ctx.Done()
66+
select {
67+
case <-ctx.Done():
68+
case <-done:
69+
}
6370
stdin.Close()
6471
}()
6572

73+
defer close(done)
74+
6675
buf := make([]byte, 1)
6776
for {
6877
n, err := stdin.Read(buf)
6978
if err != nil || n == 0 {
70-
// Only log and cancel if context isn't already done (parent died)
7179
if ctx.Err() == nil {
7280
slog.Info("stdin closed, parent process likely died, shutting down")
7381
cancel()

0 commit comments

Comments
 (0)