github.com/SmartMeshFoundation/Spectrum@v0.0.0-20220621030607-452a266fee1e/les/flowcontrol/control.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 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 lock sync.RWMutex 103 } 104 105 func NewServerNode(params *ServerParams) *ServerNode { 106 return &ServerNode{ 107 bufEstimate: params.BufLimit, 108 lastTime: mclock.Now(), 109 params: params, 110 pending: make(map[uint64]uint64), 111 } 112 } 113 114 func (peer *ServerNode) recalcBLE(time mclock.AbsTime) { 115 dt := uint64(time - peer.lastTime) 116 if time < peer.lastTime { 117 dt = 0 118 } 119 peer.bufEstimate += peer.params.MinRecharge * dt / uint64(fcTimeConst) 120 if peer.bufEstimate > peer.params.BufLimit { 121 peer.bufEstimate = peer.params.BufLimit 122 } 123 peer.lastTime = time 124 } 125 126 // safetyMargin is added to the flow control waiting time when estimated buffer value is low 127 const safetyMargin = time.Millisecond 128 129 func (peer *ServerNode) canSend(maxCost uint64) (time.Duration, float64) { 130 peer.recalcBLE(mclock.Now()) 131 maxCost += uint64(safetyMargin) * peer.params.MinRecharge / uint64(fcTimeConst) 132 if maxCost > peer.params.BufLimit { 133 maxCost = peer.params.BufLimit 134 } 135 if peer.bufEstimate >= maxCost { 136 return 0, float64(peer.bufEstimate-maxCost) / float64(peer.params.BufLimit) 137 } 138 return time.Duration((maxCost - peer.bufEstimate) * uint64(fcTimeConst) / peer.params.MinRecharge), 0 139 } 140 141 // CanSend returns the minimum waiting time required before sending a request 142 // with the given maximum estimated cost. Second return value is the relative 143 // estimated buffer level after sending the request (divided by BufLimit). 144 func (peer *ServerNode) CanSend(maxCost uint64) (time.Duration, float64) { 145 peer.lock.RLock() 146 defer peer.lock.RUnlock() 147 148 return peer.canSend(maxCost) 149 } 150 151 // QueueRequest should be called when the request has been assigned to the given 152 // server node, before putting it in the send queue. It is mandatory that requests 153 // are sent in the same order as the QueueRequest calls are made. 154 func (peer *ServerNode) QueueRequest(reqID, maxCost uint64) { 155 peer.lock.Lock() 156 defer peer.lock.Unlock() 157 158 peer.bufEstimate -= maxCost 159 peer.sumCost += maxCost 160 peer.pending[reqID] = peer.sumCost 161 } 162 163 // GotReply adjusts estimated buffer value according to the value included in 164 // the latest request reply. 165 func (peer *ServerNode) GotReply(reqID, bv uint64) { 166 167 peer.lock.Lock() 168 defer peer.lock.Unlock() 169 170 if bv > peer.params.BufLimit { 171 bv = peer.params.BufLimit 172 } 173 sc, ok := peer.pending[reqID] 174 if !ok { 175 return 176 } 177 delete(peer.pending, reqID) 178 cc := peer.sumCost - sc 179 peer.bufEstimate = 0 180 if bv > cc { 181 peer.bufEstimate = bv - cc 182 } 183 peer.lastTime = mclock.Now() 184 }