github.com/mem/u-root@v2.0.1-0.20181004165302-9b18b4636a33+incompatible/cmds/elvish/eval/builtin_fn_flow.go (about) 1 package eval 2 3 import ( 4 "errors" 5 "sync" 6 7 "github.com/u-root/u-root/cmds/elvish/util" 8 ) 9 10 // Flow control. 11 12 func init() { 13 addBuiltinFns(map[string]interface{}{ 14 "run-parallel": runParallel, 15 // Exception and control 16 "fail": fail, 17 "multi-error": multiErrorFn, 18 "return": returnFn, 19 "break": breakFn, 20 "continue": continueFn, 21 // Iterations. 22 "each": each, 23 "peach": peach, 24 }) 25 } 26 27 func runParallel(fm *Frame, functions ...Callable) error { 28 var waitg sync.WaitGroup 29 waitg.Add(len(functions)) 30 exceptions := make([]*Exception, len(functions)) 31 for i, function := range functions { 32 go func(fm2 *Frame, function Callable, exception **Exception) { 33 err := fm2.Call(function, NoArgs, NoOpts) 34 if err != nil { 35 *exception = err.(*Exception) 36 } 37 waitg.Done() 38 }(fm.fork("[run-parallel function]"), function, &exceptions[i]) 39 } 40 41 waitg.Wait() 42 return ComposeExceptionsFromPipeline(exceptions) 43 } 44 45 // each takes a single closure and applies it to all input values. 46 func each(fm *Frame, f Callable, inputs Inputs) error { 47 broken := false 48 var err error 49 inputs(func(v interface{}) { 50 if broken { 51 return 52 } 53 newFm := fm.fork("closure of each") 54 newFm.ports[0] = DevNullClosedChan 55 ex := newFm.Call(f, []interface{}{v}, NoOpts) 56 newFm.Close() 57 58 if ex != nil { 59 switch ex.(*Exception).Cause { 60 case nil, Continue: 61 // nop 62 case Break: 63 broken = true 64 default: 65 broken = true 66 err = ex 67 } 68 } 69 }) 70 return err 71 } 72 73 // peach takes a single closure and applies it to all input values in parallel. 74 func peach(fm *Frame, f Callable, inputs Inputs) error { 75 var w sync.WaitGroup 76 broken := false 77 var err error 78 inputs(func(v interface{}) { 79 if broken || err != nil { 80 return 81 } 82 w.Add(1) 83 go func() { 84 newFm := fm.fork("closure of peach") 85 newFm.ports[0] = DevNullClosedChan 86 ex := newFm.Call(f, []interface{}{v}, NoOpts) 87 newFm.Close() 88 89 if ex != nil { 90 switch ex.(*Exception).Cause { 91 case nil, Continue: 92 // nop 93 case Break: 94 broken = true 95 default: 96 broken = true 97 err = util.Errors(err, ex) 98 } 99 } 100 w.Done() 101 }() 102 }) 103 w.Wait() 104 return err 105 } 106 107 func fail(msg string) error { 108 return errors.New(msg) 109 } 110 111 func multiErrorFn(excs ...*Exception) error { 112 return PipelineError{excs} 113 } 114 115 func returnFn() error { 116 return Return 117 } 118 119 func breakFn() error { 120 return Break 121 } 122 123 func continueFn() error { 124 return Continue 125 }