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 }