github.com/nitinawathare/ethereumassignment3@v0.0.0-20211021213010-f07344c2b868/go-ethereum/les/distributor.go (about) 1 // Copyright 2017 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 les 18 19 import ( 20 "container/list" 21 "sync" 22 "time" 23 24 "github.com/ethereum/go-ethereum/common/mclock" 25 ) 26 27 // requestDistributor implements a mechanism that distributes requests to 28 // suitable peers, obeying flow control rules and prioritizing them in creation 29 // order (even when a resend is necessary). 30 type requestDistributor struct { 31 clock mclock.Clock 32 reqQueue *list.List 33 lastReqOrder uint64 34 peers map[distPeer]struct{} 35 peerLock sync.RWMutex 36 stopChn, loopChn chan struct{} 37 loopNextSent bool 38 lock sync.Mutex 39 } 40 41 // distPeer is an LES server peer interface for the request distributor. 42 // waitBefore returns either the necessary waiting time before sending a request 43 // with the given upper estimated cost or the estimated remaining relative buffer 44 // value after sending such a request (in which case the request can be sent 45 // immediately). At least one of these values is always zero. 46 type distPeer interface { 47 waitBefore(uint64) (time.Duration, float64) 48 canQueue() bool 49 queueSend(f func()) 50 } 51 52 // distReq is the request abstraction used by the distributor. It is based on 53 // three callback functions: 54 // - getCost returns the upper estimate of the cost of sending the request to a given peer 55 // - canSend tells if the server peer is suitable to serve the request 56 // - request prepares sending the request to the given peer and returns a function that 57 // does the actual sending. Request order should be preserved but the callback itself should not 58 // block until it is sent because other peers might still be able to receive requests while 59 // one of them is blocking. Instead, the returned function is put in the peer's send queue. 60 type distReq struct { 61 getCost func(distPeer) uint64 62 canSend func(distPeer) bool 63 request func(distPeer) func() 64 65 reqOrder uint64 66 sentChn chan distPeer 67 element *list.Element 68 } 69 70 // newRequestDistributor creates a new request distributor 71 func newRequestDistributor(peers *peerSet, stopChn chan struct{}, clock mclock.Clock) *requestDistributor { 72 d := &requestDistributor{ 73 clock: clock, 74 reqQueue: list.New(), 75 loopChn: make(chan struct{}, 2), 76 stopChn: stopChn, 77 peers: make(map[distPeer]struct{}), 78 } 79 if peers != nil { 80 peers.notify(d) 81 } 82 go d.loop() 83 return d 84 } 85 86 // registerPeer implements peerSetNotify 87 func (d *requestDistributor) registerPeer(p *peer) { 88 d.peerLock.Lock() 89 d.peers[p] = struct{}{} 90 d.peerLock.Unlock() 91 } 92 93 // unregisterPeer implements peerSetNotify 94 func (d *requestDistributor) unregisterPeer(p *peer) { 95 d.peerLock.Lock() 96 delete(d.peers, p) 97 d.peerLock.Unlock() 98 } 99 100 // registerTestPeer adds a new test peer 101 func (d *requestDistributor) registerTestPeer(p distPeer) { 102 d.peerLock.Lock() 103 d.peers[p] = struct{}{} 104 d.peerLock.Unlock() 105 } 106 107 // distMaxWait is the maximum waiting time after which further necessary waiting 108 // times are recalculated based on new feedback from the servers 109 const distMaxWait = time.Millisecond * 10 110 111 // main event loop 112 func (d *requestDistributor) loop() { 113 for { 114 select { 115 case <-d.stopChn: 116 d.lock.Lock() 117 elem := d.reqQueue.Front() 118 for elem != nil { 119 req := elem.Value.(*distReq) 120 close(req.sentChn) 121 req.sentChn = nil 122 elem = elem.Next() 123 } 124 d.lock.Unlock() 125 return 126 case <-d.loopChn: 127 d.lock.Lock() 128 d.loopNextSent = false 129 loop: 130 for { 131 peer, req, wait := d.nextRequest() 132 if req != nil && wait == 0 { 133 chn := req.sentChn // save sentChn because remove sets it to nil 134 d.remove(req) 135 send := req.request(peer) 136 if send != nil { 137 peer.queueSend(send) 138 } 139 chn <- peer 140 close(chn) 141 } else { 142 if wait == 0 { 143 // no request to send and nothing to wait for; the next 144 // queued request will wake up the loop 145 break loop 146 } 147 d.loopNextSent = true // a "next" signal has been sent, do not send another one until this one has been received 148 if wait > distMaxWait { 149 // waiting times may be reduced by incoming request replies, if it is too long, recalculate it periodically 150 wait = distMaxWait 151 } 152 go func() { 153 d.clock.Sleep(wait) 154 d.loopChn <- struct{}{} 155 }() 156 break loop 157 } 158 } 159 d.lock.Unlock() 160 } 161 } 162 } 163 164 // selectPeerItem represents a peer to be selected for a request by weightedRandomSelect 165 type selectPeerItem struct { 166 peer distPeer 167 req *distReq 168 weight int64 169 } 170 171 // Weight implements wrsItem interface 172 func (sp selectPeerItem) Weight() int64 { 173 return sp.weight 174 } 175 176 // nextRequest returns the next possible request from any peer, along with the 177 // associated peer and necessary waiting time 178 func (d *requestDistributor) nextRequest() (distPeer, *distReq, time.Duration) { 179 checkedPeers := make(map[distPeer]struct{}) 180 elem := d.reqQueue.Front() 181 var ( 182 bestPeer distPeer 183 bestReq *distReq 184 bestWait time.Duration 185 sel *weightedRandomSelect 186 ) 187 188 d.peerLock.RLock() 189 defer d.peerLock.RUnlock() 190 191 for (len(d.peers) > 0 || elem == d.reqQueue.Front()) && elem != nil { 192 req := elem.Value.(*distReq) 193 canSend := false 194 for peer := range d.peers { 195 if _, ok := checkedPeers[peer]; !ok && peer.canQueue() && req.canSend(peer) { 196 canSend = true 197 cost := req.getCost(peer) 198 wait, bufRemain := peer.waitBefore(cost) 199 if wait == 0 { 200 if sel == nil { 201 sel = newWeightedRandomSelect() 202 } 203 sel.update(selectPeerItem{peer: peer, req: req, weight: int64(bufRemain*1000000) + 1}) 204 } else { 205 if bestReq == nil || wait < bestWait { 206 bestPeer = peer 207 bestReq = req 208 bestWait = wait 209 } 210 } 211 checkedPeers[peer] = struct{}{} 212 } 213 } 214 next := elem.Next() 215 if !canSend && elem == d.reqQueue.Front() { 216 close(req.sentChn) 217 d.remove(req) 218 } 219 elem = next 220 } 221 222 if sel != nil { 223 c := sel.choose().(selectPeerItem) 224 return c.peer, c.req, 0 225 } 226 return bestPeer, bestReq, bestWait 227 } 228 229 // queue adds a request to the distribution queue, returns a channel where the 230 // receiving peer is sent once the request has been sent (request callback returned). 231 // If the request is cancelled or timed out without suitable peers, the channel is 232 // closed without sending any peer references to it. 233 func (d *requestDistributor) queue(r *distReq) chan distPeer { 234 d.lock.Lock() 235 defer d.lock.Unlock() 236 237 if r.reqOrder == 0 { 238 d.lastReqOrder++ 239 r.reqOrder = d.lastReqOrder 240 } 241 242 back := d.reqQueue.Back() 243 if back == nil || r.reqOrder > back.Value.(*distReq).reqOrder { 244 r.element = d.reqQueue.PushBack(r) 245 } else { 246 before := d.reqQueue.Front() 247 for before.Value.(*distReq).reqOrder < r.reqOrder { 248 before = before.Next() 249 } 250 r.element = d.reqQueue.InsertBefore(r, before) 251 } 252 253 if !d.loopNextSent { 254 d.loopNextSent = true 255 d.loopChn <- struct{}{} 256 } 257 258 r.sentChn = make(chan distPeer, 1) 259 return r.sentChn 260 } 261 262 // cancel removes a request from the queue if it has not been sent yet (returns 263 // false if it has been sent already). It is guaranteed that the callback functions 264 // will not be called after cancel returns. 265 func (d *requestDistributor) cancel(r *distReq) bool { 266 d.lock.Lock() 267 defer d.lock.Unlock() 268 269 if r.sentChn == nil { 270 return false 271 } 272 273 close(r.sentChn) 274 d.remove(r) 275 return true 276 } 277 278 // remove removes a request from the queue 279 func (d *requestDistributor) remove(r *distReq) { 280 r.sentChn = nil 281 if r.element != nil { 282 d.reqQueue.Remove(r.element) 283 r.element = nil 284 } 285 }