github.com/cryptogateway/go-paymex@v0.0.0-20210204174735-96277fb1e602/les/utils/exec_queue.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 utils 18 19 import "sync" 20 21 // ExecQueue implements a queue that executes function calls in a single thread, 22 // in the same order as they have been queued. 23 type ExecQueue struct { 24 mu sync.Mutex 25 cond *sync.Cond 26 funcs []func() 27 closeWait chan struct{} 28 } 29 30 // NewExecQueue creates a new execution Queue. 31 func NewExecQueue(capacity int) *ExecQueue { 32 q := &ExecQueue{funcs: make([]func(), 0, capacity)} 33 q.cond = sync.NewCond(&q.mu) 34 go q.loop() 35 return q 36 } 37 38 func (q *ExecQueue) loop() { 39 for f := q.waitNext(false); f != nil; f = q.waitNext(true) { 40 f() 41 } 42 close(q.closeWait) 43 } 44 45 func (q *ExecQueue) waitNext(drop bool) (f func()) { 46 q.mu.Lock() 47 if drop && len(q.funcs) > 0 { 48 // Remove the function that just executed. We do this here instead of when 49 // dequeuing so len(q.funcs) includes the function that is running. 50 q.funcs = append(q.funcs[:0], q.funcs[1:]...) 51 } 52 for !q.isClosed() { 53 if len(q.funcs) > 0 { 54 f = q.funcs[0] 55 break 56 } 57 q.cond.Wait() 58 } 59 q.mu.Unlock() 60 return f 61 } 62 63 func (q *ExecQueue) isClosed() bool { 64 return q.closeWait != nil 65 } 66 67 // CanQueue returns true if more function calls can be added to the execution Queue. 68 func (q *ExecQueue) CanQueue() bool { 69 q.mu.Lock() 70 ok := !q.isClosed() && len(q.funcs) < cap(q.funcs) 71 q.mu.Unlock() 72 return ok 73 } 74 75 // Queue adds a function call to the execution Queue. Returns true if successful. 76 func (q *ExecQueue) Queue(f func()) bool { 77 q.mu.Lock() 78 ok := !q.isClosed() && len(q.funcs) < cap(q.funcs) 79 if ok { 80 q.funcs = append(q.funcs, f) 81 q.cond.Signal() 82 } 83 q.mu.Unlock() 84 return ok 85 } 86 87 // Clear drops all queued functions. 88 func (q *ExecQueue) Clear() { 89 q.mu.Lock() 90 q.funcs = q.funcs[:0] 91 q.mu.Unlock() 92 } 93 94 // Quit stops the exec Queue. 95 // 96 // Quit waits for the current execution to finish before returning. 97 func (q *ExecQueue) Quit() { 98 q.mu.Lock() 99 if !q.isClosed() { 100 q.closeWait = make(chan struct{}) 101 q.cond.Signal() 102 } 103 q.mu.Unlock() 104 <-q.closeWait 105 }