github.com/elfadel/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  }