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