github.com/sberex/go-sberex@v1.8.2-0.20181113200658-ed96ac38f7d7/event/feed.go (about) 1 // This file is part of the go-sberex library. The go-sberex library is 2 // free software: you can redistribute it and/or modify it under the terms 3 // of the GNU Lesser General Public License as published by the Free 4 // Software Foundation, either version 3 of the License, or (at your option) 5 // any later version. 6 // 7 // The go-sberex library is distributed in the hope that it will be useful, 8 // but WITHOUT ANY WARRANTY; without even the implied warranty of 9 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser 10 // General Public License <http://www.gnu.org/licenses/> for more details. 11 12 package event 13 14 import ( 15 "errors" 16 "reflect" 17 "sync" 18 ) 19 20 var errBadChannel = errors.New("event: Subscribe argument does not have sendable channel type") 21 22 // Feed implements one-to-many subscriptions where the carrier of events is a channel. 23 // Values sent to a Feed are delivered to all subscribed channels simultaneously. 24 // 25 // Feeds can only be used with a single type. The type is determined by the first Send or 26 // Subscribe operation. Subsequent calls to these methods panic if the type does not 27 // match. 28 // 29 // The zero value is ready to use. 30 type Feed struct { 31 once sync.Once // ensures that init only runs once 32 sendLock chan struct{} // sendLock has a one-element buffer and is empty when held.It protects sendCases. 33 removeSub chan interface{} // interrupts Send 34 sendCases caseList // the active set of select cases used by Send 35 36 // The inbox holds newly subscribed channels until they are added to sendCases. 37 mu sync.Mutex 38 inbox caseList 39 etype reflect.Type 40 closed bool 41 } 42 43 // This is the index of the first actual subscription channel in sendCases. 44 // sendCases[0] is a SelectRecv case for the removeSub channel. 45 const firstSubSendCase = 1 46 47 type feedTypeError struct { 48 got, want reflect.Type 49 op string 50 } 51 52 func (e feedTypeError) Error() string { 53 return "event: wrong type in " + e.op + " got " + e.got.String() + ", want " + e.want.String() 54 } 55 56 func (f *Feed) init() { 57 f.removeSub = make(chan interface{}) 58 f.sendLock = make(chan struct{}, 1) 59 f.sendLock <- struct{}{} 60 f.sendCases = caseList{{Chan: reflect.ValueOf(f.removeSub), Dir: reflect.SelectRecv}} 61 } 62 63 // Subscribe adds a channel to the feed. Future sends will be delivered on the channel 64 // until the subscription is canceled. All channels added must have the same element type. 65 // 66 // The channel should have ample buffer space to avoid blocking other subscribers. 67 // Slow subscribers are not dropped. 68 func (f *Feed) Subscribe(channel interface{}) Subscription { 69 f.once.Do(f.init) 70 71 chanval := reflect.ValueOf(channel) 72 chantyp := chanval.Type() 73 if chantyp.Kind() != reflect.Chan || chantyp.ChanDir()&reflect.SendDir == 0 { 74 panic(errBadChannel) 75 } 76 sub := &feedSub{feed: f, channel: chanval, err: make(chan error, 1)} 77 78 f.mu.Lock() 79 defer f.mu.Unlock() 80 if !f.typecheck(chantyp.Elem()) { 81 panic(feedTypeError{op: "Subscribe", got: chantyp, want: reflect.ChanOf(reflect.SendDir, f.etype)}) 82 } 83 // Add the select case to the inbox. 84 // The next Send will add it to f.sendCases. 85 cas := reflect.SelectCase{Dir: reflect.SelectSend, Chan: chanval} 86 f.inbox = append(f.inbox, cas) 87 return sub 88 } 89 90 // note: callers must hold f.mu 91 func (f *Feed) typecheck(typ reflect.Type) bool { 92 if f.etype == nil { 93 f.etype = typ 94 return true 95 } 96 return f.etype == typ 97 } 98 99 func (f *Feed) remove(sub *feedSub) { 100 // Delete from inbox first, which covers channels 101 // that have not been added to f.sendCases yet. 102 ch := sub.channel.Interface() 103 f.mu.Lock() 104 index := f.inbox.find(ch) 105 if index != -1 { 106 f.inbox = f.inbox.delete(index) 107 f.mu.Unlock() 108 return 109 } 110 f.mu.Unlock() 111 112 select { 113 case f.removeSub <- ch: 114 // Send will remove the channel from f.sendCases. 115 case <-f.sendLock: 116 // No Send is in progress, delete the channel now that we have the send lock. 117 f.sendCases = f.sendCases.delete(f.sendCases.find(ch)) 118 f.sendLock <- struct{}{} 119 } 120 } 121 122 // Send delivers to all subscribed channels simultaneously. 123 // It returns the number of subscribers that the value was sent to. 124 func (f *Feed) Send(value interface{}) (nsent int) { 125 rvalue := reflect.ValueOf(value) 126 127 f.once.Do(f.init) 128 <-f.sendLock 129 130 // Add new cases from the inbox after taking the send lock. 131 f.mu.Lock() 132 f.sendCases = append(f.sendCases, f.inbox...) 133 f.inbox = nil 134 135 if !f.typecheck(rvalue.Type()) { 136 f.sendLock <- struct{}{} 137 panic(feedTypeError{op: "Send", got: rvalue.Type(), want: f.etype}) 138 } 139 f.mu.Unlock() 140 141 // Set the sent value on all channels. 142 for i := firstSubSendCase; i < len(f.sendCases); i++ { 143 f.sendCases[i].Send = rvalue 144 } 145 146 // Send until all channels except removeSub have been chosen. 147 cases := f.sendCases 148 for { 149 // Fast path: try sending without blocking before adding to the select set. 150 // This should usually succeed if subscribers are fast enough and have free 151 // buffer space. 152 for i := firstSubSendCase; i < len(cases); i++ { 153 if cases[i].Chan.TrySend(rvalue) { 154 nsent++ 155 cases = cases.deactivate(i) 156 i-- 157 } 158 } 159 if len(cases) == firstSubSendCase { 160 break 161 } 162 // Select on all the receivers, waiting for them to unblock. 163 chosen, recv, _ := reflect.Select(cases) 164 if chosen == 0 /* <-f.removeSub */ { 165 index := f.sendCases.find(recv.Interface()) 166 f.sendCases = f.sendCases.delete(index) 167 if index >= 0 && index < len(cases) { 168 cases = f.sendCases[:len(cases)-1] 169 } 170 } else { 171 cases = cases.deactivate(chosen) 172 nsent++ 173 } 174 } 175 176 // Forget about the sent value and hand off the send lock. 177 for i := firstSubSendCase; i < len(f.sendCases); i++ { 178 f.sendCases[i].Send = reflect.Value{} 179 } 180 f.sendLock <- struct{}{} 181 return nsent 182 } 183 184 type feedSub struct { 185 feed *Feed 186 channel reflect.Value 187 errOnce sync.Once 188 err chan error 189 } 190 191 func (sub *feedSub) Unsubscribe() { 192 sub.errOnce.Do(func() { 193 sub.feed.remove(sub) 194 close(sub.err) 195 }) 196 } 197 198 func (sub *feedSub) Err() <-chan error { 199 return sub.err 200 } 201 202 type caseList []reflect.SelectCase 203 204 // find returns the index of a case containing the given channel. 205 func (cs caseList) find(channel interface{}) int { 206 for i, cas := range cs { 207 if cas.Chan.Interface() == channel { 208 return i 209 } 210 } 211 return -1 212 } 213 214 // delete removes the given case from cs. 215 func (cs caseList) delete(index int) caseList { 216 return append(cs[:index], cs[index+1:]...) 217 } 218 219 // deactivate moves the case at index into the non-accessible portion of the cs slice. 220 func (cs caseList) deactivate(index int) caseList { 221 last := len(cs) - 1 222 cs[index], cs[last] = cs[last], cs[index] 223 return cs[:last] 224 } 225 226 // func (cs caseList) String() string { 227 // s := "[" 228 // for i, cas := range cs { 229 // if i != 0 { 230 // s += ", " 231 // } 232 // switch cas.Dir { 233 // case reflect.SelectSend: 234 // s += fmt.Sprintf("%v<-", cas.Chan.Interface()) 235 // case reflect.SelectRecv: 236 // s += fmt.Sprintf("<-%v", cas.Chan.Interface()) 237 // } 238 // } 239 // return s + "]" 240 // }