@@ -13,6 +13,8 @@ package stream
1313import (
1414 "context"
1515 "reflect"
16+
17+ . "go.structs.dev/gen"
1618)
1719
1820// 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) {
131133 return
132134 }
133135
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 ])
144167 }
145168 }
146169
147170 }
148171}
149172
150173// 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.
153175//
154176// NOTE: Execute the Distribute function in a goroutine if parallel execution is
155177// 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) {
170192 return
171193 }
172194
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 {
185197 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 ),
188201 })
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 )
191208 }
192209
193210 }
0 commit comments