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  }