go-hep.org/x/hep@v0.38.1/fwk/utils/parallel/parallel.go (about)

     1  // Copyright ©2017 The go-hep Authors. All rights reserved.
     2  // Use of this source code is governed by a BSD-style
     3  // license that can be found in the LICENSE file.
     4  
     5  // the following has been extracted from Rog Peppe's parallel package
     6  // published under the new BSD-3 clause license
     7  //   http://opensource.org/licenses/BSD-3-Clause
     8  //
     9  // original sources are available at:
    10  //   code.google.com/p/rog-go/parallel
    11  
    12  // The parallel package provides a way of running functions
    13  // concurrently while limiting the maximum number
    14  // running at once.
    15  package parallel // import "go-hep.org/x/hep/fwk/utils/parallel"
    16  
    17  import (
    18  	"fmt"
    19  	"sync"
    20  )
    21  
    22  // Run represents a number of functions running concurrently.
    23  type Run struct {
    24  	limiter chan struct{}
    25  	done    chan error
    26  	err     chan error
    27  	wg      sync.WaitGroup
    28  }
    29  
    30  // Errors holds any errors encountered during
    31  // the parallel run.
    32  type Errors []error
    33  
    34  func (errs Errors) Error() string {
    35  	switch len(errs) {
    36  	case 0:
    37  		return "no error"
    38  	case 1:
    39  		return errs[0].Error()
    40  	}
    41  	return fmt.Sprintf("%s (and %d more)", errs[0].Error(), len(errs)-1)
    42  }
    43  
    44  // NewRun returns a new parallel instance.  It will run up to maxPar
    45  // functions concurrently.
    46  func NewRun(maxPar int) *Run {
    47  	r := &Run{
    48  		limiter: make(chan struct{}, maxPar),
    49  		done:    make(chan error),
    50  		err:     make(chan error),
    51  	}
    52  	go func() {
    53  		var errs Errors
    54  		for e := range r.done {
    55  			errs = append(errs, e)
    56  		}
    57  		// TODO sort errors by original order of Do request?
    58  		if len(errs) > 0 {
    59  			r.err <- errs
    60  		} else {
    61  			r.err <- nil
    62  		}
    63  	}()
    64  	return r
    65  }
    66  
    67  // Do requests that r run f concurrently.  If there are already the maximum
    68  // number of functions running concurrently, it will block until one of
    69  // them has completed. Do may itself be called concurrently.
    70  func (r *Run) Do(f func() error) {
    71  	r.limiter <- struct{}{}
    72  	r.wg.Add(1)
    73  	go func() {
    74  		defer func() {
    75  			r.wg.Done()
    76  			<-r.limiter
    77  		}()
    78  		if err := f(); err != nil {
    79  			r.done <- err
    80  		}
    81  	}()
    82  }
    83  
    84  // Wait marks the parallel instance as complete and waits for all the
    85  // functions to complete.  If any errors were encountered, it returns an
    86  // Errors value describing all the errors in arbitrary order.
    87  func (r *Run) Wait() error {
    88  	r.wg.Wait()
    89  	close(r.done)
    90  	return <-r.err
    91  }