Parallelize runs the jobs in parallel. A max of Parallelism jobs will be run at once. Returns the combined error from the jobs.
(ctx context.Context, jobs []func(context.Context) error, options ...ParallelizeOption)
| 53 | // A max of Parallelism jobs will be run at once. |
| 54 | // Returns the combined error from the jobs. |
| 55 | func Parallelize(ctx context.Context, jobs []func(context.Context) error, options ...ParallelizeOption) error { |
| 56 | parallelizeOptions := newParallelizeOptions() |
| 57 | for _, option := range options { |
| 58 | option(parallelizeOptions) |
| 59 | } |
| 60 | switch len(jobs) { |
| 61 | case 0: |
| 62 | return nil |
| 63 | case 1: |
| 64 | return jobs[0](ctx) |
| 65 | } |
| 66 | multiplier := max(parallelizeOptions.multiplier, 1) |
| 67 | var cancel context.CancelFunc |
| 68 | if parallelizeOptions.cancelOnFailure { |
| 69 | ctx, cancel = context.WithCancel(ctx) |
| 70 | defer cancel() |
| 71 | } |
| 72 | semaphoreC := make(chan struct{}, Parallelism()*multiplier) |
| 73 | var errs []error |
| 74 | var lock sync.Mutex |
| 75 | addError := func(err error) { |
| 76 | lock.Lock() |
| 77 | errs = append(errs, err) |
| 78 | lock.Unlock() |
| 79 | } |
| 80 | var wg sync.WaitGroup |
| 81 | var stop bool |
| 82 | for _, job := range jobs { |
| 83 | if stop { |
| 84 | break |
| 85 | } |
| 86 | // We always want context cancellation/deadline expiration to take |
| 87 | // precedence over the semaphore unblocking, but select statements choose |
| 88 | // among the unblocked non-default cases pseudorandomly. To correctly |
| 89 | // enforce precedence, use a similar pattern to the check-lock-check |
| 90 | // pattern common with sync.RWMutex: check the context twice, and only do |
| 91 | // the semaphore-protected work in the innermost default case. |
| 92 | select { |
| 93 | case <-ctx.Done(): |
| 94 | stop = true |
| 95 | addError(ctx.Err()) |
| 96 | case semaphoreC <- struct{}{}: |
| 97 | select { |
| 98 | case <-ctx.Done(): |
| 99 | stop = true |
| 100 | addError(ctx.Err()) |
| 101 | default: |
| 102 | job := job |
| 103 | wg.Go(func() { |
| 104 | if err := job(ctx); err != nil { |
| 105 | addError(err) |
| 106 | if cancel != nil { |
| 107 | cancel() |
| 108 | } |
| 109 | } |
| 110 | // This will never block. |
| 111 | <-semaphoreC |
| 112 | }) |
searching dependent graphs…