@@ -18,12 +18,14 @@ import (
1818 "bufio"
1919 "bytes"
2020 "context"
21+ "errors"
2122 "fmt"
2223 "io"
2324 "os"
2425 "os/exec"
2526 "reflect"
2627 "sync"
28+ "sync/atomic"
2729 "syscall"
2830 "time"
2931
@@ -117,17 +119,24 @@ type Executor struct {
117119 pipesMutex sync.Mutex
118120 stdoutPipeFile * os.File
119121 stderrPipeFile * os.File
122+ // Read ends of pipes must be closed to unblock goroutines stuck in Read().
123+ stdoutReadPipe * os.File
124+ stderrReadPipe * os.File
125+ stdoutHandlerReadPipe * os.File
126+ stderrHandlerReadPipe * os.File
120127
121128 StderrBuffer * bytes.Buffer
122129 StderrSplitter bufio.SplitFunc
123130 StderrHandler func (l string )
124131
125132 WaitHandler func (err error )
126133
127- started bool
128- stop bool
129- waitCh chan struct {}
130- stopCh chan struct {}
134+ started atomic.Bool
135+ stop atomic.Bool
136+ waitCh chan struct {}
137+ stopCh chan struct {}
138+ doneCh chan struct {}
139+ stopOnce sync.Once
131140
132141 lockWaitError sync.RWMutex
133142 waitError error
@@ -258,6 +267,7 @@ func (e *Executor) SetupStreamHandlers() error {
258267
259268 e .pipesMutex .Lock ()
260269 e .stdoutPipeFile = stdoutWritePipe
270+ e .stdoutReadPipe = stdoutReadPipe
261271 e .pipesMutex .Unlock ()
262272
263273 // create pipe for StdoutHandler
@@ -266,6 +276,9 @@ func (e *Executor) SetupStreamHandlers() error {
266276 if err != nil {
267277 return fmt .Errorf ("unable to create os pipe for stdoutHandler: %s" , err )
268278 }
279+ e .pipesMutex .Lock ()
280+ e .stdoutHandlerReadPipe = stdoutHandlerReadPipe
281+ e .pipesMutex .Unlock ()
269282 }
270283 }
271284
@@ -283,6 +296,7 @@ func (e *Executor) SetupStreamHandlers() error {
283296
284297 e .pipesMutex .Lock ()
285298 e .stderrPipeFile = stderrWritePipe
299+ e .stderrReadPipe = stderrReadPipe
286300 e .pipesMutex .Unlock ()
287301
288302 // create pipe for StderrHandler
@@ -291,6 +305,9 @@ func (e *Executor) SetupStreamHandlers() error {
291305 if err != nil {
292306 return fmt .Errorf ("unable to create os pipe for stderrHandler: %s" , err )
293307 }
308+ e .pipesMutex .Lock ()
309+ e .stderrHandlerReadPipe = stderrHandlerReadPipe
310+ e .pipesMutex .Unlock ()
294311 }
295312 }
296313
@@ -307,6 +324,12 @@ func (e *Executor) SetupStreamHandlers() error {
307324 // - Copy to buffer if capture is enabled
308325 // - Copy to pipe if StdoutHandler is set
309326 go func () {
327+ defer func () {
328+ if stdoutHandlerWritePipe != nil {
329+ _ = stdoutHandlerWritePipe .Close ()
330+ }
331+ e .closeStdoutReadPipe ()
332+ }()
310333 e .readFromStreams (stdoutReadPipe , stdoutHandlerWritePipe )
311334 }()
312335
@@ -316,6 +339,7 @@ func (e *Executor) SetupStreamHandlers() error {
316339 if e .StdoutHandler == nil {
317340 return
318341 }
342+ defer e .closeStdoutHandlerReadPipe ()
319343 e .ConsumeLines (stdoutHandlerReadPipe , e .StdoutHandler )
320344 logger .DebugF ("stop line consumer for '%s'" , e .cmd .Args [0 ])
321345 }()
@@ -331,6 +355,12 @@ func (e *Executor) SetupStreamHandlers() error {
331355
332356 logger .DebugF ("Start reading from stderr pipe" )
333357 defer logger .DebugF ("Stop reading from stderr pipe" )
358+ defer func () {
359+ if stderrHandlerWritePipe != nil {
360+ _ = stderrHandlerWritePipe .Close ()
361+ }
362+ e .closeStderrReadPipe ()
363+ }()
334364
335365 buf := make ([]byte , 16 )
336366 for {
@@ -347,7 +377,11 @@ func (e *Executor) SetupStreamHandlers() error {
347377 _ , _ = stderrHandlerWritePipe .Write (buf [:n ])
348378 }
349379
350- if err == io .EOF {
380+ if err != nil {
381+ if errors .Is (err , io .EOF ) || errors .Is (err , os .ErrClosed ) {
382+ break
383+ }
384+ logger .DebugF ("Error reading from stderr: %s\n " , err )
351385 break
352386 }
353387 }
@@ -357,6 +391,7 @@ func (e *Executor) SetupStreamHandlers() error {
357391 if e .StderrHandler == nil {
358392 return
359393 }
394+ defer e .closeStderrHandlerReadPipe ()
360395 e .ConsumeLines (stderrHandlerReadPipe , e .StderrHandler )
361396 logger .DebugF ("stop sdterr line consumer for '%s'" , e .cmd .Args [0 ])
362397 }()
@@ -384,14 +419,6 @@ func (e *Executor) readFromStreams(stdoutReadPipe io.Reader, stdoutHandlerWriteP
384419 errorsCount := 0
385420 for {
386421 n , err := stdoutReadPipe .Read (buf )
387- if err != nil && err != io .EOF {
388- logger .DebugF ("Error reading from stdout: %s\n " , err )
389- errorsCount ++
390- if errorsCount > 1000 {
391- panic (fmt .Errorf ("readFromStreams: too many errors, last error %v" , err ))
392- }
393- continue
394- }
395422
396423 m := 0
397424 if ! matchersDone {
@@ -429,9 +456,18 @@ func (e *Executor) readFromStreams(stdoutReadPipe io.Reader, stdoutHandlerWriteP
429456 _ , _ = stdoutHandlerWritePipe .Write (buf [m :n ])
430457 }
431458
432- if err == io .EOF {
433- logger .DebugF ("readFromStreams: EOF" )
434- break
459+ if err != nil {
460+ if errors .Is (err , io .EOF ) || errors .Is (err , os .ErrClosed ) {
461+ logger .DebugF ("readFromStreams: EOF" )
462+ break
463+ }
464+
465+ logger .DebugF ("Error reading from stdout: %s\n " , err )
466+ errorsCount ++
467+ if errorsCount > 1000 {
468+ panic (fmt .Errorf ("readFromStreams: too many errors, last error %v" , err ))
469+ }
470+ continue
435471 }
436472 }
437473}
@@ -468,9 +504,9 @@ func (e *Executor) Start() error {
468504 if err != nil {
469505 return err
470506 }
471- e .started = true
472507
473508 e .ProcessWait ()
509+ e .started .Store (true )
474510
475511 logger .DebugF ("Register stoppable: '%s'" , e .cmd .String ())
476512 e .Session .RegisterStoppable (e )
@@ -482,34 +518,31 @@ func (e *Executor) ProcessWait() {
482518 waitErrCh := make (chan error , 1 )
483519 e .waitCh = make (chan struct {}, 1 )
484520 e .stopCh = make (chan struct {}, 1 )
521+ e .doneCh = make (chan struct {})
485522
486523 // wait for process in go routine
487524 go func () {
488525 waitErrCh <- e .cmd .Wait ()
489526 }()
490527
491- go func () {
492- if e .timeout > 0 {
493- time .Sleep (e .timeout )
494- if e .stopCh != nil {
495- e .stopCh <- struct {}{}
496- }
497- }
498- }()
528+ go e .waitForTimeout ()
499529
500530 // watch for wait or stop
501531 go func () {
502532 defer func () {
533+ e .closeWritePipes ()
534+ close (e .doneCh )
503535 close (e .waitCh )
504536 close (waitErrCh )
505537 }()
506538 // Wait until Stop() is called or/and Wait() is returning.
507539 for {
508540 select {
509541 case err := <- waitErrCh :
510- if e .stop {
542+ if e .stop . Load () {
511543 // Ignore error if Stop() was called.
512544 // close(e.waitCh)
545+ e .killProcessGroup ()
513546 return
514547 }
515548 e .setWaitError (err )
@@ -519,26 +552,45 @@ func (e *Executor) ProcessWait() {
519552 // close(e.waitCh)
520553 return
521554 case <- e .stopCh :
522- e .stop = true
523- // Prevent next readings from the closed channel.
524- e .stopCh = nil
525555 // The usual e.cmd.Process.Kill() is not working for the process
526556 // started with the new process group (Setpgid: true).
527557 // Negative pid number is used to send a signal to all processes in the group.
528- err := syscall .Kill (- e .cmd .Process .Pid , syscall .SIGKILL )
529- if err != nil {
530- e .killError = err
531- }
558+ e .stop .Store (true )
559+ e .killProcessGroup ()
560+ e .forceClosePipes ()
532561 }
533562 }
534563 }()
535564}
536565
537- func (e * Executor ) closePipes () {
566+ func (e * Executor ) waitForTimeout () {
567+ if e .timeout <= 0 {
568+ return
569+ }
570+
571+ timer := time .NewTimer (e .timeout )
572+ defer timer .Stop ()
573+
574+ select {
575+ case <- timer .C :
576+ e .signalStop ()
577+ case <- e .doneCh :
578+ return
579+ }
580+ }
581+
582+ func (e * Executor ) killProcessGroup () {
583+ err := syscall .Kill (- e .cmd .Process .Pid , syscall .SIGKILL )
584+ if err != nil {
585+ e .killError = err
586+ }
587+ }
588+
589+ func (e * Executor ) closeWritePipes () {
538590 logger := e .settings .Logger ()
539591
540- logger .DebugF ("Starting close piped " )
541- defer logger .DebugF ("Stop close piped " )
592+ logger .DebugF ("Starting close write pipes " )
593+ defer logger .DebugF ("Stop close write pipes " )
542594
543595 e .pipesMutex .Lock ()
544596 defer e .pipesMutex .Unlock ()
@@ -560,30 +612,80 @@ func (e *Executor) closePipes() {
560612 }
561613}
562614
615+ func (e * Executor ) forceClosePipes () {
616+ e .closeWritePipes ()
617+
618+ logger := e .settings .Logger ()
619+
620+ logger .DebugF ("Starting force close pipes" )
621+ defer logger .DebugF ("Stop force close pipes" )
622+
623+ e .closeStdoutReadPipe ()
624+ e .closeStderrReadPipe ()
625+ e .closeStdoutHandlerReadPipe ()
626+ e .closeStderrHandlerReadPipe ()
627+ }
628+
629+ func (e * Executor ) closeStdoutReadPipe () {
630+ e .closePipeFile (& e .stdoutReadPipe , "stdout read pipe" )
631+ }
632+
633+ func (e * Executor ) closeStderrReadPipe () {
634+ e .closePipeFile (& e .stderrReadPipe , "stderr read pipe" )
635+ }
636+
637+ func (e * Executor ) closeStdoutHandlerReadPipe () {
638+ e .closePipeFile (& e .stdoutHandlerReadPipe , "stdout handler read pipe" )
639+ }
640+
641+ func (e * Executor ) closeStderrHandlerReadPipe () {
642+ e .closePipeFile (& e .stderrHandlerReadPipe , "stderr handler read pipe" )
643+ }
644+
645+ func (e * Executor ) closePipeFile (pipe * * os.File , name string ) {
646+ e .pipesMutex .Lock ()
647+ defer e .pipesMutex .Unlock ()
648+
649+ if * pipe != nil {
650+ err := (* pipe ).Close ()
651+ if err != nil {
652+ e .settings .Logger ().DebugF ("Cannot close %s: %v" , name , err )
653+ }
654+ * pipe = nil
655+ }
656+ }
657+
563658func (e * Executor ) Stop () {
564659 logger := e .settings .Logger ()
565660
566- if e .stop {
567- logger .DebugF ("Stop '%s': already stopped" , e . cmd . String () )
661+ if e .cmd == nil {
662+ logger .DebugF ("Possible BUG: Call Executor.Stop with Cmd==nil" )
568663 return
569664 }
570- if ! e .started {
665+ if ! e .started . Load () {
571666 logger .DebugF ("Stop '%s': not started yet" , e .cmd .String ())
572667 return
573668 }
574- if e .cmd == nil {
575- logger .DebugF ("Possible BUG: Call Executor.Stop with Cmd==nil" )
576- return
577- }
578669
579- e .stop = true
580- logger .DebugF ("Stop '%s'" , e .cmd .String ())
581- if e .stopCh != nil {
582- close (e .stopCh )
670+ if e .stop .CompareAndSwap (false , true ) {
671+ logger .DebugF ("Stop '%s'" , e .cmd .String ())
672+ e .signalStop ()
673+ } else {
674+ logger .DebugF ("Stop '%s': already stopping" , e .cmd .String ())
583675 }
676+
677+ e .forceClosePipes ()
584678 <- e .waitCh
585679 logger .DebugF ("Stopped '%s': %d" , e .cmd .String (), e .cmd .ProcessState .ExitCode ())
586- e .closePipes ()
680+ }
681+
682+ func (e * Executor ) signalStop () {
683+ e .stopOnce .Do (func () {
684+ select {
685+ case e .stopCh <- struct {}{}:
686+ default :
687+ }
688+ })
587689}
588690
589691// Run executes a command and blocks until it is finished or stopped.
@@ -597,7 +699,7 @@ func (e *Executor) Run(_ context.Context) error {
597699
598700 <- e .waitCh
599701
600- e .closePipes ()
702+ e .closeWritePipes ()
601703
602704 return e .WaitError ()
603705}
0 commit comments