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 }