github.com/SmartMeshFoundation/Spectrum@v0.0.0-20220621030607-452a266fee1e/les/execqueue.go (about) 1 // Copyright 2017 The Spectrum Authors 2 // This file is part of the Spectrum library. 3 // 4 // The Spectrum 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 Spectrum 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 Spectrum library. If not, see <http://www.gnu.org/licenses/>. 16 17 package les 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 { 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 // quit stops the exec queue. 88 // quit waits for the current execution to finish before returning. 89 func (q *execQueue) quit() { 90 q.mu.Lock() 91 if !q.isClosed() { 92 q.closeWait = make(chan struct{}) 93 q.cond.Signal() 94 } 95 q.mu.Unlock() 96 <-q.closeWait 97 }