github.com/arieschain/arieschain@v0.0.0-20191023063405-37c074544356/les/flowcontrol/manager.go (about)

     1  // Package flowcontrol implements a client side flow control mechanism
     2  package flowcontrol
     3  
     4  import (
     5  	"sync"
     6  	"time"
     7  
     8  	"github.com/quickchainproject/quickchain/common/mclock"
     9  )
    10  
    11  const rcConst = 1000000
    12  
    13  type cmNode struct {
    14  	node                         *ClientNode
    15  	lastUpdate                   mclock.AbsTime
    16  	serving, recharging          bool
    17  	rcWeight                     uint64
    18  	rcValue, rcDelta, startValue int64
    19  	finishRecharge               mclock.AbsTime
    20  }
    21  
    22  func (node *cmNode) update(time mclock.AbsTime) {
    23  	dt := int64(time - node.lastUpdate)
    24  	node.rcValue += node.rcDelta * dt / rcConst
    25  	node.lastUpdate = time
    26  	if node.recharging && time >= node.finishRecharge {
    27  		node.recharging = false
    28  		node.rcDelta = 0
    29  		node.rcValue = 0
    30  	}
    31  }
    32  
    33  func (node *cmNode) set(serving bool, simReqCnt, sumWeight uint64) {
    34  	if node.serving && !serving {
    35  		node.recharging = true
    36  		sumWeight += node.rcWeight
    37  	}
    38  	node.serving = serving
    39  	if node.recharging && serving {
    40  		node.recharging = false
    41  		sumWeight -= node.rcWeight
    42  	}
    43  
    44  	node.rcDelta = 0
    45  	if serving {
    46  		node.rcDelta = int64(rcConst / simReqCnt)
    47  	}
    48  	if node.recharging {
    49  		node.rcDelta = -int64(node.node.cm.rcRecharge * node.rcWeight / sumWeight)
    50  		node.finishRecharge = node.lastUpdate + mclock.AbsTime(node.rcValue*rcConst/(-node.rcDelta))
    51  	}
    52  }
    53  
    54  type ClientManager struct {
    55  	lock                             sync.Mutex
    56  	nodes                            map[*cmNode]struct{}
    57  	simReqCnt, sumWeight, rcSumValue uint64
    58  	maxSimReq, maxRcSum              uint64
    59  	rcRecharge                       uint64
    60  	resumeQueue                      chan chan bool
    61  	time                             mclock.AbsTime
    62  }
    63  
    64  func NewClientManager(rcTarget, maxSimReq, maxRcSum uint64) *ClientManager {
    65  	cm := &ClientManager{
    66  		nodes:       make(map[*cmNode]struct{}),
    67  		resumeQueue: make(chan chan bool),
    68  		rcRecharge:  rcConst * rcConst / (100*rcConst/rcTarget - rcConst),
    69  		maxSimReq:   maxSimReq,
    70  		maxRcSum:    maxRcSum,
    71  	}
    72  	go cm.queueProc()
    73  	return cm
    74  }
    75  
    76  func (self *ClientManager) Stop() {
    77  	self.lock.Lock()
    78  	defer self.lock.Unlock()
    79  
    80  	// signal any waiting accept routines to return false
    81  	self.nodes = make(map[*cmNode]struct{})
    82  	close(self.resumeQueue)
    83  }
    84  
    85  func (self *ClientManager) addNode(cnode *ClientNode) *cmNode {
    86  	time := mclock.Now()
    87  	node := &cmNode{
    88  		node:           cnode,
    89  		lastUpdate:     time,
    90  		finishRecharge: time,
    91  		rcWeight:       1,
    92  	}
    93  	self.lock.Lock()
    94  	defer self.lock.Unlock()
    95  
    96  	self.nodes[node] = struct{}{}
    97  	self.update(mclock.Now())
    98  	return node
    99  }
   100  
   101  func (self *ClientManager) removeNode(node *cmNode) {
   102  	self.lock.Lock()
   103  	defer self.lock.Unlock()
   104  
   105  	time := mclock.Now()
   106  	self.stop(node, time)
   107  	delete(self.nodes, node)
   108  	self.update(time)
   109  }
   110  
   111  // recalc sumWeight
   112  func (self *ClientManager) updateNodes(time mclock.AbsTime) (rce bool) {
   113  	var sumWeight, rcSum uint64
   114  	for node := range self.nodes {
   115  		rc := node.recharging
   116  		node.update(time)
   117  		if rc && !node.recharging {
   118  			rce = true
   119  		}
   120  		if node.recharging {
   121  			sumWeight += node.rcWeight
   122  		}
   123  		rcSum += uint64(node.rcValue)
   124  	}
   125  	self.sumWeight = sumWeight
   126  	self.rcSumValue = rcSum
   127  	return
   128  }
   129  
   130  func (self *ClientManager) update(time mclock.AbsTime) {
   131  	for {
   132  		firstTime := time
   133  		for node := range self.nodes {
   134  			if node.recharging && node.finishRecharge < firstTime {
   135  				firstTime = node.finishRecharge
   136  			}
   137  		}
   138  		if self.updateNodes(firstTime) {
   139  			for node := range self.nodes {
   140  				if node.recharging {
   141  					node.set(node.serving, self.simReqCnt, self.sumWeight)
   142  				}
   143  			}
   144  		} else {
   145  			self.time = time
   146  			return
   147  		}
   148  	}
   149  }
   150  
   151  func (self *ClientManager) canStartReq() bool {
   152  	return self.simReqCnt < self.maxSimReq && self.rcSumValue < self.maxRcSum
   153  }
   154  
   155  func (self *ClientManager) queueProc() {
   156  	for rc := range self.resumeQueue {
   157  		for {
   158  			time.Sleep(time.Millisecond * 10)
   159  			self.lock.Lock()
   160  			self.update(mclock.Now())
   161  			cs := self.canStartReq()
   162  			self.lock.Unlock()
   163  			if cs {
   164  				break
   165  			}
   166  		}
   167  		close(rc)
   168  	}
   169  }
   170  
   171  func (self *ClientManager) accept(node *cmNode, time mclock.AbsTime) bool {
   172  	self.lock.Lock()
   173  	defer self.lock.Unlock()
   174  
   175  	self.update(time)
   176  	if !self.canStartReq() {
   177  		resume := make(chan bool)
   178  		self.lock.Unlock()
   179  		self.resumeQueue <- resume
   180  		<-resume
   181  		self.lock.Lock()
   182  		if _, ok := self.nodes[node]; !ok {
   183  			return false // reject if node has been removed or manager has been stopped
   184  		}
   185  	}
   186  	self.simReqCnt++
   187  	node.set(true, self.simReqCnt, self.sumWeight)
   188  	node.startValue = node.rcValue
   189  	self.update(self.time)
   190  	return true
   191  }
   192  
   193  func (self *ClientManager) stop(node *cmNode, time mclock.AbsTime) {
   194  	if node.serving {
   195  		self.update(time)
   196  		self.simReqCnt--
   197  		node.set(false, self.simReqCnt, self.sumWeight)
   198  		self.update(time)
   199  	}
   200  }
   201  
   202  func (self *ClientManager) processed(node *cmNode, time mclock.AbsTime) (rcValue, rcCost uint64) {
   203  	self.lock.Lock()
   204  	defer self.lock.Unlock()
   205  
   206  	self.stop(node, time)
   207  	return uint64(node.rcValue), uint64(node.rcValue - node.startValue)
   208  }