fix(command): fix rare race condition on log line stream at command completion

This commit is contained in:
Quentin McGaw
2026-05-21 15:44:21 +00:00
parent f8a677a424
commit 2210a0e9ad
+9 -18
View File
@@ -21,7 +21,6 @@ func (c *Cmder) Start(cmd *exec.Cmd) (
func start(cmd execCmd) (stdoutLines, stderrLines <-chan string, func start(cmd execCmd) (stdoutLines, stderrLines <-chan string,
waitError <-chan error, startErr error, waitError <-chan error, startErr error,
) { ) {
stop := make(chan struct{})
stdoutReady := make(chan struct{}) stdoutReady := make(chan struct{})
stdoutLinesCh := make(chan string) stdoutLinesCh := make(chan string)
stdoutDone := make(chan struct{}) stdoutDone := make(chan struct{})
@@ -33,22 +32,20 @@ func start(cmd execCmd) (stdoutLines, stderrLines <-chan string,
if err != nil { if err != nil {
return nil, nil, nil, err return nil, nil, nil, err
} }
go streamToChannel(stdoutReady, stop, stdoutDone, stdout, stdoutLinesCh) go streamToChannel(stdoutReady, stdoutDone, stdout, stdoutLinesCh)
stderr, err := cmd.StderrPipe() stderr, err := cmd.StderrPipe()
if err != nil { if err != nil {
_ = stdout.Close() _ = stdout.Close()
close(stop)
<-stdoutDone <-stdoutDone
return nil, nil, nil, err return nil, nil, nil, err
} }
go streamToChannel(stderrReady, stop, stderrDone, stderr, stderrLinesCh) go streamToChannel(stderrReady, stderrDone, stderr, stderrLinesCh)
err = cmd.Start() err = cmd.Start()
if err != nil { if err != nil {
_ = stdout.Close() _ = stdout.Close()
_ = stderr.Close() _ = stderr.Close()
close(stop)
<-stdoutDone <-stdoutDone
<-stderrDone <-stderrDone
return nil, nil, nil, err return nil, nil, nil, err
@@ -57,19 +54,20 @@ func start(cmd execCmd) (stdoutLines, stderrLines <-chan string,
waitErrorCh := make(chan error) waitErrorCh := make(chan error)
go func() { go func() {
err := cmd.Wait() err := cmd.Wait()
_ = stdout.Close()
_ = stderr.Close()
close(stop)
<-stdoutDone <-stdoutDone
<-stderrDone <-stderrDone
_ = stdout.Close()
_ = stderr.Close()
waitErrorCh <- err waitErrorCh <- err
}() }()
<-stdoutReady
<-stderrReady
return stdoutLinesCh, stderrLinesCh, waitErrorCh, nil return stdoutLinesCh, stderrLinesCh, waitErrorCh, nil
} }
func streamToChannel(ready chan<- struct{}, func streamToChannel(ready chan<- struct{}, done chan<- struct{},
stop <-chan struct{}, done chan<- struct{},
stream io.Reader, lines chan<- string, stream io.Reader, lines chan<- string,
) { ) {
defer close(done) defer close(done)
@@ -89,12 +87,5 @@ func streamToChannel(ready chan<- struct{},
if err == nil || errors.Is(err, os.ErrClosed) { if err == nil || errors.Is(err, os.ErrClosed) {
return return
} }
lines <- "stream error: " + err.Error()
// ignore the error if it is stopped.
select {
case <-stop:
return
default:
lines <- "stream error: " + err.Error()
}
} }