@@ -13,6 +13,8 @@ package stream
13
13
import (
14
14
"context"
15
15
"reflect"
16
+
17
+ . "go.structs.dev/gen"
16
18
)
17
19
18
20
// Pipe accepts an incoming data channel and pipes it to the supplied
@@ -131,25 +133,45 @@ func FanOut[T any](ctx context.Context, in <-chan T, out ...chan<- T) {
131
133
return
132
134
}
133
135
134
- for _ , o := range out {
135
- // Closure to catch panic on closed channel write.
136
- // Continue Loop
137
- func () {
138
- select {
139
- case <- ctx .Done ():
140
- return
141
- case o <- v :
142
- }
143
- }()
136
+ // Closure to catch panic on closed channel write.
137
+ selectCases := make ([]reflect.SelectCase , 0 , len (out )+ 1 )
138
+
139
+ // 0 index is context
140
+ selectCases = append (selectCases , reflect.SelectCase {
141
+ Dir : reflect .SelectRecv ,
142
+ Chan : reflect .ValueOf (ctx .Done ()),
143
+ })
144
+
145
+ for _ , outc := range out {
146
+ // Skip nil channels until they are non-nil
147
+ if outc == nil {
148
+ continue
149
+ }
150
+
151
+ selectCases = append (selectCases , reflect.SelectCase {
152
+ Dir : reflect .SelectSend ,
153
+ Chan : reflect .ValueOf (outc ),
154
+ Send : reflect .ValueOf (v ),
155
+ })
156
+ }
157
+
158
+ for len (selectCases ) > 1 {
159
+ chosen , _ , _ := reflect .Select (selectCases )
160
+
161
+ // The context was cancelled.
162
+ if chosen == 0 {
163
+ return
164
+ }
165
+
166
+ selectCases = Exclude (selectCases , selectCases [chosen ])
144
167
}
145
168
}
146
169
147
170
}
148
171
}
149
172
150
173
// Distribute accepts an incoming data channel and distributes the data among
151
- // the supplied outgoing data channels. This distribution is done stochastically
152
- // using the cryptographic random number generator.
174
+ // the supplied outgoing data channels using a dynamic select statement.
153
175
//
154
176
// NOTE: Execute the Distribute function in a goroutine if parallel execution is
155
177
// desired. Cancelling the context or closing the incoming channel is important
@@ -170,24 +192,19 @@ func Distribute[T any](ctx context.Context, in <-chan T, out ...chan<- T) {
170
192
return
171
193
}
172
194
173
- // Closure to catch panic on closed channel write.
174
- func () {
175
- defer recover ()
176
-
177
- selectCases := make ([]reflect.SelectCase , 0 , len (out )+ 1 )
178
- for _ , outc := range out {
179
- selectCases = append (selectCases , reflect.SelectCase {
180
- Dir : reflect .SelectSend ,
181
- Chan : reflect .ValueOf (outc ),
182
- Send : reflect .ValueOf (v ),
183
- })
184
- }
195
+ selectCases := make ([]reflect.SelectCase , 0 , len (out )+ 1 )
196
+ for _ , outc := range out {
185
197
selectCases = append (selectCases , reflect.SelectCase {
186
- Dir : reflect .SelectRecv ,
187
- Chan : reflect .ValueOf (ctx .Done ()),
198
+ Dir : reflect .SelectSend ,
199
+ Chan : reflect .ValueOf (outc ),
200
+ Send : reflect .ValueOf (v ),
188
201
})
189
- _ , _ , _ = reflect .Select (selectCases )
190
- }()
202
+ }
203
+ selectCases = append (selectCases , reflect.SelectCase {
204
+ Dir : reflect .SelectRecv ,
205
+ Chan : reflect .ValueOf (ctx .Done ()),
206
+ })
207
+ _ , _ , _ = reflect .Select (selectCases )
191
208
}
192
209
193
210
}
0 commit comments