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