github.com/tumi8/quic-go@v0.37.4-tum/noninternal/flowcontrol/stream_flow_controller.go (about) 1 package flowcontrol 2 3 import ( 4 "fmt" 5 6 "github.com/tumi8/quic-go/noninternal/protocol" 7 "github.com/tumi8/quic-go/noninternal/qerr" 8 "github.com/tumi8/quic-go/noninternal/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 utils.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 }