forked from quic-go/quic-go
congestion: avoid overflows when calculating pacer budget (#5390)
* congestion: prevent uint64 overflow in pacer This should never happen for bandwidths even remotely possible in real-world scenarios, but it’s better to be safe. * congestion: add a benchmark test for the pacer * add a comment
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
package congestion
|
||||
|
||||
import (
|
||||
"math"
|
||||
"time"
|
||||
|
||||
"github.com/quic-go/quic-go/internal/monotime"
|
||||
@@ -48,8 +49,13 @@ func (p *pacer) Budget(now monotime.Time) protocol.ByteCount {
|
||||
if p.lastSentTime.IsZero() {
|
||||
return p.maxBurstSize()
|
||||
}
|
||||
budget := p.budgetAtLastSent + (protocol.ByteCount(p.adjustedBandwidth())*protocol.ByteCount(now.Sub(p.lastSentTime).Nanoseconds()))/1e9
|
||||
if budget < 0 { // protect against overflows
|
||||
delta := now.Sub(p.lastSentTime)
|
||||
var added protocol.ByteCount
|
||||
if delta > 0 {
|
||||
added = p.timeScaledBandwidth(uint64(delta.Nanoseconds()))
|
||||
}
|
||||
budget := p.budgetAtLastSent + added
|
||||
if added > 0 && budget < p.budgetAtLastSent {
|
||||
budget = protocol.MaxByteCount
|
||||
}
|
||||
return min(p.maxBurstSize(), budget)
|
||||
@@ -57,11 +63,30 @@ func (p *pacer) Budget(now monotime.Time) protocol.ByteCount {
|
||||
|
||||
func (p *pacer) maxBurstSize() protocol.ByteCount {
|
||||
return max(
|
||||
protocol.ByteCount(uint64((protocol.MinPacingDelay+protocol.TimerGranularity).Nanoseconds())*p.adjustedBandwidth())/1e9,
|
||||
p.timeScaledBandwidth(uint64((protocol.MinPacingDelay + protocol.TimerGranularity).Nanoseconds())),
|
||||
maxBurstSizePackets*p.maxDatagramSize,
|
||||
)
|
||||
}
|
||||
|
||||
// timeScaledBandwidth calculates the number of bytes that may be sent within
|
||||
// a given time interval (ns nanoseconds), based on the current bandwidth estimate.
|
||||
// It caps the scaled value to the maximum allowed burst and handles overflows.
|
||||
func (p *pacer) timeScaledBandwidth(ns uint64) protocol.ByteCount {
|
||||
bw := p.adjustedBandwidth()
|
||||
if bw == 0 {
|
||||
return 0
|
||||
}
|
||||
const nsPerSecond = 1e9
|
||||
maxBurst := maxBurstSizePackets * p.maxDatagramSize
|
||||
var scaled protocol.ByteCount
|
||||
if ns > math.MaxUint64/bw {
|
||||
scaled = maxBurst
|
||||
} else {
|
||||
scaled = protocol.ByteCount(bw * ns / nsPerSecond)
|
||||
}
|
||||
return scaled
|
||||
}
|
||||
|
||||
// TimeUntilSend returns when the next packet should be sent.
|
||||
// It returns zero if a packet can be sent immediately.
|
||||
func (p *pacer) TimeUntilSend() monotime.Time {
|
||||
|
||||
Reference in New Issue
Block a user