github.com/cryptogateway/go-paymex@v0.0.0-20210204174735-96277fb1e602/les/lespay/client/wrsiterator.go (about)

     1  // Copyright 2020 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 client
    18  
    19  import (
    20  	"sync"
    21  
    22  	"github.com/cryptogateway/go-paymex/les/utils"
    23  	"github.com/cryptogateway/go-paymex/p2p/enode"
    24  	"github.com/cryptogateway/go-paymex/p2p/nodestate"
    25  )
    26  
    27  // WrsIterator returns nodes from the specified selectable set with a weighted random
    28  // selection. Selection weights are provided by a callback function.
    29  type WrsIterator struct {
    30  	lock sync.Mutex
    31  	cond *sync.Cond
    32  
    33  	ns       *nodestate.NodeStateMachine
    34  	wrs      *utils.WeightedRandomSelect
    35  	nextNode *enode.Node
    36  	closed   bool
    37  }
    38  
    39  // NewWrsIterator creates a new WrsIterator. Nodes are selectable if they have all the required
    40  // and none of the disabled flags set. When a node is selected the selectedFlag is set which also
    41  // disables further selectability until it is removed or times out.
    42  func NewWrsIterator(ns *nodestate.NodeStateMachine, requireFlags, disableFlags nodestate.Flags, weightField nodestate.Field) *WrsIterator {
    43  	wfn := func(i interface{}) uint64 {
    44  		n := ns.GetNode(i.(enode.ID))
    45  		if n == nil {
    46  			return 0
    47  		}
    48  		wt, _ := ns.GetField(n, weightField).(uint64)
    49  		return wt
    50  	}
    51  
    52  	w := &WrsIterator{
    53  		ns:  ns,
    54  		wrs: utils.NewWeightedRandomSelect(wfn),
    55  	}
    56  	w.cond = sync.NewCond(&w.lock)
    57  
    58  	ns.SubscribeField(weightField, func(n *enode.Node, state nodestate.Flags, oldValue, newValue interface{}) {
    59  		if state.HasAll(requireFlags) && state.HasNone(disableFlags) {
    60  			w.lock.Lock()
    61  			w.wrs.Update(n.ID())
    62  			w.lock.Unlock()
    63  			w.cond.Signal()
    64  		}
    65  	})
    66  
    67  	ns.SubscribeState(requireFlags.Or(disableFlags), func(n *enode.Node, oldState, newState nodestate.Flags) {
    68  		oldMatch := oldState.HasAll(requireFlags) && oldState.HasNone(disableFlags)
    69  		newMatch := newState.HasAll(requireFlags) && newState.HasNone(disableFlags)
    70  		if newMatch == oldMatch {
    71  			return
    72  		}
    73  
    74  		w.lock.Lock()
    75  		if newMatch {
    76  			w.wrs.Update(n.ID())
    77  		} else {
    78  			w.wrs.Remove(n.ID())
    79  		}
    80  		w.lock.Unlock()
    81  		w.cond.Signal()
    82  	})
    83  	return w
    84  }
    85  
    86  // Next selects the next node.
    87  func (w *WrsIterator) Next() bool {
    88  	w.nextNode = w.chooseNode()
    89  	return w.nextNode != nil
    90  }
    91  
    92  func (w *WrsIterator) chooseNode() *enode.Node {
    93  	w.lock.Lock()
    94  	defer w.lock.Unlock()
    95  
    96  	for {
    97  		for !w.closed && w.wrs.IsEmpty() {
    98  			w.cond.Wait()
    99  		}
   100  		if w.closed {
   101  			return nil
   102  		}
   103  		// Choose the next node at random. Even though w.wrs is guaranteed
   104  		// non-empty here, Choose might return nil if all items have weight
   105  		// zero.
   106  		if c := w.wrs.Choose(); c != nil {
   107  			id := c.(enode.ID)
   108  			w.wrs.Remove(id)
   109  			return w.ns.GetNode(id)
   110  		}
   111  	}
   112  
   113  }
   114  
   115  // Close ends the iterator.
   116  func (w *WrsIterator) Close() {
   117  	w.lock.Lock()
   118  	w.closed = true
   119  	w.lock.Unlock()
   120  	w.cond.Signal()
   121  }
   122  
   123  // Node returns the current node.
   124  func (w *WrsIterator) Node() *enode.Node {
   125  	w.lock.Lock()
   126  	defer w.lock.Unlock()
   127  	return w.nextNode
   128  }