github.com/MerlinKodo/quic-go@v0.39.2/internal/congestion/pacer.go (about) 1 package congestion 2 3 import ( 4 "math" 5 "time" 6 7 "github.com/MerlinKodo/quic-go/internal/protocol" 8 "github.com/MerlinKodo/quic-go/internal/utils" 9 ) 10 11 const maxBurstSizePackets = 10 12 13 // The pacer implements a token bucket pacing algorithm. 14 type pacer struct { 15 budgetAtLastSent protocol.ByteCount 16 maxDatagramSize protocol.ByteCount 17 lastSentTime time.Time 18 adjustedBandwidth func() uint64 // in bytes/s 19 } 20 21 func newPacer(getBandwidth func() Bandwidth) *pacer { 22 p := &pacer{ 23 maxDatagramSize: initialMaxDatagramSize, 24 adjustedBandwidth: func() uint64 { 25 // Bandwidth is in bits/s. We need the value in bytes/s. 26 bw := uint64(getBandwidth() / BytesPerSecond) 27 // Use a slightly higher value than the actual measured bandwidth. 28 // RTT variations then won't result in under-utilization of the congestion window. 29 // Ultimately, this will result in sending packets as acknowledgments are received rather than when timers fire, 30 // provided the congestion window is fully utilized and acknowledgments arrive at regular intervals. 31 return bw * 5 / 4 32 }, 33 } 34 p.budgetAtLastSent = p.maxBurstSize() 35 return p 36 } 37 38 func (p *pacer) SentPacket(sendTime time.Time, size protocol.ByteCount) { 39 budget := p.Budget(sendTime) 40 if size > budget { 41 p.budgetAtLastSent = 0 42 } else { 43 p.budgetAtLastSent = budget - size 44 } 45 p.lastSentTime = sendTime 46 } 47 48 func (p *pacer) Budget(now time.Time) protocol.ByteCount { 49 if p.lastSentTime.IsZero() { 50 return p.maxBurstSize() 51 } 52 budget := p.budgetAtLastSent + (protocol.ByteCount(p.adjustedBandwidth())*protocol.ByteCount(now.Sub(p.lastSentTime).Nanoseconds()))/1e9 53 if budget < 0 { // protect against overflows 54 budget = protocol.MaxByteCount 55 } 56 return utils.Min(p.maxBurstSize(), budget) 57 } 58 59 func (p *pacer) maxBurstSize() protocol.ByteCount { 60 return utils.Max( 61 protocol.ByteCount(uint64((protocol.MinPacingDelay+protocol.TimerGranularity).Nanoseconds())*p.adjustedBandwidth())/1e9, 62 maxBurstSizePackets*p.maxDatagramSize, 63 ) 64 } 65 66 // TimeUntilSend returns when the next packet should be sent. 67 // It returns the zero value of time.Time if a packet can be sent immediately. 68 func (p *pacer) TimeUntilSend() time.Time { 69 if p.budgetAtLastSent >= p.maxDatagramSize { 70 return time.Time{} 71 } 72 return p.lastSentTime.Add(utils.Max( 73 protocol.MinPacingDelay, 74 time.Duration(math.Ceil(float64(p.maxDatagramSize-p.budgetAtLastSent)*1e9/float64(p.adjustedBandwidth())))*time.Nanosecond, 75 )) 76 } 77 78 func (p *pacer) SetMaxDatagramSize(s protocol.ByteCount) { 79 p.maxDatagramSize = s 80 }