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