github.com/0xsequence/ethkit@v1.25.0/ethmonitor/pubsub.go (about)

     1  package ethmonitor
     2  
     3  import (
     4  	"fmt"
     5  	"sync"
     6  
     7  	"github.com/goware/channel"
     8  	"github.com/goware/superr"
     9  )
    10  
    11  type Subscription interface {
    12  	Blocks() <-chan Blocks
    13  	Done() <-chan struct{}
    14  	Err() error
    15  	Unsubscribe()
    16  }
    17  
    18  var _ Subscription = &subscriber{}
    19  
    20  type subscriber struct {
    21  	ch              channel.Channel[Blocks]
    22  	done            chan struct{}
    23  	err             error
    24  	unsubscribe     func()
    25  	unsubscribeOnce sync.Once
    26  }
    27  
    28  func (s *subscriber) Blocks() <-chan Blocks {
    29  	return s.ch.ReadChannel()
    30  }
    31  
    32  func (s *subscriber) Done() <-chan struct{} {
    33  	return s.done
    34  }
    35  
    36  func (s *subscriber) Err() error {
    37  	return s.err
    38  }
    39  
    40  func (s *subscriber) Unsubscribe() {
    41  	s.unsubscribeOnce.Do(s.unsubscribe)
    42  }
    43  
    44  // queue is the publish event queue
    45  type queue struct {
    46  	events Blocks
    47  	cap    int
    48  	mu     sync.Mutex
    49  }
    50  
    51  func newQueue(cap int) *queue {
    52  	return &queue{
    53  		events: make(Blocks, 0, cap),
    54  		cap:    cap,
    55  	}
    56  }
    57  
    58  func (q *queue) clear() {
    59  	q.mu.Lock()
    60  	defer q.mu.Unlock()
    61  	q.events = q.events[:0]
    62  }
    63  
    64  func (q *queue) len() int {
    65  	q.mu.Lock()
    66  	defer q.mu.Unlock()
    67  	return len(q.events)
    68  }
    69  
    70  func (c *queue) enqueue(events Blocks) error {
    71  	c.mu.Lock()
    72  	defer c.mu.Unlock()
    73  
    74  	for _, event := range events {
    75  		switch event.Event {
    76  
    77  		case Added:
    78  			c.events = append(c.events, event)
    79  
    80  		case Removed:
    81  			if len(c.events) > 0 {
    82  				tail := c.events[len(c.events)-1]
    83  
    84  				switch tail.Event {
    85  				case Added:
    86  					if event.Hash() == tail.Hash() {
    87  						// instead of publishing this removal, pop the most recent event
    88  						c.events = c.events[:len(c.events)-1]
    89  					} else {
    90  						// it should be impossible to remove anything but the most recent event
    91  						return fmt.Errorf("removing block %v %v %v, but last block is %v %v %v", event.Event, event.Number(), event.Hash().Hex(), tail.Event, tail.Number(), tail.Hash().Hex())
    92  					}
    93  				case Removed:
    94  					// we have a string of removal events, so we can only publish the removal
    95  					c.events = append(c.events, event)
    96  				}
    97  			} else {
    98  				// we already published the addition, so we must publish the removal
    99  				c.events = append(c.events, event)
   100  			}
   101  
   102  		default:
   103  			return fmt.Errorf("unknown event type %v %v %v", event.Event, event.Number(), event.Hash().Hex())
   104  		}
   105  	}
   106  
   107  	if len(c.events) > c.cap {
   108  		return superr.New(ErrFatal, ErrQueueFull)
   109  	}
   110  	return nil
   111  }
   112  
   113  func (c *queue) dequeue(maxBlockNum uint64) (Blocks, bool) {
   114  	c.mu.Lock()
   115  	defer c.mu.Unlock()
   116  
   117  	if len(c.events) == 0 {
   118  		return Blocks{}, false // queue is empty
   119  	}
   120  
   121  	var events Blocks
   122  
   123  	for _, ev := range c.events {
   124  		if ev.OK {
   125  			// maxBlockNum indicates we want to "trail-behind", and only dequeue
   126  			// up to a certain limit.
   127  			if maxBlockNum > 0 && ev.Block.NumberU64() > maxBlockNum {
   128  				break
   129  			}
   130  
   131  			// collect dequeued events
   132  			events = append(events, ev)
   133  		} else {
   134  			break
   135  		}
   136  	}
   137  
   138  	if len(events) == 0 {
   139  		return Blocks{}, false
   140  	}
   141  	if events[len(events)-1].Event != Added {
   142  		// last block must be an added one, otherwise we do
   143  		// not dequeue any events
   144  		return Blocks{}, false
   145  	}
   146  
   147  	// trim queue and return dequeued events
   148  	c.events = c.events[len(events):]
   149  
   150  	return events, true
   151  }
   152  
   153  func (c *queue) head() *Block {
   154  	c.mu.Lock()
   155  	defer c.mu.Unlock()
   156  	if len(c.events) == 0 {
   157  		return nil
   158  	}
   159  	return c.events[0]
   160  }
   161  
   162  func (c *queue) tail() *Block {
   163  	c.mu.Lock()
   164  	defer c.mu.Unlock()
   165  	if len(c.events) == 0 {
   166  		return nil
   167  	}
   168  	return c.events[len(c.events)-1]
   169  }