github.com/murrekatt/go-ethereum@v1.5.8-0.20170123175102-fc52f2c007fb/les/flowcontrol/control.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 fcTimeConst = time.Millisecond 28 29 type ServerParams struct { 30 BufLimit, MinRecharge uint64 31 } 32 33 type ClientNode struct { 34 params *ServerParams 35 bufValue uint64 36 lastTime mclock.AbsTime 37 lock sync.Mutex 38 cm *ClientManager 39 cmNode *cmNode 40 } 41 42 func NewClientNode(cm *ClientManager, params *ServerParams) *ClientNode { 43 node := &ClientNode{ 44 cm: cm, 45 params: params, 46 bufValue: params.BufLimit, 47 lastTime: mclock.Now(), 48 } 49 node.cmNode = cm.addNode(node) 50 return node 51 } 52 53 func (peer *ClientNode) Remove(cm *ClientManager) { 54 cm.removeNode(peer.cmNode) 55 } 56 57 func (peer *ClientNode) recalcBV(time mclock.AbsTime) { 58 dt := uint64(time - peer.lastTime) 59 if time < peer.lastTime { 60 dt = 0 61 } 62 peer.bufValue += peer.params.MinRecharge * dt / uint64(fcTimeConst) 63 if peer.bufValue > peer.params.BufLimit { 64 peer.bufValue = peer.params.BufLimit 65 } 66 peer.lastTime = time 67 } 68 69 func (peer *ClientNode) AcceptRequest() (uint64, bool) { 70 peer.lock.Lock() 71 defer peer.lock.Unlock() 72 73 time := mclock.Now() 74 peer.recalcBV(time) 75 return peer.bufValue, peer.cm.accept(peer.cmNode, time) 76 } 77 78 func (peer *ClientNode) RequestProcessed(cost uint64) (bv, realCost uint64) { 79 peer.lock.Lock() 80 defer peer.lock.Unlock() 81 82 time := mclock.Now() 83 peer.recalcBV(time) 84 peer.bufValue -= cost 85 peer.recalcBV(time) 86 rcValue, rcost := peer.cm.processed(peer.cmNode, time) 87 if rcValue < peer.params.BufLimit { 88 bv := peer.params.BufLimit - rcValue 89 if bv > peer.bufValue { 90 peer.bufValue = bv 91 } 92 } 93 return peer.bufValue, rcost 94 } 95 96 type ServerNode struct { 97 bufEstimate uint64 98 lastTime mclock.AbsTime 99 params *ServerParams 100 sumCost uint64 // sum of req costs sent to this server 101 pending map[uint64]uint64 // value = sumCost after sending the given req 102 assignedRequest uint64 // when != 0, only the request with the given ID can be sent to this peer 103 assignToken chan struct{} // send to this channel before assigning, read from it after deassigning 104 lock sync.RWMutex 105 } 106 107 func NewServerNode(params *ServerParams) *ServerNode { 108 return &ServerNode{ 109 bufEstimate: params.BufLimit, 110 lastTime: mclock.Now(), 111 params: params, 112 pending: make(map[uint64]uint64), 113 assignToken: make(chan struct{}, 1), 114 } 115 } 116 117 func (peer *ServerNode) recalcBLE(time mclock.AbsTime) { 118 dt := uint64(time - peer.lastTime) 119 if time < peer.lastTime { 120 dt = 0 121 } 122 peer.bufEstimate += peer.params.MinRecharge * dt / uint64(fcTimeConst) 123 if peer.bufEstimate > peer.params.BufLimit { 124 peer.bufEstimate = peer.params.BufLimit 125 } 126 peer.lastTime = time 127 } 128 129 // safetyMargin is added to the flow control waiting time when estimated buffer value is low 130 const safetyMargin = time.Millisecond * 200 131 132 func (peer *ServerNode) canSend(maxCost uint64) time.Duration { 133 maxCost += uint64(safetyMargin) * peer.params.MinRecharge / uint64(fcTimeConst) 134 if maxCost > peer.params.BufLimit { 135 maxCost = peer.params.BufLimit 136 } 137 if peer.bufEstimate >= maxCost { 138 return 0 139 } 140 return time.Duration((maxCost - peer.bufEstimate) * uint64(fcTimeConst) / peer.params.MinRecharge) 141 } 142 143 // CanSend returns the minimum waiting time required before sending a request 144 // with the given maximum estimated cost 145 func (peer *ServerNode) CanSend(maxCost uint64) time.Duration { 146 peer.lock.RLock() 147 defer peer.lock.RUnlock() 148 149 return peer.canSend(maxCost) 150 } 151 152 // AssignRequest tries to assign the server node to the given request, guaranteeing 153 // that once it returns true, no request will be sent to the node before this one 154 func (peer *ServerNode) AssignRequest(reqID uint64) bool { 155 select { 156 case peer.assignToken <- struct{}{}: 157 default: 158 return false 159 } 160 peer.lock.Lock() 161 peer.assignedRequest = reqID 162 peer.lock.Unlock() 163 return true 164 } 165 166 // MustAssignRequest waits until the node can be assigned to the given request. 167 // It is always guaranteed that assignments are released in a short amount of time. 168 func (peer *ServerNode) MustAssignRequest(reqID uint64) { 169 peer.assignToken <- struct{}{} 170 peer.lock.Lock() 171 peer.assignedRequest = reqID 172 peer.lock.Unlock() 173 } 174 175 // DeassignRequest releases a request assignment in case the planned request 176 // is not being sent. 177 func (peer *ServerNode) DeassignRequest(reqID uint64) { 178 peer.lock.Lock() 179 if peer.assignedRequest == reqID { 180 peer.assignedRequest = 0 181 <-peer.assignToken 182 } 183 peer.lock.Unlock() 184 } 185 186 // IsAssigned returns true if the server node has already been assigned to a request 187 // (note that this function returning false does not guarantee that you can assign a request 188 // immediately afterwards, its only purpose is to help peer selection) 189 func (peer *ServerNode) IsAssigned() bool { 190 peer.lock.RLock() 191 locked := peer.assignedRequest != 0 192 peer.lock.RUnlock() 193 return locked 194 } 195 196 // blocks until request can be sent 197 func (peer *ServerNode) SendRequest(reqID, maxCost uint64) { 198 peer.lock.Lock() 199 defer peer.lock.Unlock() 200 201 if peer.assignedRequest != reqID { 202 peer.lock.Unlock() 203 peer.MustAssignRequest(reqID) 204 peer.lock.Lock() 205 } 206 207 peer.recalcBLE(mclock.Now()) 208 wait := peer.canSend(maxCost) 209 for wait > 0 { 210 peer.lock.Unlock() 211 time.Sleep(wait) 212 peer.lock.Lock() 213 peer.recalcBLE(mclock.Now()) 214 wait = peer.canSend(maxCost) 215 } 216 peer.assignedRequest = 0 217 <-peer.assignToken 218 peer.bufEstimate -= maxCost 219 peer.sumCost += maxCost 220 if reqID >= 0 { 221 peer.pending[reqID] = peer.sumCost 222 } 223 } 224 225 func (peer *ServerNode) GotReply(reqID, bv uint64) { 226 227 peer.lock.Lock() 228 defer peer.lock.Unlock() 229 230 if bv > peer.params.BufLimit { 231 bv = peer.params.BufLimit 232 } 233 sc, ok := peer.pending[reqID] 234 if !ok { 235 return 236 } 237 delete(peer.pending, reqID) 238 peer.bufEstimate = bv - (peer.sumCost - sc) 239 peer.lastTime = mclock.Now() 240 }