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  }