github.com/pf-qiu/concourse/v6@v6.7.3-0.20201207032516-1f455d73275f/atc/exec/aggregate.go (about)

     1  package exec
     2  
     3  import (
     4  	"context"
     5  
     6  	"github.com/pf-qiu/concourse/v6/atc/util"
     7  	"github.com/hashicorp/go-multierror"
     8  )
     9  
    10  // AggregateStep is a step of steps to run in parallel.
    11  type AggregateStep []Step
    12  
    13  // Run executes all steps in parallel. It will indicate that it's ready when
    14  // all of its steps are ready, and propagate any signal received to all running
    15  // steps.
    16  //
    17  // It will wait for all steps to exit, even if one step fails or errors. After
    18  // all steps finish, their errors (if any) will be aggregated and returned as a
    19  // single error.
    20  func (step AggregateStep) Run(ctx context.Context, state RunState) (bool, error) {
    21  	oks := make(chan bool, len(step))
    22  	errs := make(chan error, len(step))
    23  
    24  	for _, s := range step {
    25  		s := s
    26  		go func() {
    27  			defer func() {
    28  				err := util.DumpPanic(recover(), "aggregate step")
    29  				if err != nil {
    30  					errs <- err
    31  				}
    32  			}()
    33  
    34  			ok, err := s.Run(ctx, state)
    35  			oks <- ok
    36  			errs <- err
    37  		}()
    38  	}
    39  
    40  	var result error
    41  	for i := 0; i < len(step); i++ {
    42  		err := <-errs
    43  		if err != nil {
    44  			result = multierror.Append(result, err)
    45  		}
    46  	}
    47  
    48  	if ctx.Err() != nil {
    49  		return false, ctx.Err()
    50  	}
    51  
    52  	if result != nil {
    53  		return false, result
    54  	}
    55  
    56  	ok := true
    57  	for i := 0; i < len(step); i++ {
    58  		ok = ok && <-oks
    59  	}
    60  
    61  	return ok, nil
    62  }