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 }