github.com/devops-filetransfer/sshego@v7.0.4+incompatible/buzz.go (about) 1 package sshego 2 3 import ( 4 "fmt" 5 "math/rand" 6 "sync" 7 "time" 8 9 ssh "github.com/glycerine/sshego/xendor/github.com/glycerine/xcryptossh" 10 ) 11 12 // UHPTower is an 1:M non-blocking value-loadable channel. 13 // 14 // Each subscriber gets their own private channel, and it 15 // will get a copy of whatever is sent to UHPTower. 16 // 17 // Sends don't block, as subscribers are given buffered channels. 18 // 19 type UHPTower struct { 20 subs []chan *UHP 21 mut sync.Mutex 22 closed bool 23 24 halt *ssh.Halter 25 } 26 27 // NewUHPTower makes a new UHPTower. 28 func NewUHPTower(halt *ssh.Halter) *UHPTower { 29 if halt == nil { 30 halt = ssh.NewHalter() 31 } 32 tower := &UHPTower{ 33 halt: halt, 34 } 35 return tower 36 } 37 38 // Subscribe if given notify (notify is optional) 39 // will return notify and notify will receive 40 // all Broadcast values. If notify is nil, Subscribe 41 // will allocate a new channel and return that. 42 // When provided, notify should typically be a size 1 buffered 43 // chan. If other sizes of chan are used, be sure 44 // to service reads in a timely manner, or we 45 // will panic since Subscribe is meant to be 46 // non-blocking or minimally blocking for a very 47 // short time. Note that buffer size 1 channels 48 // are intended for lossy status: where if new 49 // status arrives before the old is read, it 50 // is desirable to discard the old and update 51 // to the new status value. To get non-lossy 52 // behavior, use an unbuffered notify or 53 // a buffer with size > 1. In both those 54 // cases, as above, you must arrange to 55 // service the channel promptly. 56 func (b *UHPTower) Subscribe(notify chan *UHP) (ch chan *UHP) { 57 pp("UHPTower %p sees Subscribe, notify=%p", b, notify) 58 59 b.mut.Lock() 60 if notify == nil { 61 ch = make(chan *UHP, 1) 62 } else { 63 ch = notify 64 } 65 b.subs = append(b.subs, ch) 66 b.mut.Unlock() 67 return ch 68 } 69 70 func (b *UHPTower) Unsub(x chan *UHP) { 71 b.mut.Lock() 72 defer b.mut.Unlock() 73 74 // find it 75 k := -1 76 for i := range b.subs { 77 if b.subs[i] == x { 78 k = i 79 break 80 } 81 } 82 if k == -1 { 83 // not found 84 return 85 } 86 // found. delete it 87 b.subs = append(b.subs[:k], b.subs[k+1:]...) 88 } 89 90 var ErrClosed = fmt.Errorf("channel closed") 91 92 // Broadcast sends a copy of val to all subs. 93 // Any old unreceived values are purged 94 // from the receive queues before sending. 95 // Since the receivers are all buffered 96 // channels, Broadcast should never block 97 // waiting on a receiver. 98 // 99 // Any subscriber who subscribes after the Broadcast will not 100 // receive the Broadcast value, as it is not 101 // stored internally. 102 // 103 func (b *UHPTower) Broadcast(val *UHP) error { 104 b.mut.Lock() 105 defer b.mut.Unlock() 106 if b.closed { 107 return ErrClosed 108 } 109 for i := range b.subs { 110 if cap(b.subs[i]) == 1 { 111 // clear any old, so there is 112 // space for the new without blocking. 113 select { 114 case <-b.subs[i]: 115 default: 116 } 117 } 118 119 // apply the new 120 select { 121 case b.subs[i] <- val: 122 case <-b.halt.ReqStopChan(): 123 return b.internalClose() 124 case <-time.After(10 * time.Second): 125 pp("UHPTower.Broadcast() blocked: could not send for 10 seconds.") 126 // return or panic? 127 panic("big problem: Broadcast blocked for 10 seconds! Prefer buffered channel of size 1 for Tower subscribe channels.") 128 } 129 } 130 return nil 131 } 132 133 func (b *UHPTower) Signal(val *UHP) error { 134 b.mut.Lock() 135 defer b.mut.Unlock() 136 if b.closed { 137 return ErrClosed 138 } 139 n := len(b.subs) 140 i := rand.Intn(n) 141 b.subs[i] <- val 142 return nil 143 } 144 145 func (b *UHPTower) Close() (err error) { 146 b.mut.Lock() 147 err = b.internalClose() 148 b.mut.Unlock() 149 return 150 } 151 152 // for internal use only, caller must have locked b.mut 153 func (b *UHPTower) internalClose() error { 154 if b.closed { 155 return ErrClosed 156 } 157 b.closed = true 158 159 for i := range b.subs { 160 close(b.subs[i]) 161 } 162 b.halt.MarkDone() 163 return nil 164 } 165 166 func (b *UHPTower) Clear() { 167 b.mut.Lock() 168 for i := range b.subs { 169 select { 170 case <-b.subs[i]: 171 default: 172 } 173 } 174 b.mut.Unlock() 175 } 176 177 // EmptyUHPChan is helper utility. 178 // It clears everything out of ch. 179 func EmptyUHPChan(ch chan *UHP) { 180 for { 181 select { 182 case <-ch: 183 default: 184 return 185 } 186 } 187 }