github.com/sentienttechnologies/studio-go-runner@v0.0.0-20201118202441-6d21f2ced8ee/internal/runner/statebroadcast.go (about) 1 // Copyright 2018-2020 (c) Cognizant Digital Business, Evolutionary AI. All rights reserved. Issued under the Apache 2.0 License. 2 3 package runner 4 5 import ( 6 "context" 7 "sync" 8 "time" 9 10 "github.com/rs/xid" 11 12 "github.com/jjeffery/kv" // MIT License 13 ) 14 15 // This file contains the implementation of a channel fan-out 16 // based on subscriptions. 17 // 18 19 // Listeners is used to handle the broadcasting of cluster events when Kubernetes is 20 // being used 21 type Listeners struct { 22 Master chan K8sStateUpdate 23 listeners map[xid.ID]chan<- K8sStateUpdate 24 sync.Mutex 25 } 26 27 // NewStateBroadcast is used to instantiate a Kubernetes event broadcaster 28 func NewStateBroadcast(ctx context.Context, errorC chan<- kv.Error) (l *Listeners) { 29 l = &Listeners{ 30 Master: make(chan K8sStateUpdate, 1), 31 listeners: map[xid.ID]chan<- K8sStateUpdate{}, 32 } 33 34 go l.run(ctx, errorC) 35 36 return l 37 } 38 39 func (l *Listeners) run(ctx context.Context, errorC chan<- kv.Error) { 40 for { 41 select { 42 case <-ctx.Done(): 43 return 44 case state := <-l.Master: 45 46 clients := make([]chan<- K8sStateUpdate, 0, len(l.listeners)) 47 48 // Make a consistent copy of all the channels that the update will be sent down 49 // so that we retain the values at this moment in time 50 if len(l.listeners) != 0 { 51 l.Lock() 52 for _, v := range l.listeners { 53 clients = append(clients, v) 54 } 55 l.Unlock() 56 } 57 58 for _, c := range clients { 59 func() { 60 defer func() { 61 // There is a window of time in which the delete for a listener occurs 62 // between copying the collection of listeners and someone else 63 // deleting the listen and this function then doing a send 64 recover() 65 }() 66 select { 67 case c <- state: 68 case <-time.After(500 * time.Millisecond): 69 } 70 }() 71 } 72 } 73 } 74 } 75 76 // Add is used when a running thread wishes to add a channel to the broadcaster 77 // on which Kubernetes events will be received 78 func (l *Listeners) Add(listen chan<- K8sStateUpdate) (id xid.ID, err kv.Error) { 79 80 id = xid.New() 81 82 l.Lock() 83 l.listeners[id] = listen 84 l.Unlock() 85 86 return id, nil 87 } 88 89 // Delete is used when a running thread wishes to drop a channel from the broadcaster 90 // on which Kubernetes events will be received 91 func (l *Listeners) Delete(id xid.ID) { 92 93 l.Lock() 94 delete(l.listeners, id) 95 l.Unlock() 96 }