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  }