github.com/koko1123/flow-go-1@v0.29.6/module/util/util.go (about) 1 package util 2 3 import ( 4 "context" 5 "reflect" 6 "sync" 7 8 "github.com/koko1123/flow-go-1/module" 9 ) 10 11 // AllReady calls Ready on all input components and returns a channel that is 12 // closed when all input components are ready. 13 func AllReady(components ...module.ReadyDoneAware) <-chan struct{} { 14 readyChans := make([]<-chan struct{}, len(components)) 15 16 for i, c := range components { 17 readyChans[i] = c.Ready() 18 } 19 20 return AllClosed(readyChans...) 21 } 22 23 // AllDone calls Done on all input components and returns a channel that is 24 // closed when all input components are done. 25 func AllDone(components ...module.ReadyDoneAware) <-chan struct{} { 26 doneChans := make([]<-chan struct{}, len(components)) 27 28 for i, c := range components { 29 doneChans[i] = c.Done() 30 } 31 32 return AllClosed(doneChans...) 33 } 34 35 // AllClosed returns a channel that is closed when all input channels are closed. 36 func AllClosed(channels ...<-chan struct{}) <-chan struct{} { 37 done := make(chan struct{}) 38 if len(channels) == 0 { 39 close(done) 40 return done 41 } 42 43 var wg sync.WaitGroup 44 45 for _, ch := range channels { 46 wg.Add(1) 47 go func(ch <-chan struct{}) { 48 <-ch 49 wg.Done() 50 }(ch) 51 } 52 53 go func() { 54 wg.Wait() 55 close(done) 56 }() 57 58 return done 59 } 60 61 // WaitClosed waits for either a signal/close on the channel or for the context to be cancelled 62 // Returns nil if the channel was signalled/closed before returning, otherwise, it returns the context 63 // error. 64 // 65 // This handles the corner case where the context is cancelled at the same time that the channel 66 // is closed, and the Done case was selected. 67 // This is intended for situations where ignoring a signal can cause safety issues. 68 func WaitClosed(ctx context.Context, ch <-chan struct{}) error { 69 select { 70 case <-ctx.Done(): 71 select { 72 case <-ch: 73 return nil 74 default: 75 } 76 return ctx.Err() 77 case <-ch: 78 return nil 79 } 80 } 81 82 // CheckClosed checks if the provided channel has a signal or was closed. 83 // Returns true if the channel was signaled/closed, otherwise, returns false. 84 // 85 // This is intended to reduce boilerplate code when multiple channel checks are required because 86 // missed signals could cause safety issues. 87 func CheckClosed(done <-chan struct{}) bool { 88 select { 89 case <-done: 90 return true 91 default: 92 return false 93 } 94 } 95 96 // MergeChannels merges a list of channels into a single channel 97 func MergeChannels(channels interface{}) interface{} { 98 sliceType := reflect.TypeOf(channels) 99 if sliceType.Kind() != reflect.Slice && sliceType.Kind() != reflect.Array { 100 panic("argument must be an array or slice") 101 } 102 chanType := sliceType.Elem() 103 if chanType.ChanDir() == reflect.SendDir { 104 panic("channels cannot be send-only") 105 } 106 c := reflect.ValueOf(channels) 107 var cases []reflect.SelectCase 108 for i := 0; i < c.Len(); i++ { 109 cases = append(cases, reflect.SelectCase{ 110 Dir: reflect.SelectRecv, 111 Chan: c.Index(i), 112 }) 113 } 114 elemType := chanType.Elem() 115 out := reflect.MakeChan(reflect.ChanOf(reflect.BothDir, elemType), 0) 116 go func() { 117 for len(cases) > 0 { 118 i, v, ok := reflect.Select(cases) 119 if !ok { 120 lastIndex := len(cases) - 1 121 cases[i], cases[lastIndex] = cases[lastIndex], cases[i] 122 cases = cases[:lastIndex] 123 continue 124 } 125 out.Send(v) 126 } 127 out.Close() 128 }() 129 return out.Convert(reflect.ChanOf(reflect.RecvDir, elemType)).Interface() 130 } 131 132 // WaitError waits for either an error on the error channel or the done channel to close 133 // Returns an error if one is received on the error channel, otherwise it returns nil 134 // 135 // This handles a race condition where the done channel could have been closed as a result of an 136 // irrecoverable error being thrown, so that when the scheduler yields control back to this 137 // goroutine, both channels are available to read from. If the done case happens to be chosen 138 // at random to proceed instead of the error case, then we would return without error which could 139 // result in unsafe continuation. 140 func WaitError(errChan <-chan error, done <-chan struct{}) error { 141 select { 142 case err := <-errChan: 143 return err 144 case <-done: 145 select { 146 case err := <-errChan: 147 return err 148 default: 149 } 150 return nil 151 } 152 } 153 154 // DetypeSlice converts a typed slice containing any kind of elements into an 155 // untyped []any type, in effect removing the element type information from the slice. 156 // It is useful for passing data into structpb.NewValue, which accepts []any but not 157 // []T for any specific type T. 158 func DetypeSlice[T any](typedSlice []T) []any { 159 untypedSlice := make([]any, len(typedSlice)) 160 for i, t := range typedSlice { 161 untypedSlice[i] = t 162 } 163 return untypedSlice 164 }