github.com/unicornultrafoundation/go-u2u@v1.0.0-rc1.0.20240205080301-e74a83d3fadc/gossip/protocols/blockvotes/bvprocessor/processor.go (about) 1 package bvprocessor 2 3 import ( 4 "errors" 5 "sync" 6 7 "github.com/unicornultrafoundation/go-helios/native/dag" 8 "github.com/unicornultrafoundation/go-helios/native/idx" 9 "github.com/unicornultrafoundation/go-helios/utils/datasemaphore" 10 "github.com/unicornultrafoundation/go-helios/utils/workers" 11 12 "github.com/unicornultrafoundation/go-u2u/native" 13 ) 14 15 var ( 16 ErrBusy = errors.New("failed to acquire events semaphore") 17 ) 18 19 // Processor is responsible for processing incoming events 20 type Processor struct { 21 cfg Config 22 23 quit chan struct{} 24 wg sync.WaitGroup 25 26 callback Callback 27 28 inserter *workers.Workers 29 30 checker *workers.Workers 31 32 itemsSemaphore *datasemaphore.DataSemaphore 33 } 34 35 type ItemCallback struct { 36 Process func(bvs native.LlrSignedBlockVotes) error 37 Released func(bvs native.LlrSignedBlockVotes, peer string, err error) 38 Check func(bvs native.LlrSignedBlockVotes, checked func(error)) 39 } 40 41 type Callback struct { 42 Item ItemCallback 43 } 44 45 // New creates an event processor 46 func New(itemsSemaphore *datasemaphore.DataSemaphore, cfg Config, callback Callback) *Processor { 47 f := &Processor{ 48 cfg: cfg, 49 quit: make(chan struct{}), 50 itemsSemaphore: itemsSemaphore, 51 } 52 released := callback.Item.Released 53 callback.Item.Released = func(bvs native.LlrSignedBlockVotes, peer string, err error) { 54 f.itemsSemaphore.Release(dag.Metric{Num: 1, Size: uint64(bvs.Size())}) 55 if released != nil { 56 released(bvs, peer, err) 57 } 58 } 59 f.callback = callback 60 f.inserter = workers.New(&f.wg, f.quit, cfg.MaxTasks) 61 f.checker = workers.New(&f.wg, f.quit, cfg.MaxTasks) 62 return f 63 } 64 65 // Start boots up the items processor. 66 func (f *Processor) Start() { 67 f.inserter.Start(1) 68 f.checker.Start(1) 69 } 70 71 // Stop interrupts the processor, canceling all the pending operations. 72 // Stop waits until all the internal goroutines have finished. 73 func (f *Processor) Stop() { 74 close(f.quit) 75 f.itemsSemaphore.Terminate() 76 f.wg.Wait() 77 } 78 79 // Overloaded returns true if too much items are being processed 80 func (f *Processor) Overloaded() bool { 81 return f.TasksCount() > f.cfg.MaxTasks*3/4 82 } 83 84 type checkRes struct { 85 bvs native.LlrSignedBlockVotes 86 err error 87 pos idx.Event 88 } 89 90 func (f *Processor) Enqueue(peer string, items []native.LlrSignedBlockVotes, done func()) error { 91 totalSize := uint64(0) 92 for _, v := range items { 93 totalSize += v.Size() 94 } 95 if !f.itemsSemaphore.Acquire(dag.Metric{Num: idx.Event(len(items)), Size: totalSize}, f.cfg.SemaphoreTimeout) { 96 return ErrBusy 97 } 98 99 checkedC := make(chan *checkRes, len(items)) 100 err := f.checker.Enqueue(func() { 101 for i, v := range items { 102 pos := idx.Event(i) 103 bvs := v 104 f.callback.Item.Check(bvs, func(err error) { 105 checkedC <- &checkRes{ 106 bvs: bvs, 107 err: err, 108 pos: pos, 109 } 110 }) 111 } 112 }) 113 if err != nil { 114 return err 115 } 116 itemsLen := len(items) 117 return f.inserter.Enqueue(func() { 118 if done != nil { 119 defer done() 120 } 121 122 var orderedResults = make([]*checkRes, itemsLen) 123 var processed int 124 for processed < itemsLen { 125 select { 126 case res := <-checkedC: 127 orderedResults[res.pos] = res 128 129 for i := processed; processed < len(orderedResults) && orderedResults[i] != nil; i++ { 130 f.process(peer, orderedResults[i].bvs, orderedResults[i].err) 131 orderedResults[i] = nil // free the memory 132 processed++ 133 } 134 135 case <-f.quit: 136 return 137 } 138 } 139 }) 140 } 141 142 func (f *Processor) process(peer string, bvs native.LlrSignedBlockVotes, resErr error) { 143 // release item if failed validation 144 if resErr != nil { 145 f.callback.Item.Released(bvs, peer, resErr) 146 return 147 } 148 // process item 149 err := f.callback.Item.Process(bvs) 150 f.callback.Item.Released(bvs, peer, err) 151 } 152 153 func (f *Processor) TasksCount() int { 154 return f.inserter.TasksCount() + f.checker.TasksCount() 155 }