github.com/gochain-io/gochain@v2.2.26+incompatible/les/execqueue.go (about) 1 // Copyright 2017 The go-ethereum Authors 2 // This file is part of the go-ethereum library. 3 // 4 // The go-ethereum library is free software: you can redistribute it and/or modify 5 // it under the terms of the GNU Lesser General Public License as published by 6 // the Free Software Foundation, either version 3 of the License, or 7 // (at your option) any later version. 8 // 9 // The go-ethereum library is distributed in the hope that it will be useful, 10 // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 // GNU Lesser General Public License for more details. 13 // 14 // You should have received a copy of the GNU Lesser General Public License 15 // along with the go-ethereum library. If not, see <http://www.gnu.org/licenses/>. 16 17 package les 18 19 import ( 20 "context" 21 "sync" 22 23 "go.opencensus.io/trace" 24 ) 25 26 // execQueue implements a queue that executes function calls in a single thread, 27 // in the same order as they have been queued. 28 type execQueue struct { 29 mu sync.Mutex 30 cond *sync.Cond 31 funcs []func(context.Context) 32 closeWait chan struct{} 33 } 34 35 // newExecQueue creates a new execution queue. 36 func newExecQueue(capacity int) *execQueue { 37 q := &execQueue{funcs: make([]func(context.Context), 0, capacity)} 38 q.cond = sync.NewCond(&q.mu) 39 go q.loop() 40 return q 41 } 42 43 func (q *execQueue) loop() { 44 for f := q.waitNext(false); f != nil; f = q.waitNext(true) { 45 ctx, span := trace.StartSpan(context.Background(), "execQueue.loop") 46 f(ctx) 47 span.End() 48 } 49 close(q.closeWait) 50 } 51 52 func (q *execQueue) waitNext(drop bool) (f func(context.Context)) { 53 q.mu.Lock() 54 if drop { 55 // Remove the function that just executed. We do this here instead of when 56 // dequeuing so len(q.funcs) includes the function that is running. 57 q.funcs = append(q.funcs[:0], q.funcs[1:]...) 58 } 59 for !q.isClosed() { 60 if len(q.funcs) > 0 { 61 f = q.funcs[0] 62 break 63 } 64 q.cond.Wait() 65 } 66 q.mu.Unlock() 67 return f 68 } 69 70 func (q *execQueue) isClosed() bool { 71 return q.closeWait != nil 72 } 73 74 // canQueue returns true if more function calls can be added to the execution queue. 75 func (q *execQueue) canQueue() bool { 76 q.mu.Lock() 77 ok := !q.isClosed() && len(q.funcs) < cap(q.funcs) 78 q.mu.Unlock() 79 return ok 80 } 81 82 // queue adds a function call to the execution queue. Returns true if successful. 83 func (q *execQueue) queue(f func(context.Context)) bool { 84 q.mu.Lock() 85 ok := !q.isClosed() && len(q.funcs) < cap(q.funcs) 86 if ok { 87 q.funcs = append(q.funcs, f) 88 q.cond.Signal() 89 } 90 q.mu.Unlock() 91 return ok 92 } 93 94 // quit stops the exec queue. 95 // quit waits for the current execution to finish before returning. 96 func (q *execQueue) quit() { 97 q.mu.Lock() 98 if !q.isClosed() { 99 q.closeWait = make(chan struct{}) 100 q.cond.Signal() 101 } 102 q.mu.Unlock() 103 <-q.closeWait 104 }