github.com/SmartMeshFoundation/Spectrum@v0.0.0-20220621030607-452a266fee1e/les/flowcontrol/manager.go (about)

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