gorgonia.org/gorgonia@v0.9.17/x/vm/pubsub.go (about) 1 package xvm 2 3 import ( 4 "context" 5 "sync" 6 7 "gorgonia.org/gorgonia" 8 ) 9 10 type publisher struct { 11 id int64 12 publisher <-chan gorgonia.Value 13 subscribers []chan<- gorgonia.Value 14 } 15 16 type subscriber struct { 17 id int64 18 publishers []<-chan gorgonia.Value 19 subscriber chan<- ioValue 20 } 21 22 type pubsub struct { 23 publishers []*publisher 24 subscribers []*subscriber 25 } 26 27 func (p *pubsub) run(ctx context.Context) (context.CancelFunc, *sync.WaitGroup) { 28 var wg sync.WaitGroup 29 ctx, cancel := context.WithCancel(ctx) 30 for i := range p.publishers { 31 wg.Add(1) 32 go broadcast(ctx, &wg, p.publishers[i].publisher, p.publishers[i].subscribers...) 33 } 34 for i := range p.subscribers { 35 wg.Add(1) 36 go merge(ctx, &wg, p.subscribers[i].subscriber, p.subscribers[i].publishers...) 37 } 38 return cancel, &wg 39 } 40 41 func merge(ctx context.Context, globalWG *sync.WaitGroup, out chan<- ioValue, cs ...<-chan gorgonia.Value) { 42 defer globalWG.Done() 43 44 // Start an output goroutine for each input channel in cs. output 45 // copies values from c to out until c or done is closed, then calls 46 output := func(ctx context.Context, c <-chan gorgonia.Value, pos int) { 47 defer globalWG.Done() 48 for { 49 select { 50 case n := <-c: 51 select { 52 case out <- ioValue{ 53 pos: pos, 54 v: n, 55 }: 56 case <-ctx.Done(): 57 return 58 } 59 case <-ctx.Done(): 60 return 61 } 62 } 63 } 64 for i, c := range cs { 65 globalWG.Add(1) 66 go output(ctx, c, i) 67 } 68 } 69 70 func broadcast(ctx context.Context, globalWG *sync.WaitGroup, ch <-chan gorgonia.Value, cs ...chan<- gorgonia.Value) { 71 defer globalWG.Done() 72 for { 73 select { 74 case msg := <-ch: 75 for _, c := range cs { 76 select { 77 case c <- msg: 78 case <-ctx.Done(): 79 return 80 } 81 } 82 case <-ctx.Done(): 83 return 84 } 85 } 86 }