github.com/daeuniverse/quic-go@v0.0.0-20240413031024-943f218e0810/internal/flowcontrol/stream_flow_controller.go (about)

     1  package flowcontrol
     2  
     3  import (
     4  	"fmt"
     5  
     6  	"github.com/daeuniverse/quic-go/internal/protocol"
     7  	"github.com/daeuniverse/quic-go/internal/qerr"
     8  	"github.com/daeuniverse/quic-go/internal/utils"
     9  )
    10  
    11  type streamFlowController struct {
    12  	baseFlowController
    13  
    14  	streamID protocol.StreamID
    15  
    16  	queueWindowUpdate func()
    17  
    18  	connection connectionFlowControllerI
    19  
    20  	receivedFinalOffset bool
    21  }
    22  
    23  var _ StreamFlowController = &streamFlowController{}
    24  
    25  // NewStreamFlowController gets a new flow controller for a stream
    26  func NewStreamFlowController(
    27  	streamID protocol.StreamID,
    28  	cfc ConnectionFlowController,
    29  	receiveWindow protocol.ByteCount,
    30  	maxReceiveWindow protocol.ByteCount,
    31  	initialSendWindow protocol.ByteCount,
    32  	queueWindowUpdate func(protocol.StreamID),
    33  	rttStats *utils.RTTStats,
    34  	logger utils.Logger,
    35  ) StreamFlowController {
    36  	return &streamFlowController{
    37  		streamID:          streamID,
    38  		connection:        cfc.(connectionFlowControllerI),
    39  		queueWindowUpdate: func() { queueWindowUpdate(streamID) },
    40  		baseFlowController: baseFlowController{
    41  			rttStats:             rttStats,
    42  			receiveWindow:        receiveWindow,
    43  			receiveWindowSize:    receiveWindow,
    44  			maxReceiveWindowSize: maxReceiveWindow,
    45  			sendWindow:           initialSendWindow,
    46  			logger:               logger,
    47  		},
    48  	}
    49  }
    50  
    51  // UpdateHighestReceived updates the highestReceived value, if the offset is higher.
    52  func (c *streamFlowController) UpdateHighestReceived(offset protocol.ByteCount, final bool) error {
    53  	// If the final offset for this stream is already known, check for consistency.
    54  	if c.receivedFinalOffset {
    55  		// If we receive another final offset, check that it's the same.
    56  		if final && offset != c.highestReceived {
    57  			return &qerr.TransportError{
    58  				ErrorCode:    qerr.FinalSizeError,
    59  				ErrorMessage: fmt.Sprintf("received inconsistent final offset for stream %d (old: %d, new: %d bytes)", c.streamID, c.highestReceived, offset),
    60  			}
    61  		}
    62  		// Check that the offset is below the final offset.
    63  		if offset > c.highestReceived {
    64  			return &qerr.TransportError{
    65  				ErrorCode:    qerr.FinalSizeError,
    66  				ErrorMessage: fmt.Sprintf("received offset %d for stream %d, but final offset was already received at %d", offset, c.streamID, c.highestReceived),
    67  			}
    68  		}
    69  	}
    70  
    71  	if final {
    72  		c.receivedFinalOffset = true
    73  	}
    74  	if offset == c.highestReceived {
    75  		return nil
    76  	}
    77  	// A higher offset was received before.
    78  	// This can happen due to reordering.
    79  	if offset <= c.highestReceived {
    80  		if final {
    81  			return &qerr.TransportError{
    82  				ErrorCode:    qerr.FinalSizeError,
    83  				ErrorMessage: fmt.Sprintf("received final offset %d for stream %d, but already received offset %d before", offset, c.streamID, c.highestReceived),
    84  			}
    85  		}
    86  		return nil
    87  	}
    88  
    89  	increment := offset - c.highestReceived
    90  	c.highestReceived = offset
    91  	if c.checkFlowControlViolation() {
    92  		return &qerr.TransportError{
    93  			ErrorCode:    qerr.FlowControlError,
    94  			ErrorMessage: fmt.Sprintf("received %d bytes on stream %d, allowed %d bytes", offset, c.streamID, c.receiveWindow),
    95  		}
    96  	}
    97  	return c.connection.IncrementHighestReceived(increment)
    98  }
    99  
   100  func (c *streamFlowController) AddBytesRead(n protocol.ByteCount) {
   101  	c.mutex.Lock()
   102  	c.baseFlowController.addBytesRead(n)
   103  	shouldQueueWindowUpdate := c.shouldQueueWindowUpdate()
   104  	c.mutex.Unlock()
   105  	if shouldQueueWindowUpdate {
   106  		c.queueWindowUpdate()
   107  	}
   108  	c.connection.AddBytesRead(n)
   109  }
   110  
   111  func (c *streamFlowController) Abandon() {
   112  	c.mutex.Lock()
   113  	unread := c.highestReceived - c.bytesRead
   114  	c.mutex.Unlock()
   115  	if unread > 0 {
   116  		c.connection.AddBytesRead(unread)
   117  	}
   118  }
   119  
   120  func (c *streamFlowController) AddBytesSent(n protocol.ByteCount) {
   121  	c.baseFlowController.AddBytesSent(n)
   122  	c.connection.AddBytesSent(n)
   123  }
   124  
   125  func (c *streamFlowController) SendWindowSize() protocol.ByteCount {
   126  	return min(c.baseFlowController.sendWindowSize(), c.connection.SendWindowSize())
   127  }
   128  
   129  func (c *streamFlowController) shouldQueueWindowUpdate() bool {
   130  	return !c.receivedFinalOffset && c.hasWindowUpdate()
   131  }
   132  
   133  func (c *streamFlowController) GetWindowUpdate() protocol.ByteCount {
   134  	// If we already received the final offset for this stream, the peer won't need any additional flow control credit.
   135  	if c.receivedFinalOffset {
   136  		return 0
   137  	}
   138  
   139  	// Don't use defer for unlocking the mutex here, GetWindowUpdate() is called frequently and defer shows up in the profiler
   140  	c.mutex.Lock()
   141  	oldWindowSize := c.receiveWindowSize
   142  	offset := c.baseFlowController.getWindowUpdate()
   143  	if c.receiveWindowSize > oldWindowSize { // auto-tuning enlarged the window size
   144  		c.logger.Debugf("Increasing receive flow control window for stream %d to %d kB", c.streamID, c.receiveWindowSize/(1<<10))
   145  		c.connection.EnsureMinimumWindowSize(protocol.ByteCount(float64(c.receiveWindowSize) * protocol.ConnectionFlowControlMultiplier))
   146  	}
   147  	c.mutex.Unlock()
   148  	return offset
   149  }