github.com/cloudbase/juju-core@v0.0.0-20140504232958-a7271ac7912f/utils/parallel/parallel.go (about)

     1  // Copyright 2013 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  // The parallel package provides a way of running functions concurrently while
     5  // limiting the maximum number running at once.
     6  package parallel
     7  
     8  import (
     9  	"fmt"
    10  	"sync"
    11  )
    12  
    13  // Run represents a number of functions running concurrently.
    14  type Run struct {
    15  	limiter chan struct{}
    16  	done    chan error
    17  	err     chan error
    18  	wg      sync.WaitGroup
    19  }
    20  
    21  // Errors holds any errors encountered during the parallel run.
    22  type Errors []error
    23  
    24  func (errs Errors) Error() string {
    25  	switch len(errs) {
    26  	case 0:
    27  		return "no error"
    28  	case 1:
    29  		return errs[0].Error()
    30  	}
    31  	return fmt.Sprintf("%s (and %d more)", errs[0].Error(), len(errs)-1)
    32  }
    33  
    34  // NewRun returns a new parallel instance.  It will run up to maxParallel
    35  // functions concurrently.
    36  func NewRun(maxParallel int) *Run {
    37  	if maxParallel < 1 {
    38  		panic("parameter maxParallel must be >= 1")
    39  	}
    40  	parallelRun := &Run{
    41  		limiter: make(chan struct{}, maxParallel),
    42  		done:    make(chan error),
    43  		err:     make(chan error),
    44  	}
    45  	go func() {
    46  		var errs Errors
    47  		for e := range parallelRun.done {
    48  			errs = append(errs, e)
    49  		}
    50  		// TODO(rog) sort errors by original order of Do request?
    51  		if len(errs) > 0 {
    52  			parallelRun.err <- errs
    53  		} else {
    54  			parallelRun.err <- nil
    55  		}
    56  	}()
    57  	return parallelRun
    58  }
    59  
    60  // Do requests that r run f concurrently.  If there are already the maximum
    61  // number of functions running concurrently, it will block until one of them
    62  // has completed. Do may itself be called concurrently.
    63  func (parallelRun *Run) Do(f func() error) {
    64  	parallelRun.limiter <- struct{}{}
    65  	parallelRun.wg.Add(1)
    66  	go func() {
    67  		defer func() {
    68  			parallelRun.wg.Done()
    69  			<-parallelRun.limiter
    70  		}()
    71  		if err := f(); err != nil {
    72  			parallelRun.done <- err
    73  		}
    74  	}()
    75  }
    76  
    77  // Wait marks the parallel instance as complete and waits for all the functions
    78  // to complete.  If any errors were encountered, it returns an Errors value
    79  // describing all the errors in arbitrary order.
    80  func (parallelRun *Run) Wait() error {
    81  	parallelRun.wg.Wait()
    82  	close(parallelRun.done)
    83  	return <-parallelRun.err
    84  }