github.com/arieschain/arieschain@v0.0.0-20191023063405-37c074544356/les/execqueue.go (about) 1 package les 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 { 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 // quit stops the exec queue. 72 // quit waits for the current execution to finish before returning. 73 func (q *execQueue) quit() { 74 q.mu.Lock() 75 if !q.isClosed() { 76 q.closeWait = make(chan struct{}) 77 q.cond.Signal() 78 } 79 q.mu.Unlock() 80 <-q.closeWait 81 }