github.com/phillinzzz/newBsc@v1.1.6/event/subscription.go (about) 1 // Copyright 2016 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 18 19 import ( 20 "context" 21 "sync" 22 "time" 23 24 "github.com/phillinzzz/newBsc/common/gopool" 25 "github.com/phillinzzz/newBsc/common/mclock" 26 ) 27 28 // Subscription represents a stream of events. The carrier of the events is typically a 29 // channel, but isn't part of the interface. 30 // 31 // Subscriptions can fail while established. Failures are reported through an error 32 // channel. It receives a value if there is an issue with the subscription (e.g. the 33 // network connection delivering the events has been closed). Only one value will ever be 34 // sent. 35 // 36 // The error channel is closed when the subscription ends successfully (i.e. when the 37 // source of events is closed). It is also closed when Unsubscribe is called. 38 // 39 // The Unsubscribe method cancels the sending of events. You must call Unsubscribe in all 40 // cases to ensure that resources related to the subscription are released. It can be 41 // called any number of times. 42 type Subscription interface { 43 Err() <-chan error // returns the error channel 44 Unsubscribe() // cancels sending of events, closing the error channel 45 } 46 47 // NewSubscription runs a producer function as a subscription in a new goroutine. The 48 // channel given to the producer is closed when Unsubscribe is called. If fn returns an 49 // error, it is sent on the subscription's error channel. 50 func NewSubscription(producer func(<-chan struct{}) error) Subscription { 51 s := &funcSub{unsub: make(chan struct{}), err: make(chan error, 1)} 52 gopool.Submit(func() { 53 defer close(s.err) 54 err := producer(s.unsub) 55 s.mu.Lock() 56 defer s.mu.Unlock() 57 if !s.unsubscribed { 58 if err != nil { 59 s.err <- err 60 } 61 s.unsubscribed = true 62 } 63 }) 64 return s 65 } 66 67 type funcSub struct { 68 unsub chan struct{} 69 err chan error 70 mu sync.Mutex 71 unsubscribed bool 72 } 73 74 func (s *funcSub) Unsubscribe() { 75 s.mu.Lock() 76 if s.unsubscribed { 77 s.mu.Unlock() 78 return 79 } 80 s.unsubscribed = true 81 close(s.unsub) 82 s.mu.Unlock() 83 // Wait for producer shutdown. 84 <-s.err 85 } 86 87 func (s *funcSub) Err() <-chan error { 88 return s.err 89 } 90 91 // Resubscribe calls fn repeatedly to keep a subscription established. When the 92 // subscription is established, Resubscribe waits for it to fail and calls fn again. This 93 // process repeats until Unsubscribe is called or the active subscription ends 94 // successfully. 95 // 96 // Resubscribe applies backoff between calls to fn. The time between calls is adapted 97 // based on the error rate, but will never exceed backoffMax. 98 func Resubscribe(backoffMax time.Duration, fn ResubscribeFunc) Subscription { 99 return ResubscribeErr(backoffMax, func(ctx context.Context, _ error) (Subscription, error) { 100 return fn(ctx) 101 }) 102 } 103 104 // A ResubscribeFunc attempts to establish a subscription. 105 type ResubscribeFunc func(context.Context) (Subscription, error) 106 107 // ResubscribeErr calls fn repeatedly to keep a subscription established. When the 108 // subscription is established, ResubscribeErr waits for it to fail and calls fn again. This 109 // process repeats until Unsubscribe is called or the active subscription ends 110 // successfully. 111 // 112 // The difference between Resubscribe and ResubscribeErr is that with ResubscribeErr, 113 // the error of the failing subscription is available to the callback for logging 114 // purposes. 115 // 116 // ResubscribeErr applies backoff between calls to fn. The time between calls is adapted 117 // based on the error rate, but will never exceed backoffMax. 118 func ResubscribeErr(backoffMax time.Duration, fn ResubscribeErrFunc) Subscription { 119 s := &resubscribeSub{ 120 waitTime: backoffMax / 10, 121 backoffMax: backoffMax, 122 fn: fn, 123 err: make(chan error), 124 unsub: make(chan struct{}), 125 } 126 go s.loop() 127 return s 128 } 129 130 // A ResubscribeErrFunc attempts to establish a subscription. 131 // For every call but the first, the second argument to this function is 132 // the error that occurred with the previous subscription. 133 type ResubscribeErrFunc func(context.Context, error) (Subscription, error) 134 135 type resubscribeSub struct { 136 fn ResubscribeErrFunc 137 err chan error 138 unsub chan struct{} 139 unsubOnce sync.Once 140 lastTry mclock.AbsTime 141 lastSubErr error 142 waitTime, backoffMax time.Duration 143 } 144 145 func (s *resubscribeSub) Unsubscribe() { 146 s.unsubOnce.Do(func() { 147 s.unsub <- struct{}{} 148 <-s.err 149 }) 150 } 151 152 func (s *resubscribeSub) Err() <-chan error { 153 return s.err 154 } 155 156 func (s *resubscribeSub) loop() { 157 defer close(s.err) 158 var done bool 159 for !done { 160 sub := s.subscribe() 161 if sub == nil { 162 break 163 } 164 done = s.waitForError(sub) 165 sub.Unsubscribe() 166 } 167 } 168 169 func (s *resubscribeSub) subscribe() Subscription { 170 subscribed := make(chan error) 171 var sub Subscription 172 for { 173 s.lastTry = mclock.Now() 174 ctx, cancel := context.WithCancel(context.Background()) 175 gopool.Submit(func() { 176 rsub, err := s.fn(ctx, s.lastSubErr) 177 sub = rsub 178 subscribed <- err 179 }) 180 select { 181 case err := <-subscribed: 182 cancel() 183 if err == nil { 184 if sub == nil { 185 panic("event: ResubscribeFunc returned nil subscription and no error") 186 } 187 return sub 188 } 189 // Subscribing failed, wait before launching the next try. 190 if s.backoffWait() { 191 return nil // unsubscribed during wait 192 } 193 case <-s.unsub: 194 cancel() 195 <-subscribed // avoid leaking the s.fn goroutine. 196 return nil 197 } 198 } 199 } 200 201 func (s *resubscribeSub) waitForError(sub Subscription) bool { 202 defer sub.Unsubscribe() 203 select { 204 case err := <-sub.Err(): 205 s.lastSubErr = err 206 return err == nil 207 case <-s.unsub: 208 return true 209 } 210 } 211 212 func (s *resubscribeSub) backoffWait() bool { 213 if time.Duration(mclock.Now()-s.lastTry) > s.backoffMax { 214 s.waitTime = s.backoffMax / 10 215 } else { 216 s.waitTime *= 2 217 if s.waitTime > s.backoffMax { 218 s.waitTime = s.backoffMax 219 } 220 } 221 222 t := time.NewTimer(s.waitTime) 223 defer t.Stop() 224 select { 225 case <-t.C: 226 return false 227 case <-s.unsub: 228 return true 229 } 230 } 231 232 // SubscriptionScope provides a facility to unsubscribe multiple subscriptions at once. 233 // 234 // For code that handle more than one subscription, a scope can be used to conveniently 235 // unsubscribe all of them with a single call. The example demonstrates a typical use in a 236 // larger program. 237 // 238 // The zero value is ready to use. 239 type SubscriptionScope struct { 240 mu sync.Mutex 241 subs map[*scopeSub]struct{} 242 closed bool 243 } 244 245 type scopeSub struct { 246 sc *SubscriptionScope 247 s Subscription 248 } 249 250 // Track starts tracking a subscription. If the scope is closed, Track returns nil. The 251 // returned subscription is a wrapper. Unsubscribing the wrapper removes it from the 252 // scope. 253 func (sc *SubscriptionScope) Track(s Subscription) Subscription { 254 sc.mu.Lock() 255 defer sc.mu.Unlock() 256 if sc.closed { 257 return nil 258 } 259 if sc.subs == nil { 260 sc.subs = make(map[*scopeSub]struct{}) 261 } 262 ss := &scopeSub{sc, s} 263 sc.subs[ss] = struct{}{} 264 return ss 265 } 266 267 // Close calls Unsubscribe on all tracked subscriptions and prevents further additions to 268 // the tracked set. Calls to Track after Close return nil. 269 func (sc *SubscriptionScope) Close() { 270 sc.mu.Lock() 271 defer sc.mu.Unlock() 272 if sc.closed { 273 return 274 } 275 sc.closed = true 276 for s := range sc.subs { 277 s.s.Unsubscribe() 278 } 279 sc.subs = nil 280 } 281 282 // Count returns the number of tracked subscriptions. 283 // It is meant to be used for debugging. 284 func (sc *SubscriptionScope) Count() int { 285 sc.mu.Lock() 286 defer sc.mu.Unlock() 287 return len(sc.subs) 288 } 289 290 func (s *scopeSub) Unsubscribe() { 291 s.s.Unsubscribe() 292 s.sc.mu.Lock() 293 defer s.sc.mu.Unlock() 294 delete(s.sc.subs, s) 295 } 296 297 func (s *scopeSub) Err() <-chan error { 298 return s.s.Err() 299 }