github.com/neatlab/neatio@v1.7.3-0.20220425043230-d903e92fcc75/utilities/event/subscription.go (about)

     1  package event
     2  
     3  import (
     4  	"context"
     5  	"sync"
     6  	"time"
     7  
     8  	"github.com/neatlab/neatio/utilities/common/mclock"
     9  )
    10  
    11  type Subscription interface {
    12  	Err() <-chan error
    13  	Unsubscribe()
    14  }
    15  
    16  func NewSubscription(producer func(<-chan struct{}) error) Subscription {
    17  	s := &funcSub{unsub: make(chan struct{}), err: make(chan error, 1)}
    18  	go func() {
    19  		defer close(s.err)
    20  		err := producer(s.unsub)
    21  		s.mu.Lock()
    22  		defer s.mu.Unlock()
    23  		if !s.unsubscribed {
    24  			if err != nil {
    25  				s.err <- err
    26  			}
    27  			s.unsubscribed = true
    28  		}
    29  	}()
    30  	return s
    31  }
    32  
    33  type funcSub struct {
    34  	unsub        chan struct{}
    35  	err          chan error
    36  	mu           sync.Mutex
    37  	unsubscribed bool
    38  }
    39  
    40  func (s *funcSub) Unsubscribe() {
    41  	s.mu.Lock()
    42  	if s.unsubscribed {
    43  		s.mu.Unlock()
    44  		return
    45  	}
    46  	s.unsubscribed = true
    47  	close(s.unsub)
    48  	s.mu.Unlock()
    49  
    50  	<-s.err
    51  }
    52  
    53  func (s *funcSub) Err() <-chan error {
    54  	return s.err
    55  }
    56  
    57  func Resubscribe(backoffMax time.Duration, fn ResubscribeFunc) Subscription {
    58  	s := &resubscribeSub{
    59  		waitTime:   backoffMax / 10,
    60  		backoffMax: backoffMax,
    61  		fn:         fn,
    62  		err:        make(chan error),
    63  		unsub:      make(chan struct{}),
    64  	}
    65  	go s.loop()
    66  	return s
    67  }
    68  
    69  type ResubscribeFunc func(context.Context) (Subscription, error)
    70  
    71  type resubscribeSub struct {
    72  	fn                   ResubscribeFunc
    73  	err                  chan error
    74  	unsub                chan struct{}
    75  	unsubOnce            sync.Once
    76  	lastTry              mclock.AbsTime
    77  	waitTime, backoffMax time.Duration
    78  }
    79  
    80  func (s *resubscribeSub) Unsubscribe() {
    81  	s.unsubOnce.Do(func() {
    82  		s.unsub <- struct{}{}
    83  		<-s.err
    84  	})
    85  }
    86  
    87  func (s *resubscribeSub) Err() <-chan error {
    88  	return s.err
    89  }
    90  
    91  func (s *resubscribeSub) loop() {
    92  	defer close(s.err)
    93  	var done bool
    94  	for !done {
    95  		sub := s.subscribe()
    96  		if sub == nil {
    97  			break
    98  		}
    99  		done = s.waitForError(sub)
   100  		sub.Unsubscribe()
   101  	}
   102  }
   103  
   104  func (s *resubscribeSub) subscribe() Subscription {
   105  	subscribed := make(chan error)
   106  	var sub Subscription
   107  retry:
   108  	for {
   109  		s.lastTry = mclock.Now()
   110  		ctx, cancel := context.WithCancel(context.Background())
   111  		go func() {
   112  			rsub, err := s.fn(ctx)
   113  			sub = rsub
   114  			subscribed <- err
   115  		}()
   116  		select {
   117  		case err := <-subscribed:
   118  			cancel()
   119  			if err != nil {
   120  
   121  				if s.backoffWait() {
   122  					return nil
   123  				}
   124  				continue retry
   125  			}
   126  			if sub == nil {
   127  				panic("event: ResubscribeFunc returned nil subscription and no error")
   128  			}
   129  			return sub
   130  		case <-s.unsub:
   131  			cancel()
   132  			return nil
   133  		}
   134  	}
   135  }
   136  
   137  func (s *resubscribeSub) waitForError(sub Subscription) bool {
   138  	defer sub.Unsubscribe()
   139  	select {
   140  	case err := <-sub.Err():
   141  		return err == nil
   142  	case <-s.unsub:
   143  		return true
   144  	}
   145  }
   146  
   147  func (s *resubscribeSub) backoffWait() bool {
   148  	if time.Duration(mclock.Now()-s.lastTry) > s.backoffMax {
   149  		s.waitTime = s.backoffMax / 10
   150  	} else {
   151  		s.waitTime *= 2
   152  		if s.waitTime > s.backoffMax {
   153  			s.waitTime = s.backoffMax
   154  		}
   155  	}
   156  
   157  	t := time.NewTimer(s.waitTime)
   158  	defer t.Stop()
   159  	select {
   160  	case <-t.C:
   161  		return false
   162  	case <-s.unsub:
   163  		return true
   164  	}
   165  }
   166  
   167  type SubscriptionScope struct {
   168  	mu     sync.Mutex
   169  	subs   map[*scopeSub]struct{}
   170  	closed bool
   171  }
   172  
   173  type scopeSub struct {
   174  	sc *SubscriptionScope
   175  	s  Subscription
   176  }
   177  
   178  func (sc *SubscriptionScope) Track(s Subscription) Subscription {
   179  	sc.mu.Lock()
   180  	defer sc.mu.Unlock()
   181  	if sc.closed {
   182  		return nil
   183  	}
   184  	if sc.subs == nil {
   185  		sc.subs = make(map[*scopeSub]struct{})
   186  	}
   187  	ss := &scopeSub{sc, s}
   188  	sc.subs[ss] = struct{}{}
   189  	return ss
   190  }
   191  
   192  func (sc *SubscriptionScope) Close() {
   193  	sc.mu.Lock()
   194  	defer sc.mu.Unlock()
   195  	if sc.closed {
   196  		return
   197  	}
   198  	sc.closed = true
   199  	for s := range sc.subs {
   200  		s.s.Unsubscribe()
   201  	}
   202  	sc.subs = nil
   203  }
   204  
   205  func (sc *SubscriptionScope) Count() int {
   206  	sc.mu.Lock()
   207  	defer sc.mu.Unlock()
   208  	return len(sc.subs)
   209  }
   210  
   211  func (s *scopeSub) Unsubscribe() {
   212  	s.s.Unsubscribe()
   213  	s.sc.mu.Lock()
   214  	defer s.sc.mu.Unlock()
   215  	delete(s.sc.subs, s)
   216  }
   217  
   218  func (s *scopeSub) Err() <-chan error {
   219  	return s.s.Err()
   220  }