github.com/klaytn/klaytn@v1.12.1/event/event.go (about) 1 // Modifications Copyright 2018 The klaytn Authors 2 // Copyright 2014 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/event.go (2018/06/04). 19 // Modified and improved for the klaytn development. 20 21 package event 22 23 import ( 24 "errors" 25 "fmt" 26 "reflect" 27 "sync" 28 "time" 29 ) 30 31 // TypeMuxEvent is a time-tagged notification pushed to subscribers. 32 type TypeMuxEvent struct { 33 Time time.Time 34 Data interface{} 35 } 36 37 // A TypeMux dispatches events to registered receivers. Receivers can be 38 // registered to handle events of certain type. Any operation 39 // called after mux is stopped will return ErrMuxClosed. 40 // 41 // The zero value is ready to use. 42 type TypeMux struct { 43 mutex sync.RWMutex 44 subm map[reflect.Type][]*TypeMuxSubscription 45 stopped bool 46 } 47 48 // ErrMuxClosed is returned when Posting on a closed TypeMux. 49 var ErrMuxClosed = errors.New("event: mux closed") 50 51 // Subscribe creates a subscription for events of the given types. The 52 // subscription's channel is closed when it is unsubscribed 53 // or the mux is closed. 54 func (mux *TypeMux) Subscribe(types ...interface{}) *TypeMuxSubscription { 55 sub := newsub(mux) 56 mux.mutex.Lock() 57 defer mux.mutex.Unlock() 58 if mux.stopped { 59 // set the status to closed so that calling Unsubscribe after this 60 // call will short circuit. 61 sub.closed = true 62 close(sub.postC) 63 } else { 64 if mux.subm == nil { 65 mux.subm = make(map[reflect.Type][]*TypeMuxSubscription) 66 } 67 for _, t := range types { 68 rtyp := reflect.TypeOf(t) 69 oldsubs := mux.subm[rtyp] 70 if find(oldsubs, sub) != -1 { 71 panic(fmt.Sprintf("event: duplicate type %s in Subscribe", rtyp)) 72 } 73 subs := make([]*TypeMuxSubscription, len(oldsubs)+1) 74 copy(subs, oldsubs) 75 subs[len(oldsubs)] = sub 76 mux.subm[rtyp] = subs 77 } 78 } 79 return sub 80 } 81 82 // Post sends an event to all receivers registered for the given type. 83 // It returns ErrMuxClosed if the mux has been stopped. 84 func (mux *TypeMux) Post(ev interface{}) error { 85 event := &TypeMuxEvent{ 86 Time: time.Now(), 87 Data: ev, 88 } 89 rtyp := reflect.TypeOf(ev) 90 mux.mutex.RLock() 91 if mux.stopped { 92 mux.mutex.RUnlock() 93 return ErrMuxClosed 94 } 95 subs := mux.subm[rtyp] 96 mux.mutex.RUnlock() 97 for _, sub := range subs { 98 sub.deliver(event) 99 } 100 return nil 101 } 102 103 // Stop closes a mux. The mux can no longer be used. 104 // Future Post calls will fail with ErrMuxClosed. 105 // Stop blocks until all current deliveries have finished. 106 func (mux *TypeMux) Stop() { 107 mux.mutex.Lock() 108 for _, subs := range mux.subm { 109 for _, sub := range subs { 110 sub.closewait() 111 } 112 } 113 mux.subm = nil 114 mux.stopped = true 115 mux.mutex.Unlock() 116 } 117 118 func (mux *TypeMux) del(s *TypeMuxSubscription) { 119 mux.mutex.Lock() 120 for typ, subs := range mux.subm { 121 if pos := find(subs, s); pos >= 0 { 122 if len(subs) == 1 { 123 delete(mux.subm, typ) 124 } else { 125 mux.subm[typ] = posdelete(subs, pos) 126 } 127 } 128 } 129 s.mux.mutex.Unlock() 130 } 131 132 func find(slice []*TypeMuxSubscription, item *TypeMuxSubscription) int { 133 for i, v := range slice { 134 if v == item { 135 return i 136 } 137 } 138 return -1 139 } 140 141 func posdelete(slice []*TypeMuxSubscription, pos int) []*TypeMuxSubscription { 142 news := make([]*TypeMuxSubscription, len(slice)-1) 143 copy(news[:pos], slice[:pos]) 144 copy(news[pos:], slice[pos+1:]) 145 return news 146 } 147 148 // TypeMuxSubscription is a subscription established through TypeMux. 149 type TypeMuxSubscription struct { 150 mux *TypeMux 151 created time.Time 152 closeMu sync.Mutex 153 closing chan struct{} 154 closed bool 155 156 // these two are the same channel. they are stored separately so 157 // postC can be set to nil without affecting the return value of 158 // Chan. 159 postMu sync.RWMutex 160 readC <-chan *TypeMuxEvent 161 postC chan<- *TypeMuxEvent 162 } 163 164 func newsub(mux *TypeMux) *TypeMuxSubscription { 165 c := make(chan *TypeMuxEvent) 166 return &TypeMuxSubscription{ 167 mux: mux, 168 created: time.Now(), 169 readC: c, 170 postC: c, 171 closing: make(chan struct{}), 172 } 173 } 174 175 func (s *TypeMuxSubscription) Chan() <-chan *TypeMuxEvent { 176 return s.readC 177 } 178 179 func (s *TypeMuxSubscription) Unsubscribe() { 180 s.mux.del(s) 181 s.closewait() 182 } 183 184 func (s *TypeMuxSubscription) Closed() bool { 185 s.closeMu.Lock() 186 defer s.closeMu.Unlock() 187 return s.closed 188 } 189 190 func (s *TypeMuxSubscription) closewait() { 191 s.closeMu.Lock() 192 defer s.closeMu.Unlock() 193 if s.closed { 194 return 195 } 196 close(s.closing) 197 s.closed = true 198 199 s.postMu.Lock() 200 close(s.postC) 201 s.postC = nil 202 s.postMu.Unlock() 203 } 204 205 func (s *TypeMuxSubscription) deliver(event *TypeMuxEvent) { 206 // Short circuit delivery if stale event 207 if s.created.After(event.Time) { 208 return 209 } 210 // Otherwise deliver the event 211 s.postMu.RLock() 212 defer s.postMu.RUnlock() 213 214 select { 215 case s.postC <- event: 216 case <-s.closing: 217 } 218 }