github.com/unicornultrafoundation/go-u2u@v1.0.0-rc1.0.20240205080301-e74a83d3fadc/gossip/execqueue.go (about) 1 package gossip 2 3 import "sync" 4 5 // execQueue implements a queue that executes function calls in a single thread, 6 // in the same order as they have been queued. 7 type execQueue struct { 8 mu sync.Mutex 9 cond *sync.Cond 10 funcs []func() 11 closeWait chan struct{} 12 } 13 14 // newExecQueue creates a new execution queue. 15 func newExecQueue(capacity int) *execQueue { 16 q := &execQueue{funcs: make([]func(), 0, capacity)} 17 q.cond = sync.NewCond(&q.mu) 18 go q.loop() 19 return q 20 } 21 22 func (q *execQueue) loop() { 23 for f := q.waitNext(false); f != nil; f = q.waitNext(true) { 24 f() 25 } 26 close(q.closeWait) 27 } 28 29 func (q *execQueue) waitNext(drop bool) (f func()) { 30 q.mu.Lock() 31 if drop && len(q.funcs) > 0 { 32 // Remove the function that just executed. We do this here instead of when 33 // dequeuing so len(q.funcs) includes the function that is running. 34 q.funcs = append(q.funcs[:0], q.funcs[1:]...) 35 } 36 for !q.isClosed() { 37 if len(q.funcs) > 0 { 38 f = q.funcs[0] 39 break 40 } 41 q.cond.Wait() 42 } 43 q.mu.Unlock() 44 return f 45 } 46 47 func (q *execQueue) isClosed() bool { 48 return q.closeWait != nil 49 } 50 51 // canQueue returns true if more function calls can be added to the execution queue. 52 func (q *execQueue) canQueue() bool { 53 q.mu.Lock() 54 ok := !q.isClosed() && len(q.funcs) < cap(q.funcs) 55 q.mu.Unlock() 56 return ok 57 } 58 59 // queue adds a function call to the execution queue. Returns true if successful. 60 func (q *execQueue) queue(f func()) bool { 61 q.mu.Lock() 62 ok := !q.isClosed() && len(q.funcs) < cap(q.funcs) 63 if ok { 64 q.funcs = append(q.funcs, f) 65 q.cond.Signal() 66 } 67 q.mu.Unlock() 68 return ok 69 } 70 71 // clear drops all queued functions 72 func (q *execQueue) clear() { 73 q.mu.Lock() 74 q.funcs = q.funcs[:0] 75 q.mu.Unlock() 76 } 77 78 // quit stops the exec queue. 79 // quit waits for the current execution to finish before returning. 80 func (q *execQueue) quit() { 81 q.mu.Lock() 82 if !q.isClosed() { 83 q.closeWait = make(chan struct{}) 84 q.cond.Signal() 85 } 86 q.mu.Unlock() 87 <-q.closeWait 88 }