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