github.phpd.cn/cilium/cilium@v1.6.12/pkg/serializer/func_queue.go (about) 1 // Copyright 2017-2019 Authors of Cilium 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package serializer 16 17 import ( 18 "context" 19 "fmt" 20 ) 21 22 var ( 23 // NoRetry always returns false independently of the number of retries. 24 NoRetry = func(int) bool { return false } 25 ) 26 27 // WaitFunc will be invoked each time a queued function has returned an error. 28 // nRetries will be set to the number of consecutive execution failures that 29 // have occurred so far. The WaitFunc must return true if execution must be 30 // retried or false if the function must be returned from the queue. 31 type WaitFunc func(nRetries int) bool 32 33 type queuedFunction struct { 34 f func() error 35 waitFunc WaitFunc 36 } 37 38 type FunctionQueue struct { 39 queue chan queuedFunction 40 stopCh chan struct{} 41 } 42 43 // NewFunctionQueue returns a FunctionQueue that will be used to execute 44 // functions in the same order they are enqueued. 45 func NewFunctionQueue(queueSize uint) *FunctionQueue { 46 fq := &FunctionQueue{ 47 queue: make(chan queuedFunction, queueSize), 48 stopCh: make(chan struct{}), 49 } 50 go fq.run() 51 return fq 52 } 53 54 // run starts the FunctionQueue internal worker. It will be stopped once 55 // `stopCh` is closed or receives a value. 56 func (fq *FunctionQueue) run() { 57 for { 58 select { 59 case <-fq.stopCh: 60 return 61 case f := <-fq.queue: 62 retries := 0 63 for { 64 select { 65 case <-fq.stopCh: 66 return 67 default: 68 } 69 retries++ 70 if err := f.f(); err != nil { 71 if !f.waitFunc(retries) { 72 break 73 } 74 } else { 75 break 76 } 77 } 78 } 79 } 80 } 81 82 // Stop stops the function queue from processing the functions on the queue. 83 // If there are functions in the queue waiting for them to be processed, they 84 // won't be executed. 85 func (fq *FunctionQueue) Stop() { 86 close(fq.stopCh) 87 } 88 89 // Wait until the FunctionQueue is stopped, or the specified context deadline 90 // expires. Returns the error from the context, or nil if the FunctionQueue 91 // was completed before the context deadline. 92 func (fq *FunctionQueue) Wait(ctx context.Context) error { 93 select { 94 case <-fq.stopCh: 95 case <-ctx.Done(): 96 } 97 if err := ctx.Err(); err != nil { 98 return fmt.Errorf("serializer %s", err) 99 } 100 return nil 101 } 102 103 // Enqueue enqueues the receiving function `f` to be executed by the function 104 // queue. Depending on the size of the function queue and the amount 105 // of functions queued, this function can block until the function queue 106 // is ready to receive more requests. 107 // If `f` returns an error, `waitFunc` will be executed and, depending on the 108 // return value of `waitFunc`, `f` will be executed again or not. 109 // The return value of `f` will not be logged and it's up to the caller to log 110 // it properly. 111 func (fq *FunctionQueue) Enqueue(f func() error, waitFunc WaitFunc) { 112 fq.queue <- queuedFunction{f: f, waitFunc: waitFunc} 113 }