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  }