github.com/aacfactory/fns@v1.2.86-0.20240310083819-80d667fc0a17/cmd/generates/processes/process.go (about) 1 /* 2 * Copyright 2023 Wang Min Xiang 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 * 16 */ 17 18 package processes 19 20 import ( 21 "context" 22 "github.com/aacfactory/errors" 23 "time" 24 ) 25 26 var ( 27 ErrAborted = errors.Warning("processes: abort") 28 ) 29 30 func New() *Process { 31 return &Process{ 32 units: 0, 33 steps: make([]*Step, 0, 1), 34 resultCh: make(chan Result, 512), 35 closedCh: make(chan struct{}, 1), 36 cancel: nil, 37 } 38 } 39 40 type ProcessController interface { 41 Steps() (n int64) 42 Units() (n int64) 43 Start(ctx context.Context) (results <-chan Result) 44 Abort(timeout time.Duration) (err error) 45 } 46 47 type Process struct { 48 units int64 49 steps []*Step 50 resultCh chan Result 51 closedCh chan struct{} 52 cancel context.CancelFunc 53 } 54 55 func (p *Process) Add(name string, units ...Unit) { 56 no := int64(len(p.steps) + 1) 57 p.steps = append(p.steps, &Step{ 58 no: no, 59 name: name, 60 num: 0, 61 units: units, 62 resultCh: p.resultCh, 63 }) 64 for _, step := range p.steps { 65 step.num = no 66 } 67 if units != nil { 68 p.units = p.units + int64(len(units)) 69 } 70 } 71 72 func (p *Process) Steps() (n int64) { 73 n = int64(len(p.steps)) 74 return 75 } 76 77 func (p *Process) Units() (n int64) { 78 n = p.units 79 return 80 } 81 82 func (p *Process) Start(ctx context.Context) (results <-chan Result) { 83 ctx, p.cancel = context.WithCancel(ctx) 84 go func(ctx context.Context, p *Process, result chan Result) { 85 for _, step := range p.steps { 86 stop := false 87 select { 88 case <-ctx.Done(): 89 result <- Result{ 90 StepNo: 0, 91 StepNum: 0, 92 StepName: "", 93 UnitNo: 0, 94 UnitNum: 0, 95 Data: nil, 96 Error: ErrAborted.WithCause(ctx.Err()), 97 } 98 stop = true 99 p.closedCh <- struct{}{} 100 break 101 default: 102 err := step.Execute(ctx) 103 if err != nil { 104 stop = true 105 p.closedCh <- struct{}{} 106 } 107 break 108 } 109 if stop { 110 break 111 } 112 } 113 close(result) 114 close(p.closedCh) 115 }(ctx, p, p.resultCh) 116 results = p.resultCh 117 return 118 } 119 120 func (p *Process) Abort(timeout time.Duration) (err error) { 121 if p.cancel == nil { 122 return 123 } 124 p.cancel() 125 select { 126 case <-time.After(timeout): 127 err = errors.Timeout("processes: abort timeout") 128 break 129 case <-p.closedCh: 130 break 131 } 132 return 133 } 134 135 func IsAbortErr(err error) (ok bool) { 136 ok = errors.Wrap(err).Contains(ErrAborted) || errors.Wrap(err).Contains(context.Canceled) 137 return 138 }