github.com/4000d/go-ethereum@v1.8.2-0.20180223170251-423c8bb1d821/les/flowcontrol/manager.go (about) 1 // Copyright 2016 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 flowcontrol implements a client side flow control mechanism 18 package flowcontrol 19 20 import ( 21 "sync" 22 "time" 23 24 "github.com/ethereum/go-ethereum/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 }