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