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  }