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